English Русский 中文 Español Deutsch 日本語 Português Français Italiano Türkçe
preview
Scikit-Learn 라이브러리의 분류 모델 및 ONNX로 내보내기

Scikit-Learn 라이브러리의 분류 모델 및 ONNX로 내보내기

MetaTrader 5 | 28 8월 2024, 14:47
36 0
MetaQuotes
MetaQuotes

기술의 발달로 인해 데이터 처리 알고리즘을 구축하는 근본적으로 새로운 접근 방식이 등장했습니다. 이전에는 각각의 특정한 작업을 해결하기 위해 해당 알고리즘을 명확하게 공식화하고 개발해야 했습니다.

머신 러닝에서 컴퓨터는 컴퓨터 스스로 데이터를 처리하는 가장 좋은 방법을 찾는 방법을 학습합니다. 머신 러닝 모델은 분류 작업(고정된 클래스 집합이 있고 각 클래스에 속하는 주어진 피처 집합의 확률을 찾는 것을 목표로 하는 경우)과 회귀 작업(주어진 피처 집합을 기반으로 목표 변수의 수치 값을 추정하는 것을 목표로 하는 경우)을 성공적으로 해결할 수 있습니다. 이러한 기본 구성 요소를 기반으로 더 복잡한 데이터 처리 모델을 구축할 수 있습니다.

Scikit-learn 라이브러리는 분류와 회귀를 위한 다양한 도구를 제공합니다. 특정 메서드와 모델의 선택은 데이터의 특성에 따라 달라지는데 이는 메서드마다 효과가 다르고 각 메서드는 작업에 따라 다른 결과를 가져올 수 있기 때문입니다.

보도 자료 "ONNX 런타임은 이제 오픈 소스입니다"에 따르면 ONNX 런타임은 ONNX-ML 프로필도 지원한다고 합니다:

ONNX 런타임은 공개적으로 사용 가능한 최초의 추론 엔진으로서 ONNX-ML 프로필을 포함하여 ONNX 1.2 이상을 완벽하게 지원합니다.

ONNX-ML 프로필은 머신 러닝(ML) 모델을 위해 특별히 설계된 ONNX의 일부입니다. ONNX-ML은 분류, 회귀, 클러스터링 등 다양한 유형의 머신 러닝 모델을 편리한 형식으로 기술하고 표현하기 위한 것으로 ONNX를 지원하는 다양한 플랫폼과 환경에서 사용할 수 있습니다. ONNX-ML 프로필은 머신 러닝 모델의 전송, 배포 및 실행을 간소화하고 접근성과 휴대성을 높입니다.

이 글에서는 피셔의 붓꽃 분류 과제를 해결하기 위해 Scikit-learn 패키지의 모든 분류 모델을 적용하는 방법에 대해 살펴볼 것입니다. 우리는 이러한 모델을 ONNX 형식으로 변환하고 결과 모델을 MQL5 프로그램에서 사용해 볼 것입니다.

또한 전체 붓꽃 데이터 세트에서 원래 모델의 정확도를 ONNX 버전과 비교할 것입니다.


목차



1. 피셔의 붓꽃

붓꽃 데이터 세트는 머신 러닝 분야에서 가장 많이 알려져 있고 널리 사용되는 데이터 세트 중 하나로 1936년 통계학자이자 생물학자인 R.A.가 처음 소개한 개념이며 이후 분류 작업의 고전적인 데이터 세트가 되었습니다.

붓꽃 데이터 세트는 세 종류의 붓꽃, 즉 붓꽃 세토사, 붓꽃 버지니카, 붓꽃 버지컬러의 꽃받침과 꽃잎을 측정한 데이터로 구성되어 있습니다.

붓꽃 세토사

그림 1. 붓꽃 세토사


그림 2. 붓꽃 버지니카

그림 2. 붓꽃 버지니카


그림 3. 붓꽃 버시컬러

그림 3. 붓꽃 버시컬러


붓꽃 데이터 세트는 150개의 붓꽃 인스턴스로 구성되며 세 가지 종 각각 50개의 인스턴스가 있습니다. 각 인스턴스에는 네 가지 숫자 피처(센티미터 단위로 측정)이 있습니다:

  1. 꽃받침 길이
  2. 꽃받침 너비
  3. 꽃잎 길이
  4. 꽃잎 너비

각각의 인스턴스에는 붓꽃의 종을 나타내는 해당 클래스(붓꽃 세토사, 붓꽃 버지니카 또는 붓꽃 버시컬러)도 있습니다. 이러한 분류 속성으로 인해 붓꽃 데이터 세트는 분류 및 클러스터링과 같은 머신 러닝 작업에서 이상적인 데이터 세트가 됩니다.

MetaEditor에서는 Python 스크립트로 작업할 수 있습니다. Python 스크립트를 만들려면 MetaEditor의 '파일' 메뉴에서 '새로 만들기'를 선택하면 만들 객체를 선택하는 대화 상자가 나타납니다(그림 4 참조).

그림 4. MQL5 마법사에서 Python 스크립트 만들기 - 1단계

그림 4. MQL5 마법사에서 Python 스크립트 만들기 - 1단계

그런 다음 스크립트의 이름(예: "IRIS.py")을 입력합니다(그림 5 참조).

그림 5. MQL5 마법사에서 Python 스크립트 만들기 - 2단계 - 스크립트 이름

그림 5. MQL5 마법사에서 Python 스크립트 만들기 - 2단계 - 스크립트 이름

그런 다음 사용할 라이브러리를 지정할 수 있습니다. 이 경우에 우리는 이 필드를 비워 두겠습니다(그림 6 참조).

그림 6: MQL5 마법사에서 Python 스크립트 만들기 - 3단계

그림 6: MQL5 마법사에서 Python 스크립트 만들기 - 3단계


붓꽃 데이터 집합 분석을 시작하는 한 가지 방법은 데이터를 시각화하는 것입니다. 그래픽을 통하면 데이터의 구조와 피처 간의 관계를 더 잘 이해할 수 있습니다.

예를 들어 여러분은 산점도를 만들어 피처 공간에서 다양한 종류의 붓꽃이 어떻게 분포되어 있는지 확인할 수 있습니다.

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 참조) "컴파일"을 클릭해야 합니다.

그림 7: MetaEditor의 IRIS.py 스크립트

그림 7: MetaEditor의 IRIS.py 스크립트


그 후 플롯이 화면에 나타납니다:

그림 8: MetaEditor의 IRIS.py 스크립트(세팔 길이/세팔 너비 플롯 포함)

그림 8: 세팔 길이/세팔 너비 플롯이 있는 MetaEditor의 IRIS.py 스크립트


그림 9: 꽃잎 길이/꽃잎 너비 플롯이 있는 MetaEditor의 IRIS.py 스크립트

그림 9: 꽃잎 길이/꽃잎 너비 플롯이 있는 MetaEditor의 IRIS.py 스크립트


자세히 살펴보겠습니다.

그림 10: 산점도 꽃받침 길이와 꽃받침 너비 비교

그림 10: 산점도 꽃받침 길이 대 꽃받침 폭


이 도표에서 꽃받침 길이와 꽃받침 너비에 따라 붓꽃의 종의 분포가 어떻게 다른지 확인할 수 있습니다. 붓꽃 세토사는 일반적으로 다른 두 종에 비해 꽃받침이 짧고 넓은 것을 관찰할 수 있습니다.

그림 11: 분산형 차트 꽃잎 길이 대 꽃잎 너비

그림 11: 스캐터 플롯 꽃잎 길이 대 꽃잎 폭



이 도표에서 우리는 꽃잎 길이와 꽃잎 너비에 따라 붓꽃의 종류가 어떻게 분포하는지 확인할 수 있습니다. 붓꽃 세토사는 꽃잎이 가장 짧고 좁고 붓꽃 버지니카는 꽃잎이 가장 길고 넓으며 붓꽃 버지컬러는 그 사이에 속한다는 것을 알 수 있습니다.

붓꽃 데이터 세트는 머신 러닝 모델을 학습하고 테스트하는 데 이상적인 데이터 세트입니다. 우리는 이 세트를 분류 작업에 대한 머신러닝 모델의 효과를 분석하는 데 사용할 것입니다.



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

이 분류기 목록은 여러분의 편의를 위해 각기 다른 색상으로 강조 표시되어 있습니다. 기본 분류기가 필요한 모델은 노란색으로 강조 표시되며 그 외 모델은 독립적으로 사용할 수 있습니다.

한 가지 주목할 점은 초록색 모델은 ONNX 형식으로 성공적으로 내보내어진 반면 빨간색 모델에서는 현재 버전의 scikit-learn 1.2.2에서 변환하는 동안 오류가 발생한다는 점입니다.


모델에서 출력 데이터의 다양한 표현

ONNX로 변환된 모델로 작업할 때는 모델에 따라 출력 데이터가 다르게 표현되므로 주의해야 합니다.

피셔의 붓꽃 분류 작업의 경우 입력 텐서는 모든 모델에 대해 동일한 형식을 갖습니다:

ONNX의 입력 텐서에 대한 정보입니다:
1. Name: float_input, 데이터 유형: 텐서(float), 모양: [None, 4]

ONNX 모델의 출력 텐서는 다릅니다.

1. 후처리가 필요하지 않은 모델입니다:

  1. SVC 분류기;
  2. LinearSVC 분류기;
  3. NuSVC 분류기;
  4. Radius Neighbors Classifier;
  5. Ridge Classifier;
  6. Ridge Classifier CV.
ONNX의 출력 텐서에 대한 정보입니다:
1. 이름: label, Data Type: tensor(int64), Shape: [None]
2. 이름: probabilities, Data Type: tensor(float), Shape: [None, 3]

이러한 모델은 사후 처리 없이 첫 번째 출력 정수 텐서 '레이블'에 결과(클래스 번호)를 명시적으로 반환합니다.

2. 결과에 후처리가 필요한 모델:

  1. 랜덤 숲 분류기;
  2. 경사 부스팅 분류기;
  3. AdaBoost 분류기;
  4. 포장 분류기;
  5. K-NN_Classifier;
  6. 의사 결정 트리 분류기;
  7. 로지스틱 회귀 분류기;
  8. 로지스틱 회귀 CV 분류기;
  9. 패시브-어그레시브 분류기;
  10. 퍼셉트론 분류기;
  11. SGD 분류기;
  12. 가우시안 나이브 베이즈 분류기;
  13. 다항식 나이브 베이즈 분류기;
  14. 보완 나이브 베이즈 분류기;
  15. 베르누이 나이브 베이즈 분류기;
  16. 다층 퍼셉트론 분류기;
  17. 선형 판별 분석 분류기;
  18. 히스트 경사 부스팅 분류기;
  19. 범주형 나이브 베이즈 분류기;
  20. ExtraTree Classifier;
  21. ExtraTrees Classifier.
ONNX의 출력 텐서에 대한 정보입니다:
1. 이름: output_label, Data Type: tensor(int64), Shape: [None]
2. 이름: output_probability, Data Type: seq(map(int64,tensor(float))), Shape: []

이 모델은 클래스 목록과 각 클래스에 속할 확률을 반환합니다.

이러한 경우 결과를 얻으려면 seq(map(int64, tensor(float))(가장 높은 확률을 가진 요소 찾기)와 같은 후처리가 필요합니다.

따라서 ONNX 모델로 작업할 때는 세심한 주의를 기울이고 이러한 측면을 고려해야 합니다. 다양한 결과 처리의 예는 2.28.2의 스크립트에 나와 있습니다.


iris.mqh

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(서포트 벡터 분류), LinearSVC(선형 서포트 벡터 분류), NuSVC(뉴 서포트 벡터 분류).

운영 원칙:

    SVC(지원 벡터 분류)
        작동 원리: SVC는 클래스 간의 마진을 극대화하는 분류 메서드입니다. SVC는 클래스를 최대한 분리하고 서포트 벡터를 지원하는 최적의 분리 초평면(초평면에 가장 가까운 점)을 찾습니다.
        커널 함수: SVC는 선형, 방사형 기저 함수(RBF), 다항식 등 다양한 커널 함수를 사용할 수 있습니다. 커널 함수는 최적의 초평면을 찾기 위해 데이터를 변환하는 방법을 결정합니다.

    LinearSVC(선형 지원 벡터 분류)
        작동 원리: LinearSVC는 선형 분류에 특화된 SVC의 변형입니다. LinearSVC는 커널 함수를 사용하지 않고 최적의 선형 분리 초평면을 찾습니다. 따라서 대량의 데이터로 작업할 때 더 빠르고 효율적으로 작업할 수 있습니다.

    NuSVC(Nu 지원 벡터 분류)
        작동 원리: NuSVC도 서포트 벡터 메서드를 기반으로 하지만 모델의 복잡도와 서포트 벡터의 비율을 제어하는 매개 변수 Nu(nu)를 도입합니다. Nu 값은 0에서 1 사이의 범위이며 이 값이 지원 벡터 및 오류에 사용할 수 있는 데이터의 양을 결정합니다.

장점:

    SVC
        강력한 알고리즘: SVC는 커널 함수를 사용해서 복잡한 분류 작업을 처리하고 비선형 데이터로 작업할 수 있습니다.
        이상값에 대한 견고성: SVC는 서포트 벡터를 사용하여 분리 초평면을 구축하기 때문에 데이터 이상값에 강합니다.

    LinearSVC
        높은 효율성: LinearSVC는 특히 데이터가 크고 선형 분리가 작업에 적합한 경우 대규모 데이터 집합을 처리할 때 더 빠르고 효율적입니다.
        선형 분류: 문제가 선형적으로 잘 분리될 수 있는 경우 LinearSVC는 복잡한 커널 함수 없이도 좋은 결과를 얻을 수 있습니다.

    NuSVC
        모델 복잡도 제어: NuSVC의 Nu 매개변수를 사용하면 모델의 복잡성과 데이터 맞춤과 일반화 사이의 균형을 제어할 수 있습니다.
        이상값에 대한 견고성: SVC와 마찬가지로 NuSVC는 이상값에 강하므로 노이즈가 많은 데이터가 있는 작업에 유용합니다.

제한 사항:

    SVC
        계산의 복잡성: SVC는 대규모 데이터 세트나 복잡한 커널 함수를 사용하는 경우 느려질 수 있습니다.
        커널 감도: 올바른 커널 함수를 선택하는 것은 어려운 작업이며 모델 성능에 큰 영향을 미칠 수 있습니다.

    LinearSVC
        선형성 제약 조건: LinearSVC는 선형 데이터 분리에 의해 제약을 받으며 피처와 대상 변수 간에 비선형 종속성이 있는 경우 성능이 저하될 수 있습니다.

    NuSVC
        뉴 파라미터 튜닝: Nu 매개변수를 조정하여 최적의 결과를 얻으려고 할 경우 많은 시간과 실험이 필요할 수 있습니다.

작업 특성과 데이터 양에 따라 이러한 각 메서드가 최선의 선택이 될 수 있습니다. 그러므로 실험을 수행하여 특정 분류 작업 요구 사항에 가장 적합한 메서드를 선택하는 것이 중요합니다.



2.1. SVC 분류기

서포트 벡터 분류(SVC) 분류 메서드는 분류 작업을 해결하는 데 널리 사용되는 강력한 머신 러닝 알고리즘입니다.

운영 원칙:

  1. 최적의 분리 하이퍼플레인
    작동 원리: SVC의 기본 개념은 피처 공간에서 최적의 분리 초평면을 찾는 것입니다. 이 초평면은 서로 다른 클래스의 오브젝트 간의 분리를 최대화하고 초평면에 가장 가까운 데이터 포인트인 서포트 벡터를 지원해야 합니다.
    마진 극대화: SVC는 클래스 간 마진, 즉 서포트 벡터에서 초평면까지의 거리를 최대화하는 것을 목표로 합니다. 이를 통해 이 메서드는 이상값에 대해 강력하고 새로운 데이터에 잘 일반화할 수 있습니다.

  2. 커널 함수 활용
    커널 함수: SVC는 선형, 방사형 기저 함수(RBF), 다항식 등 다양한 커널 함수를 사용할 수 있습니다. 커널 함수를 사용하면 원래 데이터 공간에 선형 분리 가능성이 없더라도 작업이 선형화되는 고차원 공간으로 데이터를 투영할 수 있습니다.
    커널 선택: 올바른 커널 함수를 선택은 SVC 모델의 성능에 큰 영향을 미칠 수 있습니다. 선형 초평면이 항상 최적의 솔루션은 아닙니다.

장점:

  • 강력한 알고리즘. 복잡한 작업 처리하기: SVC는 피처와 대상 변수 간에 비선형 종속성이 있는 분류 작업을 포함해 복잡한 분류 작업을 해결할 수 있습니다.
  • 이상값에 대한 견고성: 지원 벡터를 사용하면 데이터 이상값에 대해 강력한 메서드를 사용할 수 있습니다. 전체 데이터 세트가 아닌 지원 벡터에 따라 달라집니다.
  • 커널 유연성. 데이터에 대한 적응성: 다양한 커널 함수를 사용할 수 있다는 사실은 SVC가 특정 데이터에 적응하게 하고 비선형 관계를 발견할 수 있게 합니다.
  • 좋은 일반화. 새 데이터로 일반화: SVC 모델은 새로운 데이터에도 잘 일반화할 수 있어 예측 작업에 유용합니다.

제한 사항:

  • 계산 복잡성. 훈련 시간: 특히 대량의 데이터나 복잡한 커널 함수를 처리하는 경우 SVC는 학습 속도가 느려질 수 있습니다.
  • 커널 선택. 올바른 커널 함수 선택: 올바른 커널 함수를 선택하는 것은 실험적일 수 있으며 데이터 특성에 따라 달라질 수 있습니다.
  • 피처 스케일링에 대한 민감도. 데이터 정규화: SVC는 피처 스케일링에 민감하므로 학습 전에 데이터를 정규화하거나 표준화하는 것이 좋습니다.
  • 모델 해석 가능성. 해석의 복잡성: SVC 모델은 비선형 커널과 다양한 지원 벡터를 사용하기 때문에 해석을 하는 것이 복잡할 수 있습니다.

특정의 작업과 데이터 양에 따라 SVC 메서드는 분류 작업을 해결하는 데 강력한 도구가 될 수 있습니다. 그러나 최적의 결과를 얻으려면 그 한계를 고려하고 매개변수를 조정하는 것이 중요합니다.

2.1.1. SVC 분류기 모델 생성 코드

이 코드는 붓꽃 데이터 세트에 대해 SVC 분류기 모델을 학습하고 이를 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에서 스크립트를 실행하면 이후 저널 탭에서 실행 결과를 볼 수 있습니다.

그림 12. MetaEditor에서 Iris_SVMClassifier.py 스크립트의 결과

그림 12. MetaEditor에서 Iris_SVMClassifier.py 스크립트 결과

Iris_SVCClassifier.py 스크립트의 출력입니다:

Python SVC 분류기 모델의 정확도: 0.9933333333333333
Python   
파이썬 분류 보고서:
파이썬 정밀 리콜 f1 점수 지원
Python   
파이썬 0 1.00 1.00 1.00 50
파이썬 1 1.00 0.98 0.99 50
파이썬 2 0.98 1.00 0.99 50
Python   
파이썬 정확도 0.99 150
파이썬 매크로 평균 0.99 0.99 0.99 150
파이썬 가중 평균 0.99 0.99 0.99 150
Python   
파이썬 모델은 C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\svc_iris.onnx에 저장됩니다.
Python   
파이썬 ONNX의 입력 텐서에 대한 정보입니다:
Python 1. Name: float_input, Data Type: tensor(float), Shape: [None, 4]
Python   
Python ONNX의 출력 텐서에 대한 정보입니다:
Python 1. Name: label, Data Type: tensor(int64), Shape: [None]
Python 2. Name: probabilities, Data Type: tensor(float), Shape: [None, 3]
Python   
Python ONNX 형식의 SVC 분류기 모델 정확도: 0.9933333333333333

여기에서 ONNX 모델이 저장된 경로, ONNX 모델의 입력 및 출력 매개변수 유형, 붓꽃 데이터 세트 설명의 정확도에 대한 정보를 확인할 수 있습니다.

SVM 분류기를 사용하여 데이터 집합을 설명하는 정확도는 99%이며 ONNX 형식으로 내보낸 모델도 동일한 수준의 정확도를 보여줍니다.

이제 150개의 데이터 샘플 각각에 대해 구성된 모델을 실행하여 MQL5에서 이들 결과를 검증해 보겠습니다. 또한 스크립트에는 일괄 데이터 처리의 예가 포함되어 있습니다.


2.1.2. SVC 분류기 모델 작업을 위한 MQL5 코드

//+------------------------------------------------------------------+
//|                                           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);
  }
//+------------------------------------------------------------------+

스크립트 실행 결과는 MetaTrader 5 터미널의 "Experts" 탭에 표시됩니다.

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 모델은 150개 샘플 중 149개를 정확하게 분류했습니다. 이는 매우 우수한 결과입니다. 이 모델은 붓꽃 데이터 세트에서 단 한 번의 분류 오류만 발생했는데 샘플 #84에 대해 클래스 1(버지니카)이 아닌 클래스 2(버지컬러)를 예측했습니다.

전체 붓꽃 데이터 세트에서 내보낸 ONNX 모델의 정확도는 99.33%로 원래 모델의 정확도와 일치한다는 점에 주목할 필요가 있습니다.


2.1.3. SVC 분류기 모델의 ONNX 표현

MetaEditor에서 빌드된 ONNX 모델을 볼 수 있습니다.


그림 13. MetaEditor의 ONNX 모델 svc_iris.onnx

그림 13. MetaEditor의 ONNX 모델 svc_iris.onnx


모델 아키텍처에 대한 자세한 내용은 Netron에서 확인할 수 있습니다. 이렇게 하려면 MetaEditor에서 모델 설명의 "네트론에서 열기" 버튼을 클릭합니다.


그림 14. 네트론의 ONNX 모델 svc_iris.onnx

그림 14. 네트론의 ONNX 모델 svc_iris.onnx


모델에 있는 ONNX 연산자를 마우스로 가리키면 이러한 연산자의 매개변수에 대한 정보를 얻을 수 있습니다(그림 15의 SVMClassifier).


그림 15. 네트론의 ONNX 모델 svc_iris.onnx(SVMClassifier ONNX 오퍼레이터 파라미터)

그림 15. 네트론의 ONNX 모델 svc_iris.onnx(SVMClassifier ONNX 연산자 매개변수)



2.2. LinearSVC 분류기

LinearSVC(선형 지원 벡터 분류)는 이진 및 다중 클래스 분류 작업에 사용되는 강력한 머신 러닝 알고리즘입니다. 이는 데이터를 가장 잘 분리하는 초평면을 찾는 아이디어를 기반으로 합니다.

LinearSVC의 원리:

  1. 최적의 초평면 찾기: LinearSVC의 주요 아이디어는 두 데이터 클래스를 최대한 분리하는 최적의 초평면을 찾는 것입니다. 초평면은 선형 방정식으로 정의된 다차원 평면입니다.
  2. 마진 최소화: LinearSVC는 여백(데이터 포인트와 초평면 사이의 거리)을 최소화하는 것을 목표로 합니다. 여백이 클수록 초평면이 클래스를 더 효과적으로 구분합니다.
  3. 선형적으로 분리할 수 없는 데이터 처리: LinearSVC를 통해 데이터를 선형적으로 분리할 수 있는 고차원 공간으로 투영하는 커널 함수(커널 트릭)를 사용하여 원래 피처 공간에서 선형적으로 분리할 수 없는 데이터도 작업할 수 있습니다.

LinearSVC의 장점:

  • 일반화: LinearSVC는 일반화 능력이 뛰어나며 보이지 않는 새로운 데이터에 대해서도 일반화를 잘 수행할 수 있습니다.
  • 효율성: LinearSVC는 대규모 데이터 세트에서 빠르게 작동하며 상대적으로 적은 컴퓨팅 리소스를 요구 합니다.
  • 선형적으로 분리할 수 없는 데이터 처리: 커널 함수를 사용하여 LinearSVC는 선형적으로 분리할 수 없는 데이터로 분류 작업을 처리할 수 있습니다.
  • 확장성: LinearSVC는 피처가 많고 데이터 양이 많은 작업에서 효율적으로 사용할 수 있습니다.

LinearSVC의 한계:

  • 선형 분리 초평면만 가능합니다: LinearSVC는 선형 분리 초평면만 구성하므로 비선형 종속성이 있는 복잡한 분류 작업에는 불충분할 수 있습니다.
  • 매개변수 선택: 올바른 매개변수(예: 정규화 매개변수)를 선택하려면 전문적인 지식이나 교차 검증이 필요할 수 있습니다.
  • 이상값에 대한 민감도: LinearSVC는 데이터의 이상값에 민감할 수 있으며 이는 분류 품질에 영향을 미칠 수 있습니다.
  • 모델 해석 가능성: LinearSVC를 사용하여 생성된 모델은 다른 메서드에 비해 해석이 어려울 수 있습니다.

LinearSVC는 일반화, 효율성, 선형적으로 분리할 수 없는 데이터를 처리하는 데 탁월한 강력한 분류 알고리즘입니다. 특히 선형 초평면으로 데이터를 분리할 수 있는 경우 다양한 분류 작업에서 응용할 수 있습니다. 그러나 비선형 종속성을 모델링해야 하는 복잡한 작업의 경우 LinearSVC가 적합하지 않을 수 있으며, 이러한 경우 더 복잡한 결정 경계를 가진 대체적인 메서드를 고려해야 합니다.


2.2.1. LinearSVC 분류기 모델 생성 코드

이 코드는 붓꽃 데이터 세트에서 LinearSVC 분류기 모델을 학습하고 이를 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 LinearSVC 모델의 정확도: 0.9666666666666667
파이썬    
파이썬 분류 보고서:
파이썬 정밀 리콜 f1 점수 지원
파이썬    
파이썬 0 1.00 1.00 1.00 50
파이썬 1 0.96 0.94 0.95 50
파이썬 2 0.94 0.96 0.95 50
파이썬    
파이썬 정확도 0.97 150
파이썬 매크로 평균 0.97 0.97 0.97 150
파이썬 가중 평균 0.97 0.97 0.97 150
파이썬    
파이썬 모델은 C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\linear_svc_iris.onnx에 저장됩니다.
파이썬    
파이썬 ONNX의 입력 텐서에 대한 정보입니다:
파이썬 1. Name: float_input, Data Type: tensor(float), Shape: [None, 4]
파이썬    
Python ONNX의 출력 텐서에 대한 정보:
파이썬 1. Name: label, Data Type: tensor(int64), Shape: [None]
Python 2. Name: probabilities, Data Type: tensor(float), Shape: [None, 3]
파이썬    
Python ONNX 형식의 LinearSVC 모델 정확도: 0.9666666666666667


2.2.2. LinearSVC 분류기 모델 작업을 위한 MQL5 코드

//+------------------------------------------------------------------+
//|                                               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 모델의 정확도는 96.67%로 원래 모델의 정확도와 일치합니다.


2.2.3. LinearSVC 분류기 모델의 ONNX 표현

그림 16. 네트론에서 LinearSVC 분류기 모델의 ONNX 표현

그림 16. 네트론에서 리니어SVC 분류기 모델의 ONNX 표현


2.3. NuSVC 분류기

Nu-지원 벡터 분류(NuSVC) 메서드는 서포트 벡터 머신(SVM) 접근법을 기반으로 하는 강력한 머신 러닝 알고리즘입니다.

NuSVC의 원칙:

  1. SVM(서포트 벡터 머신): NuSVC는 이진 및 다중 클래스 분류 작업에 사용되는 SVM의 변형입니다. SVM의 핵심 원리는 최대한의 마진을 유지하면서 클래스를 최대한 분리하는 최적의 분리 초평면을 찾는 것입니다.
  2. Nu 파라미터: NuSVC에서 핵심이 되는 파라미터는 모델의 복잡도를 제어하고 지원 벡터 및 오류로 사용할 수 있는 샘플의 비율을 정의하는 Nu 파라미터(nu)입니다. Nu의 값은 0에서 1 사이이며 0.5는 샘플의 약 절반이 지원 벡터와 오류로 사용됨을 의미합니다.
  3. 매개변수 조정: Nu 매개변수 및 기타 하이퍼파라미터의 최적값을 결정하려면 교차 검증과 학습 데이터에서 최적의 값을 찾아야 할 수 있습니다.
  4. 커널 함수: NuSVC는 선형, 방사형 기저 함수(RBF), 다항식 등 다양한 커널 함수를 사용할 수 있습니다. 커널 함수는 분리하는 초평면을 찾기 위해 피처 공간을 변환하는 방법을 결정합니다.

NuSVC의 장점:

  • 고차원 공간에서의 효율성: NuSVC는 고차원 공간에서 효율적으로 작업할 수 있으므로 피처가 많은 작업에 적합합니다.
  • 이상값에 대한 견고성: 특히 SVM과 NuSVC는 서포트 벡터를 사용하기 때문에 데이터의 이상값에 대해 강합니다.
  • 모델 복잡성 제어: Nu 매개변수를 사용하면 모델 복잡성을 제어하고 데이터 피팅과 일반화의 균형을 맞출 수 있습니다.
  • 좋은 일반화: 특히 SVM과 NuSVC는 일반화에 특출납니다. 그러므로 이전에는 볼 수 없었던 새로운 데이터에 대해 뛰어난 성능을 발휘합니다.

NuSVC의 한계:

  • 대용량 데이터에서의 비효율성: NuSVC는 계산 복잡성으로 인해 대량의 데이터에 대해 학습할 때 비효율적일 수 있습니다.
  • 매개변수 조정이 필요합니다: Nu 매개변수와 커널 함수를 조정하려면 상당한 시간과 컴퓨팅 리소스가 필요할 수 있습니다.
  • 커널 함수 선형성: NuSVC의 효과는 커널 기능의 선택에 따라 크게 달라질 수 있으며 일부 작업의 경우 다른 함수들로 실험해야 할 수도 있습니다.
  • 모델 해석 가능성: SVM과 NuSVC는 뛰어난 결과를 제공하지만 특히 비선형 커널을 사용하는 경우 모델을 해석을 하는것이 복잡할 수 있습니다.

Nu-지원 벡터 분류(NuSVC)는 이상값에 대한 견고성과 우수한 일반화 등 여러 가지 장점을 가진 SVM 기반의 강력한 분류 메서드입니다. 그러나 그 효과는 매개변수 및 커널 함수 선택에 따라 달라지며 대용량 데이터의 경우 비효율적일 수 있습니다. 여러분은 매개변수를 신중하게 선택하고 특정 분류 작업에 맞게 메서드를 조정하는 것이 중요합니다.


2.3.1. NuSVC 분류기 모델 생성 코드

이 코드는 붓꽃 데이터 세트에서 NuSVC 분류기 모델을 학습하고 이를 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 NuSVC 모델의 정확도: 0.9733333333333334
파이썬    
파이썬 분류 보고서:
파이썬 정밀 리콜 f1 점수 지원
파이썬    
파이썬 0 1.00 1.00 1.00 50
파이썬 1 0.96 0.96 0.96 50
파이썬 2 0.96 0.96 0.96 50
파이썬    
파이썬 정확도 0.97 150
파이썬 매크로 평균 0.97 0.97 0.97 150
파이썬 가중 평균 0.97 0.97 0.97 150
파이썬    
파이썬 모델은 C:\사용자\사용자\앱데이터\로밍\MetaQuotes\터미널\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\스크립트\nusvc_iris.onnx에 저장됩니다.
파이썬    
파이썬 ONNX의 입력 텐서에 대한 정보입니다:
파이썬 1. Name: float_input, Data Type: tensor(float), Shape: [None, 4]
파이썬    
Python ONNX의 출력 텐서에 대한 정보:
파이썬 1. Name: label, Data Type: tensor(int64), Shape: [None]
파이썬 2. Name: probabilities, Data Type: tensor(float), Shape: [None, 3]
파이썬    
Python ONNX 형식의 NuSVC 모델 정확도: 0.9733333333333334


2.3.2. NuSVC 분류기 모델 작업을 위한 MQL5 코드

//+------------------------------------------------------------------+
//|                                                   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 모델의 정확도는 97.33%로 원래 모델의 정확도와 일치합니다.


2.3.3. NuSVC 분류기 모델의 ONNX 표현

그림 17. Netron에서 NuSVC 분류기 모델의 ONNX 표현

그림 17. 트론에서 NuSVC 분류기 모델의 ONNX 표현


2.4. 반경 이웃 분류기

반경 이웃 분류기는 객체 간 근접성의 원리를 기반으로 분류 작업에 사용되는 머신 러닝 메서드입니다. 고정된 수의 가장 가까운 이웃(K)이 선택되는 기존의 K-NN(K-Nearest Neighbors) 분류기와 달리 반경 이웃 분류기에서는 지정된 반경 내에서 가장 가까운 이웃과의 거리를 기준으로 객체를 분류합니다.

반경 이웃 분류기의 원리:
  1. 반경 결정하기: 반경 이웃 분류기의 주요 매개변수는 반경으로 이웃 클래스에 가까운 것으로 간주될 수 있는 객체와 이웃 클래스 사이의 최대 거리를 정의합니다.
  2. 가장 가까운 이웃 찾기: 학습 데이터 세트의 다른 모든 객체와의 거리는 각 객체에 대해 계산됩니다. 지정된 반경 내에 위치한 객체는 객체의 이웃으로 간주됩니다.
  3. 투표: 반경 이웃 분류기는 이웃 간의 다수결 투표를 사용하여 객체의 클래스를 결정합니다. 예를 들어 대부분의 이웃이 클래스 A에 속하는 경우 해당 객체도 클래스 A로 분류됩니다.
반경 이웃 분류기의 장점:
  • 데이터 밀도에 대한 적응성: 반경 이웃 분류기는 다양한 피처 공간 영역의 데이터 밀도가 다를 수도 있는 작업에 적합합니다.
  • 다양한 클래스 모양으로 작업할 수 있습니다: 이 메서드는 클래스가 복잡하고 비선형적인 모양을 가진 작업에서 잘 수행됩니다.
  • 이상값이 있는 데이터에 적합합니다: 반경 이웃 분류기는 지정된 반경 너머에 위치한 이웃을 무시하기 때문에 K-NN보다 이상값에 더 강력합니다.
반경 이웃 분류기의 한계:
  • 반경 선택에 대한 민감도: 최적의 반경 값을 선택하는 것은 간단한 작업이 아니며 조정이 필요할 수 있습니다.
  • 대규모 데이터 세트에서의 비효율성: 대규모 데이터 세트의 경우 모든 객체까지의 거리를 계산하면 계산 비용이 많이 들 수 있습니다.
  • 데이터 밀도에 따른 의존성: 이 메서드는 피처 공간에서 데이터의 밀도가 균일하지 않은 경우 효율성이 떨어질 수 있습니다.

반경 이웃 분류기는 객체 근접성이 중요하고 클래스 모양이 복잡할 수 있는 상황에서 유용한 머신 러닝 메서드이며 이미지 분석, 자연어 처리 등 다양한 영역에 적용할 수 있습니다.


2.4.1. 반경 이웃 분류기 모델을 생성하는 코드

이 코드는 붓꽃 데이터 세트에서 반경 이웃 분류기 모델을 훈련하고 이를 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 반경 이웃 분류기 모델의 정확도: 0.9733333333333334
파이썬    
파이썬 분류 보고서:
파이썬 정밀 리콜 f1 점수 지원
파이썬    
파이썬 0 1.00 1.00 1.00 50
파이썬 1 0.94 0.98 0.96 50
파이썬 2 0.98 0.94 0.96 50
파이썬    
파이썬 정확도 0.97 150
파이썬 매크로 평균 0.97 0.97 0.97 150
파이썬 가중 평균 0.97 0.97 0.97 150
파이썬    
파이썬 모델은 C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\radius_neighbors_iris.onnx에 저장됩니다.
파이썬    
파이썬 ONNX의 입력 텐서에 대한 정보입니다:
파이썬 1. Name: float_input, Data Type: tensor(float), Shape: [None, 4]
파이썬    
Python ONNX의 출력 텐서에 대한 정보:
파이썬 1. Name: label, Data Type: tensor(int64), Shape: [None]
파이썬 2. Name: probabilities, Data Type: tensor(float), Shape: [None, 3]
파이썬    
Python 반경 이웃 분류기 모델의 정확도(ONNX 형식): 0.9733333333333334

원래 모델의 정확도와 ONNX 형식으로 내보낸 모델의 정확도는 동일합니다.


2.4.2. 반경 이웃 분류기 모델 작업을 위한 MQL5 코드

//+------------------------------------------------------------------+
//|                               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

반경 이웃 분류기 모델은 4개의 분류 오류(샘플 78, 107, 127, 139)로 97.33%의 정확도를 보였습니다.

전체 붓꽃 데이터 세트에서 내보낸 ONNX 모델의 정확도는 97.33%로 원래 모델의 정확도와 일치합니다.


2.4.3.반경 이웃 분류기 모델의 ONNX 표현

그림 18. 네트론에서 반경 이웃 분류기의 ONNX 표현

그림 18. 네트론에서 반경 이웃 분류기의 ONNX 표현


RidgeClassifier 및 RidgeClassifierCV 메서드에 대한 참고 사항

RidgeClassifier와 RidgeClassifierCV는 릿지 회귀를 기반으로 하는 두 가지 분류 메서드이지만 매개변수를 조정되는 방식과 하이퍼파라미터가 자동으로 선택되는 방식이라는 점에서 서로 다릅니다:

RidgeClassifier:

  • RidgeClassifier는 이진 및 다중 클래스 분류 작업에 사용되는 Ridge 회귀에 기반한 분류 메서드입니다.
  • 다중 클래스 분류의 경우 RidgeClassifier는 작업을 여러 개의 이진 작업(하나 대 전체)으로 변환하고 각각에 대한 모델을 구축합니다.
  • 정규화 매개변수 알파는 사용자가 수동으로 조정해야 하므로 실험이나 검증 데이터 분석을 통해 최적의 알파 값을 선택해야 합니다.

RidgeClassifierCV:

  • RidgeClassifierCV는 교차 검증과 최적의 정규화 파라미터 알파의 자동 선택을 위한 내장된 지원을 제공하는 RidgeClassifier의 확장 기능입니다.
  • 알파를 수동으로 설정하는 대신 여러분은 조사할 알파 값 목록을 RidgeClassifierCV에 제공하고 교차 검증 메서드를 지정할 수 있습니다(예: cv 매개변수를 통해).
  • RidgeClassifierCV는 교차 검증 중에 가장 성능이 좋은 최적의 알파 값을 자동으로 선택합니다.

따라서 이들의 주요 차이점은 정규화 매개변수 알파의 최적값을 선택하는 자동화 수준에 있습니다. RidgeClassifier는 알파를 수동으로 조정해야 하는 반면 RidgeClassifierCV는 교차 검증을 통해서 최적의 알파 값을 자동으로 선택할 수 있습니다. 이 중 어떤 것을 선택할지는 모델 튜닝 프로세스의 요구 사항과 자동화에 대한 욕구에 따라 달라집니다.


2.5. 릿지 분류기

릿지 분류기는 모델에 L2 정규화(릿지 회귀)를 포함하는 로지스틱 회귀의 변형입니다. L2 정규화는 모델의 큰 계수에 페널티를 추가하여 과적합을 줄이고 모델의 일반화 능력을 개선하는 데 도움이 됩니다.

릿지 분류기의 원리:

  1. 확률 예측: 로지스틱 회귀와 마찬가지로 릿지 분류기는 로지스틱(시그모이드) 함수를 사용하여 특정 클래스에 속하는 객체의 확률을 모델링합니다.
  2. L2 정규화: 릿지 분류기는 모델의 큰 계수에 불이익을 주는 L2 정규화 항을 추가합니다. 이는 모델의 복잡성을 제어하고 과적합을 줄이기 위해서 입니다.
  3. 매개변수 학습: 릿지 분류기 모델은 학습 데이터 세트에서 학습되어 피처에 대한 가중치(계수)와 정규화 매개변수를 조정합니다.

릿지 분류기의 장점:

  • 오버피팅 감소: L2 정규화는 모델의 과적합 경향을 줄이는 데 도움이 되며 데이터가 제한적일 때 특히 유용합니다.
  • 다중선형성 처리하기: 릿지 분류기는 피처들이 서로 높은 상관관계를 갖는 다중 상관성 문제를 잘 처리합니다.

릿지 분류기의 한계:

  • 정규화 매개변수 선택에 대한 민감도: 다른 정규화 메서드와 마찬가지로 정규화 매개변수(알파)에 적합한 값을 선택하려면 튜닝과 평가가 필요합니다.
  • 멀티클래스 분류 제약 조건: 릿지 분류기는 처음에 이진 분류용으로 설계되었지만 One-vs-All과 같은 접근 방식을 사용하여 다중 클래스 분류에도 적용할 수 있습니다.

릿지 분류기는 로지스틱 회귀와 정규화의 이점을 결합하여 과적합을 방지하고 모델의 일반화 능력을 향상시키는 강력한 머신 러닝 메서드로 특히 확률적 분류와 모델 복잡도 제어가 중요한 다양한 분야에서 활용되고 있습니다.


2.5.1. 릿지 분류기 모델 생성 코드

이 코드는 붓꽃 데이터 세트에서 릿지 분류기 모델을 학습하고 이를 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 릿지 분류기 모델의 정확도: 0.8533333333333334
파이썬    
파이썬 분류 보고서:
파이썬 정밀 리콜 f1-점수 지원
파이썬    
파이썬 0 1.00 1.00 1.00 50
파이썬 1 0.87 0.66 0.75 50
파이썬 2 0.73 0.90 0.80 50
파이썬    
파이썬 정확도 0.85 150
파이썬 매크로 평균 0.86 0.85 0.85 150
파이썬 가중 평균 0.86 0.85 0.85 150
파이썬    
파이썬 모델은 C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\ridge_classifier_iris.onnx에 저장됩니다.
파이썬    
파이썬 ONNX의 입력 텐서에 대한 정보입니다:
파이썬 1. Name: float_input, Data Type: tensor(float), Shape: [None, 4]
파이썬    
Python ONNX의 출력 텐서에 대한 정보:
파이썬 1. Name: label, Data Type: tensor(int64), Shape: [None]
파이썬 2. Name: probabilities, Data Type: tensor(float), Shape: [None, 3]
파이썬    
Python ONNX 형식의 릿지 분류기 모델의 정확도: 0.8533333333333334


2.5.2. 릿지 분류기 모델 작업을 위한 MQL5 코드

//+------------------------------------------------------------------+
//|                                         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

전체 붓꽃 데이터 세트에서 모델은 85.33%의 정확도를 보였으며 이는 원본의 정확도와 일치하는 수치입니다.


2.5.3. 릿지 분류기 모델의 ONNX 표현

그림 19. 네트론에서 릿지 분류기 모델의 ONNX 표현

그림 19. 네트론의 릿지 분류기 모델에 대한 ONNX 표현


2.6. RidgeClassifierCV

RidgeClassifierCV 분류 메서드는 릿지 회귀에 기반한 이진 및 다중 클래스 분류를 위한 강력한 알고리즘입니다.

RidgeClassifierCV의 원리:

  1. 선형 릿지 회귀: RidgeClassifierCV는 선형 릿지 회귀를 기반으로 합니다. 이 메서드는 L2 정규화를 추가하는 선형 회귀의 변형입니다. 정규화는 피처 가중치의 크기를 줄여 과적합을 제어하는 데 도움이 됩니다.
  2. 이진 및 다중 클래스 분류: RidgeClassifierCV는 이진 분류(두 개의 클래스만 있는 경우)와 다중 클래스 분류(두 개 이상의 클래스가 있는 경우) 모두에 사용할 수 있습니다. 다중 클래스 분류의 경우 작업을 여러 개의 이진 작업(하나 대 전체)으로 변환하고 각각에 대한 모델을 구축합니다.
  3. 정규화 매개변수 자동 선택: 교차 검증과 최적의 정규화 매개변수 알파의 자동 선택을 지원하는 기능이 내장되어 있다는 점이 RidgeClassifierCV의 주요 장점 중 하나입니다. 이 메서드는 알파를 수동으로 조정하는 대신 다양한 알파 값을 반복하여 교차 검증하여 가장 적합한 알파 값을 선택합니다.
  4. 다중선형성 처리하기: 릿지 회귀는 피처가 서로 높은 상관 관계를 갖는 다중 상관성 문제를 잘 처리합니다. 정규화를 통해 각 피처의 기여도를 제어하여 상관관계가 있는 데이터에 대한 모델을 견고하게 만들 수 있습니다.

RidgeClassifierCV의 장점:

  • 자동 하이퍼파라미터 선택: 교차 검증을 통해 최적의 알파 값을 자동으로 선택할 수 있다는 점이 RidgeClassifierCV의 가장 큰 장점 중 하나입니다. 이렇게 하면 다양한 알파 값으로 실험할 필요가 없어지고 좋은 결과를 얻을 가능성이 높아집니다.
  • 오버핏 제어: RidgeClassifierCV에서 제공하는 L2 정규화는 모델 복잡성을 제어하고 과적합의 위험을 줄이는 데 도움이 됩니다. 이는 데이터가 제한된 작업에서 특히 중요합니다.
  • 투명성 및 해석 가능성: RidgeClassifierCV는 해석 가능한 피처 가중치를 제공하여 각 피처의 예측 기여도를 분석하고 피처 중요도에 대한 결론을 내릴 수 있습니다.
  • 효율성: 이 메서드는 매우 효율적이며 대규모 데이터 세트에 적용할 수 있습니다.

RidgeClassifierCV의 한계:

  • 선형성: RidgeClassifierCV는 피처와 대상 변수 간의 선형 관계를 가정합니다. 데이터에 강한 비선형 관계가 있는 경우 이 메서드는 정확하지 않을 수 있습니다.
  • 피처 스케일링 감도: 이 메서드는 피처 스케일링에 민감합니다. 피처를 표준화하거나 정규화한 후 RidgeClassifierCV를 적용하는 것이 좋습니다.
  • 최적의 피처 선택: RidgeClassifierCV는 자동 피처 선택을 수행하지 않으므로 모델에 포함할 피처를 수동으로 결정해야 합니다.

RidgeClassifierCV 분류 메서드는 최적의 정규화 파라미터를 자동으로 선택하는 이진 및 다중 클래스 분류를 위한 강력한 도구이며 오버핏 제어, 해석 가능성 및 효율성으로 다양한 분류 작업에 널리 사용되고 있습니다. 하지만 피처와 대상 변수 간의 선형 관계를 가정하는 등 한계점을 염두에 두는 것이 중요합니다.

2.6.1. RidgeClassifierCV 모델 생성 코드

이 코드는 붓꽃 데이터 세트에 대해 RidgeClassifierCV 모델을 학습시키고 이를 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 RidgeClassifierCV 모델의 정확도: 0.8533333333333334
파이썬    
파이썬 분류 보고서:
파이썬 정밀 리콜 f1-점수 지원
파이썬    
파이썬 0 1.00 1.00 1.00 50
파이썬 1 0.87 0.66 0.75 50
파이썬 2 0.73 0.90 0.80 50
파이썬    
파이썬 정확도 0.85 150
파이썬 매크로 평균 0.86 0.85 0.85 150
파이썬 가중 평균 0.86 0.85 0.85 150
파이썬    
파이썬 모델은 C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\ridge_classifier_cv_iris.onnx에 저장됩니다.
파이썬    
파이썬 ONNX의 입력 텐서에 대한 정보입니다:
파이썬 1. Name: float_input, Data Type: tensor(float), Shape: [None, 4]
파이썬    
Python ONNX의 출력 텐서에 대한 정보:
파이썬 1. Name: label, Data Type: tensor(int64), Shape: [None]
파이썬 2. Name: probabilities, Data Type: tensor(float), Shape: [None, 3]
파이썬    
Python ONNX 형식의 RidgeClassifierCV 모델 정확도: 0.8533333333333334


2.6.2. RidgeClassifierCV 모델 작업을 위한 MQL5 코드

//+------------------------------------------------------------------+
//|                                       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 모델의 성능은 오리지널 스키킷 학습 모델의 성능(85.33%)과도 완벽하게 일치합니다.


2.6.3. RidgeClassifierCV 모델의 ONNX 표현

그림 20. 넷트론에서 RidgeClassifierCV의 ONNX 표현

그림 20. 네트론에서 RidgeClassifierCV의 ONNX 표현



2.7. 랜덤 숲 분류기

랜덤 숲 분류기는 여러 개의 의사 결정 트리를 구성하고 그 결과를 결합하여 분류 품질을 향상시키는 앙상블 머신 러닝 메서드입니다. 이 메서드는 효과적이고 다양한 데이터로 작업할 수 있기 때문에 매우 인기가 있습니다.

랜덤 숲 분류기의 원리:

  1. 배깅(부트스트랩 애그리게이팅): 랜덤 숲은 학습 데이터에서 대체를 통해 여러 개의 하위 샘플(부트스트랩 샘플)을 생성하는 배깅 메서드를 사용하며 각 하위 샘플에 대해 별도의 의사 결정 트리가 구성됩니다.
  2. 무작위 피처 선택: 각 트리를 만들 때 전체 피처 집합에서 무작위로 피처의 하위 집합이 선택됩니다. 이렇게 하면 나무 간의 다양성을 촉진하고 나무 간의 상관관계를 줄일 수 있습니다.
  3. 투표: 객체를 분류할 때 각 트리는 자체 예측을 제공하며 모든 트리 중에서 가장 많은 표를 받은 클래스가 최종 모델 예측으로 선택됩니다.

랜덤 숲 분류기의 장점:

  • 높은 정확도: 랜덤 숲은 일반적으로 여러 트리의 결과를 평균화하여 높은 분류 정확도를 달성합니다.
  • 다양한 데이터를 처리하는 능력: 숫자 및 범주형 피처는 물론 다양한 구조의 데이터에도 잘 작동합니다.
  • 오버피팅 저항: 랜덤 숲에는 정규화 기능이 내장되어 있어 과적합에 강합니다.
  • 피처 중요도: 랜덤 숲은 피처의 중요성을 평가하여 데이터 과학자와 피처 엔지니어가 데이터를 더 잘 이해할 수 있도록 도와줍니다.

랜덤 숲분류기의 한계:

  • 계산의 복잡성: 특히 트리와 피처가 많은 경우 랜덤 숲 훈련에 시간이 많이 걸릴 수 있습니다.
  • 해석 가능성 문제: 많은 수의 트리와 무작위 피처 선택으로 인해 모델 해석이 어려울 수 있습니다.
  • 이상값 견고성이 보장되지 않습니다: 랜덤 숲이 항상 데이터 이상값에 대한 견고성을 제공하는 것은 아닙니다.

랜덤 숲 분류기는 생물 의학, 금융 분석, 텍스트 데이터 분석 등 다양한 분야에서 널리 사용되는 강력한 머신 러닝 알고리즘입니다. 분류 및 회귀 작업 해결에 탁월하며 일반화 능력이 높습니다.

2.7.1. 랜덤 숲 분류기 모델 생성 코드

이 코드는 붓꽃 데이터 세트에서 랜덤 숲 분류기 모델을 훈련하고 이를 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 랜덤 숲 분류기 모델의 정확도: 1.0
파이썬    
파이썬 분류 보고서:
파이썬 정밀 리콜 f1-점수 지원
파이썬    
파이썬 0 1.00 1.00 1.00 50
파이썬 1 1.00 1.00 1.00 50
파이썬 2 1.00 1.00 1.00 50
파이썬    
파이썬 정확도 1.00 150
파이썬 매크로 평균 1.00 1.00 1.00 150
파이썬 가중 평균 1.00 1.00 1.00 150
파이썬    
파이썬 모델은 C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\rf_iris.onnx에 저장됩니다.
파이썬    
파이썬 ONNX의 입력 텐서에 대한 정보입니다:
파이썬 1. Name: float_input, Data Type: tensor(float), Shape: [None, 4]
파이썬    
Python ONNX의 출력 텐서에 대한 정보:
파이썬 1. 이름: output_label, Data Type: tensor(int64), Shape: [None]
파이썬 2. Name: output_probability, Data Type: seq(map(int64,tensor(float))), Shape: []
파이썬    
Python ONNX 형식의 랜덤 숲 분류기 모델의 정확도: 1.0

랜덤 숲 분류기 모델(및 ONNX 버전)은 100% 정확도로 피셔의 붓꽃 분류 문제를 해결합니다.


2.7.2. 랜덤 숲 분류기 모델 작업을 위한 MQL5 코드

//+------------------------------------------------------------------+
//|                                  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 모델의 정확도는 100%이며 이는 원래 모델의 정확도와 일치합니다.


2.7.3. 랜덤 숲 분류기 모델의 ONNX 표현

그림 21. 넷트론에서 랜덤 숲 분류기 모델의 ONNX 표현

그림 21. 네트론에서 랜덤 숲 분류기 모델의 ONNX 표현


2.8. 경사 부스팅 분류기

경사 부스팅은 가장 강력한 머신러닝 메서드 중 하나로 높은 정확도와 다양한 데이터 작업 능력 덕분에 데이터 분석, 컴퓨터 비전, 자연어 처리, 재무 분석 등 다양한 영역에서 활용되고 있습니다. 경사 부스팅 분류기는 의사 결정 트리를 구성하여 분류 작업을 해결하는 앙상블 머신 러닝 메서드입니다. 이 메서드는 높은 정확도와 과적합에 대한 저항력으로 널리 사용됩니다.


경사 부스팅 분류기의 원리:

  1. 의사 결정 트리의 앙상블: 경사 부스팅 분류기는 의사 결정 트리의 앙상블을 구성하며 각 트리는 이전 트리의 예측을 개선하는 것을 목표로 합니다.
  2. 경사 하강: 경사 부스팅은 경사 하강을 사용하여 손실 함수를 최적화합니다. 경사 부스팅은 손실 함수의 기울기를 계산하고 이 기울기를 기반으로 예측을 업데이트하여 분류 오류를 최소화합니다.
  3. 트리 가중치: 컴포지션의 각 트리에는 가중치가 있으며 결국 모든 트리의 예측은 가중치를 고려하여 합산됩니다.

경사 부스팅 분류기의 장점:

  • 높은 정확도: 경사 부스팅 분류기는 일반적으로 높은 수준의 분류 정확도를 제공하며 가장 강력한 머신 러닝 메서드 중 하나입니다.
  • 오버피팅 저항: 정규화 및 경사 하강을 사용하기 때문에 이 메서드는 특히 하이퍼파라미터를 조정할 때 과적합에 강합니다.
  • 다양한 데이터 유형으로 작업할 수 있는 기능: 경사 부스팅 분류기는 숫자 및 범주형 피처를 포함한 다양한 데이터 유형을 처리할 수 있습니다.

경사 부스팅 분류기의 한계:

  • 계산의 복잡성: 경사 부스팅 분류기를 훈련시킬때 특히 많은 수의 트리나 깊은 트리가 있는 경우 계산에 많은 리소스가 필요할 수 있습니다.
  • 해석 가능성 문제: 여러 트리의 구성이 복잡하기 때문에 결과를 해석하는 것이 어려울 수 있습니다.
  • 소규모 데이터 집합에 항상 적합한 것은 아닙니다: 경사 부스팅은 일반적으로 효과적인 작동을 위해 상당한 양의 데이터가 필요하며 작은 데이터 세트에서는 과적합이 발생하기 쉽습니다.

경사 부스팅 분류기는 데이터 분석 대회에서 자주 사용되는 강력한 머신러닝 메서드로 다양한 분류 작업을 효과적으로 해결할수 있으며 데이터에서 복잡한 비선형 관계를 발견할 수 있고 하이퍼파라미터를 적절히 조정하면 우수한 일반화 성능을 발휘합니다.


2.8.1. 경사 부스팅 분류기(Gradient Boosting Classifier Model) 모델 생성 코드

이 코드는 붓꽃 데이터 세트에서 경사 부스팅 분류기 모델을 학습하고 이를 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 경사 부스팅 분류기 모델의 정확도: 1.0
파이썬    
파이썬 분류 보고서:
파이썬 정밀 리콜 f1-점수 지원
파이썬    
파이썬 0 1.00 1.00 1.00 50
파이썬 1 1.00 1.00 1.00 50
파이썬 2 1.00 1.00 1.00 50
파이썬    
파이썬 정확도 1.00 150
파이썬 매크로 평균 1.00 1.00 1.00 150
파이썬 가중 평균 1.00 1.00 1.00 150
파이썬    
파이썬 모델은 C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\gb_iris.onnx에 저장됩니다.
파이썬    
파이썬 ONNX의 입력 텐서에 대한 정보입니다:
파이썬 1. Name: float_input, Data Type: tensor(float), Shape: [None, 4]
파이썬    
Python ONNX의 출력 텐서에 대한 정보:
파이썬 1. 이름: output_label, Data Type: tensor(int64), Shape: [None]
파이썬 2. Name: output_probability, Data Type: seq(map(int64,tensor(float))), Shape: []
파이썬    
Python ONNX 형식의 경사 부스팅 분류기 모델의 정확도: 1.0

전체 붓꽃 데이터 세트에서 내보낸 ONNX 모델의 정확도는 100%이며 이는 원래 모델의 정확도와 일치합니다.


2.8.2. 경사 부스팅 분류기 모델 작업을 위한 MQL5 코드

//+------------------------------------------------------------------+
//|                              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 모델의 정확도는 100%이며 이는 원래 모델의 정확도와 일치합니다.


2.8.3. 경사 부스팅 분류기 모델의 ONNX 표현

그림 22. 네트론에서 경사 부스팅 분류기 모델의 ONNX 표현

그림 22. 네트론에서 경사 부스팅 분류기 모델의 ONNX 표현


2.9. 적응형 부스팅 분류기

AdaBoost(적응형 부스팅) 분류기는 여러 약한 분류기(예: 의사 결정 트리)의 결과를 결합하여 더 강력한 알고리즘을 만들어 분류를 향상시키는 데 사용되는 앙상블 머신 러닝 메서드입니다.

AdaBoost 분류기의 원리:

  1. 약한 분류기의 앙상블: AdaBoost는 훈련 세트에 있는 각 샘플의 가중치를 초기화하여 동일한 초기 값을 할당하는 것으로 시작합니다.
  2. 약한 분류자 훈련: 그런 다음 AdaBoost는 샘플 가중치를 고려하여 훈련 세트에 대해 약한 분류기(예: 의사 결정 트리)를 훈련합니다. 이 분류기는 샘플을 올바르게 분류하려고 시도합니다.
  3. 가중치 재분배: AdaBoost는 샘플 가중치를 조정하여 잘못 분류된 샘플의 가중치를 높이고 올바르게 분류된 샘플의 가중치를 낮춥니다.
  4. 컴포지션 생성: AdaBoost는 약한 분류기를 훈련하고 가중치를 재분배하는 과정을 여러 번 반복합니다. 그런 다음 이러한 약한 분류기의 결과를 조합하여 각각의 분류기별 정확도에 따라 기여하는 구성으로 결합합니다.

AdaBoost 분류기의 장점:

  • 높은 정확도: AdaBoost는 일반적으로 여러 개의 약한 분류기를 결합하여 높은 분류 정확도를 제공합니다.
  • 오버피팅 저항: AdaBoost에는 정규화 기능이 내장되어 있어 과적합에 강합니다.
  • 다양한 분류기로 작업할 수 있는 능력: AdaBoost는 다양한 기본 분류기를 사용할 수 있으므로 특정 작업에 맞게 조정할 수 있습니다.

AdaBoost 분류기의 한계:

  • 이상값에 대한 민감도: AdaBoost는 데이터의 이상값이 상당한 비중을 차지할 수 있으므로 데이터의 이상값에 민감할 수 있습니다.
  • 복잡한 작업에 항상 적합한 것은 아닙니다: 일부 복잡한 작업에서는 좋은 결과를 얻기 위해 많은 수의 기본 분류기가 필요할 수 있습니다.
  • 기본 분류기의 품질에 대한 의존도: 기본 분류기가 무작위 추측보다 우수할 때 AdaBoost의 성능이 더 우수합니다.

AdaBoost Classifier는 분류 작업을 해결하기 위해 실무에서 일반적으로 사용되는 강력한 머신 러닝 알고리즘이며 이진 및 다중 클래스 문제 모두에 적합하며 다양한 기본 분류기에 적용할 수 있습니다.


2.9.1. 적응형 부스팅 분류기(Adaptive Boosting Classifier) 모델 생성 코드

이 코드는 붓꽃 데이터 세트에서 적응형 부스팅 분류기 모델을 학습하고 이를 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 AdaBoost 분류기 모델의 정확도: 0.96
파이썬    
파이썬 분류 보고서:
파이썬 정밀 리콜 f1-점수 지원
파이썬    
파이썬 0 1.00 1.00 1.00 50
파이썬 1 0.92 0.96 0.94 50
파이썬 2 0.96 0.92 0.94 50
파이썬    
파이썬 정확도 0.96 150
파이썬 매크로 평균 0.96 0.96 0.96 150
파이썬 가중 평균 0.96 0.96 0.96 150
파이썬    
파이썬 모델은 C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\adaboost_iris.onnx에 저장됩니다.
파이썬    
파이썬 ONNX의 입력 텐서에 대한 정보입니다:
파이썬 1. Name: float_input, Data Type: tensor(float), Shape: [None, 4]
파이썬    
Python ONNX의 출력 텐서에 대한 정보:
파이썬 1. 이름: output_label, Data Type: tensor(int64), Shape: [None]
파이썬 2. Name: output_probability, Data Type: seq(map(int64,tensor(float))), Shape: []
파이썬    
Python ONNX 형식의 AdaBoost 분류기 모델 정확도: 0.96


2.9.2. 적응형 부스팅 분류기 모델 작업을 위한 MQL5 코드

//+------------------------------------------------------------------+
//|                                      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 모델의 정확도는 96%로 원래 모델의 정확도와 일치합니다.


2.9.3. 적응형 부스팅 분류기 모델의 ONNX 표현

그림 23. 네트론에서 적응형 부스팅 분류기의 ONNX 표현

그림 23. 네트론의 적응형 부스팅 분류기의 ONNX 표현


2.10. 부트스트랩 집계 분류기

배깅(부트스트랩 집계) 분류기는 학습 데이터에서 여러 개의 무작위 하위 샘플(부트스트랩 샘플)을 생성하고 각각에 대해 별도의 모델을 구축하는 앙상블 머신 러닝 메서드입니다. 그런 다음 결과는 결합되고 그리하여 모델의 일반화 능력을 향상시킵니다.

배깅 분류기의 원리:

  1. 하위 샘플 만들기: 배깅은 학습 데이터에서 무작위 하위 샘플(부트스트랩 샘플)을 여러 개 생성하여 교체하는 것으로 시작됩니다. 즉 동일한 샘플이 여러 하위 샘플에 나타날 수 있으며 일부 샘플은 생략될 수 있습니다.
  2. 기본 모델 학습: 각 하위 샘플에 대해 별도의 기본 모델(예: 의사 결정 트리)이 학습됩니다. 각 모델은 다른 모델과 독립적으로 훈련됩니다.
  3. 결과 집계: 모든 기본 모델을 학습한 후 모델의 예측 결과를 결합하여 최종 예측을 얻습니다. 이진 분류에서는 다수결 투표를 통해 이 작업을 수행할 수 있습니다.

배깅 분류기의 장점:

  • 분산 감소: 배깅은 여러 기본 모델의 결과를 평균화하여 모델의 편차를 줄임으로써 보다 안정적이고 신뢰할 수 있는 예측 결과를 도출할 수 있습니다.
  • 오버피팅 감소: 각각의 기본 모델은 서로 다른 하위 샘플로 학습되기 때문에 배깅을 사용하면 모델의 과적합 경향을 줄일 수 있습니다.
  • 다용도성: 배깅은 다양한 기본 모델을 사용할 수 있으므로 다양한 데이터 유형과 작업에 맞게 조정할 수 있습니다.

배깅 분류기의 한계:

  • 편향성 배깅은 편차를 줄이는 경향이 있지만 모델의 편향성을 해결하지는 못합니다. 기본 모델이 편향된 경향이 있는 경우(예: 언더 핏) 배깅으로 이 문제를 해결할 수 없습니다.
  • 복잡한 작업에 항상 적합한 것은 아닙니다: 일부 복잡한 작업에서는 좋은 결과를 얻기 위해 많은 수의 기본 모델을 필요로 할수 있습니다.

배깅 분류기는 모델의 일반화 능력을 향상시키고 과적합을 줄일 수 있는 효과적인 머신러닝 메서드이며 다양한 분류 및 회귀 작업을 처리하기 위해 다른 기본 모델과 함께 자주 사용됩니다.


2.10.1. 부트스트랩 집계 분류기 모델 생성 코드

이 코드는 붓꽃 데이터 세트에서 부트스트랩 집계 분류기 모델을 학습하고 이를 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 배깅 분류기 모델의 정확도: 1.0
파이썬    
파이썬 분류 보고서:
파이썬 정밀 리콜 f1-점수 지원
파이썬    
파이썬 0 1.00 1.00 1.00 50
파이썬 1 1.00 1.00 1.00 50
파이썬 2 1.00 1.00 1.00 50
파이썬    
파이썬 정확도 1.00 150
파이썬 매크로 평균 1.00 1.00 1.00 150
파이썬 가중 평균 1.00 1.00 1.00 150
파이썬    
파이썬 모델은 C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\bagging_iris.onnx에 저장됩니다.
파이썬    
파이썬 ONNX의 입력 텐서에 대한 정보입니다:
파이썬 1. Name: float_input, Data Type: tensor(float), Shape: [None, 4]
파이썬    
Python ONNX의 출력 텐서에 대한 정보:
파이썬 1. Name: output_label, Data Type: tensor(int64), Shape: [None]
파이썬 2. Name: output_probability, Data Type: seq(map(int64,tensor(float))), Shape: []
파이썬    
Python ONNX 형식의 배깅 분류기 모델의 정확도: 1.0

부트스트랩 집계 분류기 모델(및 ONNX 버전)은 붓꽃 데이터 세트를 분류하는 데 100%의 정확도를 달성했습니다.


2.10.2. 부트스트랩 집계 분류기 모델 작업을 위한 MQL5 코드

//+------------------------------------------------------------------+
//|                          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 모델의 정확도는 100%이며 이는 원래 모델의 정확도와 일치합니다.


2.10.3. 부트스트랩 집계 분류기의 ONNX 표현

그림 24. 넷트론에서 부트스트랩 집계 분류기의 ONNX 표현

그림 24. 네트론에서 부트스트랩 집계 분류기의 ONNX 표현


2.11. K-최근접 이웃(K-NN) 분류기

K-NN(최근접 이웃) 분류기는 데이터 포인트 간의 유사성을 기반으로 분류 및 회귀 작업을 해결하는 데 사용되는 머신 러닝 메서드로서 다차원 피처 공간에서 서로 가까운 객체는 비슷한 특성을 가지므로 비슷한 클래스 레이블을 가질 수 있다는 원칙에 따라 작동합니다.

K-NN 분류기의 원리:

  1. 근접성 결정: K-NN 분류기는 분류할 객체와 학습 데이터 세트의 다른 객체 사이의 근접성을 계산하며 유클리드 거리 또는 맨해튼 거리와 같은 거리 측정 기준을 사용하여 수행되는 경우가 많습니다.
  2. 이웃 수 선택하기: 매개변수 K는 객체를 분류하는 데 사용할 가장 가까운 이웃의 수를 결정합니다. 일반적으로 K는 작업과 데이터에 따라 선택됩니다.
  3. 투표: K-NN은 가장 가까운 K개의 이웃들 사이에서 다수결 투표를 사용하여 객체의 클래스를 결정합니다. 예를 들어 K 이웃의 대다수가 클래스 A에 속하는 경우 해당 객체도 클래스 A로 분류됩니다.

K-NN 분류기의 장점:

  • 단순함과 직관성: K-NN은 간단하고 직관적인 메서드로 이해하기 쉽고 적용하기도 쉽습니다.
  • 다양한 데이터 유형으로 작업할 수 있습니다: K-NN은 숫자, 범주형, 텍스트 데이터 등 다양한 데이터 유형에 사용될 수 있습니다.
  • 변화하는 데이터에 대한 적응력: K-NN은 데이터의 변화에 빠르게 적응할 수 있어 동적 데이터가 있는 작업에 적합합니다.

K-NN 분류기의 한계:

  • K의 선택에 대한 민감도: 최적의 K 값을 선택하는 것은 결코 쉬운 일이 아닙니다. K가 작으면 과적합으로 이어질 수 있고 K가 크면 과소적합으로 이어질 수 있습니다.
  • 피처 스케일링에 대한 민감도: K-NN은 피처 확장에 민감하므로 데이터 정규화가 중요할 수 있습니다.
  • 계산의 복잡성: 대규모 데이터 세트와 많은 수의 피처의 경우 모든 객체 쌍 간의 거리를 계산하는 데 많은 계산 비용이 발생할 수 있습니다.
  • 해석 가능성 부족: 특히 K가 크고 데이터의 양이 많은 경우에는 K-NN 결과를 해석하기가 어려울 수 있습니다.

K-NN 분류기는 추천 시스템, 텍스트 분류, 패턴 인식 등 객체 근접성이 필수적인 작업에 유용한 머신러닝 메서드로 초기 데이터 분석과 신속한 모델 프로토타이핑에 적합합니다.

2.11.1. K-최근접 이웃(K-NN) 분류기 모델 생성 코드

이 코드는 붓꽃 데이터 세트에서 K-Nearest Neighbors(K-NN) 분류기 모델을 훈련하고 이를 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 KNN 분류기 모델의 정확도: 0.96
파이썬   
파이썬 분류 보고서:
파이썬 정밀 리콜 f1-점수 지원
파이썬   
파이썬 0 1.00 1.00 1.00 50
파이썬 1 0.94 0.94 0.94 50
파이썬 2 0.94 0.94 0.94 50
파이썬   
파이썬 정확도 0.96 150
파이썬 매크로 평균 0.96 0.96 0.96 150
파이썬 가중 평균 0.96 0.96 0.96 150
파이썬   
파이썬 모델은 C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\knn_iris.onnx에 저장됩니다.
파이썬   
파이썬 ONNX의 입력 텐서에 대한 정보입니다:
Python 1. Name: float_input, Data Type: tensor(float), Shape: [None, 4]
파이썬   
Python 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 KNN 분류기 모델의 정확도를 ONNX 형식으로 표시합니다: 0.96


2.11.2. K-최근접 이웃(K-NN) 분류기 모델 작업을 위한 MQL5 코드

//+------------------------------------------------------------------+
//|                             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 모델의 정확도는 96%로 원래 모델의 정확도와 일치합니다.


2.11.3. K-NN(최근접 이웃) 분류기의 ONNX 표현

그림 25. 네트론에서 K-최근접 이웃의 ONNX 표현

그림 25. 네트론에서 가장 가까운 K-이웃 최근접 이웃을 표현하는 ONNX


2.12. 의사 결정 트리 분류기

의사 결정 트리 분류기는 의사 결정 트리의 구성을 기반으로 분류 작업에 사용되는 머신 러닝 메서드입니다. 이 메서드는 피처에 대해 일련의 조건부 테스트를 수행하여 데이터 집합을 더 작은 하위 그룹으로 나누고 트리에서 객체가 따라가는 경로에 따라 객체의 클래스를 결정합니다.

의사 결정 트리 분류기의 원리:

  1. 의사 결정 트리 구축하기: 처음에는 모든 데이터가 트리의 루트에 표시됩니다. 트리의 각 노드에 대해 데이터는 하나의 피처 값에 따라 두 개 이상의 하위 그룹으로 분할되며 이는 각 하위 그룹의 불확실성(예: 엔트로피 또는 지니 지수)을 최소화하기 위해서 입니다.
  2. 재귀적 구조: 데이터를 분할하는 과정은 트리가 잎에 도달할 때까지 재귀적으로 수행됩니다. 잎은 객체의 최종 클래스를 나타냅니다.
  3. 의사 결정: 객체가 트리에 들어오면 루트에서 잎 중 하나까지 경로를 따라 이동하며 해당 잎에 있는 대부분의 객체를 기준으로 클래스가 결정됩니다.

의사 결정 트리 분류기의 장점:

  • 해석 가능성: 의사 결정 트리는 쉽게 해석하고 시각화할 수 있습니다. 분류에 사용되는 결정 규칙을 이해하기 쉽습니다.
  • 다양한 데이터 유형 처리: 의사 결정 트리 분류기는 숫자 및 범주형 피처 모두에서 작동할 수 있습니다.
  • 피처 중요도: 의사 결정 트리는 피처의 중요성을 평가하여 데이터 분석가와 피처 엔지니어가 데이터를 이해하는 데 도움을 줍니다.

의사 결정 트리 분류기의 한계:

  • 오버피팅: 크고 깊은 트리는 과적합이 발생하기 쉬우므로 새로운 데이터에 일반화하기 어려울 수 있습니다.
  • 노이즈에 대한 민감도: 의사 결정 트리는 데이터의 노이즈와 이상값에 민감할 수 있습니다.
  • 탐욕스러운 구축: 의사 결정 트리는 탐욕스러운 알고리즘을 사용하여 구축되므로 최적이 아닌 글로벌 솔루션으로 이어질 수 있습니다.
  • 데이터 변경에 대한 불안정성: 데이터의 사소한 변경으로 인해 트리 구조가 크게 변경될 수 있습니다.

의사 결정 트리 분류기는 분류 작업에 유용한 머신 러닝 메서드으로 특히 모델 해석 가능성이 필수적이고 어떤 피처가 결정에 영향을 미치는지 이해해야 하는 상황에서 유용합니다. 이 메서드는 랜덤 숲 및 경사 부스팅과 같은 앙상블 메서드에도 사용할 수 있습니다.


2.12.1. 의사 결정 트리 분류기 모델 생성 코드

이 코드는 붓꽃 데이터 세트에서 의사 결정 트리 분류기 모델을 학습하고 이를 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 의사 결정 트리 분류기 모델의 정확도: 1.0
파이썬    
파이썬 분류 보고서:
파이썬 정밀 리콜 f1-점수 지원
파이썬    
파이썬 0 1.00 1.00 1.00 50
파이썬 1 1.00 1.00 1.00 50
파이썬 2 1.00 1.00 1.00 50
파이썬    
파이썬 정확도 1.00 150
파이썬 매크로 평균 1.00 1.00 1.00 150
파이썬 가중 평균 1.00 1.00 1.00 150
파이썬    
파이썬 모델은 C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\decision_tree_iris.onnx에 저장됩니다.
파이썬    
파이썬 ONNX의 입력 텐서에 대한 정보입니다:
파이썬 1. Name: float_input, Data Type: tensor(float), Shape: [None, 4]
파이썬    
Python ONNX의 출력 텐서에 대한 정보:
파이썬 1. Name: output_label, Data Type: tensor(int64), Shape: [None]
파이썬 2. Name: output_probability, Data Type: seq(map(int64,tensor(float))), Shape: []
파이썬    
Python 의사 결정 트리 분류기 모델의 정확도(ONNX 형식): 1.0

의사 결정 트리 분류기 모델(및 ONNX 버전)은 전체 피셔의 붓꽃 데이터 세트를 100% 정확하게 분류하는 것으로 나타났습니다.


2.12.2. 의사 결정 트리 분류기 모델 작업을 위한 MQL5 코드

//+------------------------------------------------------------------+
//|                                  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 모델의 정확도는 100%이며 이는 원래 모델의 정확도와 일치합니다.


2.12.3. 의사 결정 트리 분류기의 ONNX 표현

그림 26. Netron에서 의사 결정 트리 분류기의 ONNX 표현

그림 26. 네트론에서 의사 결정 트리 분류기의 ONNX 표현


LogisticRegression 및 LogisticRegressionCV에 대한 참고 사항:

로지스틱 회귀와 로지스틱 회귀 CV는 로지스틱 회귀를 사용한 이진 분류에 사용되는 두 가지 분류기이지만 모델 파라미터를 조정하는 방식이 서로 다릅니다:

    LogisticRegression:

  • 로지스틱 회귀는 로지스틱 함수를 사용하여 두 클래스 중 하나에 속할 확률을 모델링하는 분류기입니다(이진 분류).
  • C(역 정규화 강도), 페널티(정규화 유형, 예: L1 또는 L2), 솔버(최적화 알고리즘) 등 사용자 지정을 위한 기본 파라미터를 제공합니다.
  • 로지스틱 회귀를 사용할 때는 일반적으로 매개변수 값과 그 조합을 여러분 스스로 선택한 다음 데이터에 대해 모델을 학습시킵니다.

    LogisticRegressionCV:

  • 로지스틱회귀CV는 로지스틱회귀의 확장으로 교차 검증과 정규화 매개변수 C의 최적값 선택을 기본적으로 지원합니다.
  • C를 수동으로 선택하는 대신 교차 검증 메서드를 탐색하고 지정할 C 값의 목록을 LogisticRegressionCV에 전달할 수 있습니다(예: cv 매개 변수를 통해).
  • 로지스틱 회귀 CV는 교차 검증에서 가장 우수한 성능을 발휘하는 최적의 C 값을 자동으로 선택합니다.
  • 이 기능은 정규화를 자동으로 조정해야 할 때 특히 데이터가 많거나 어떤 C 값을 선택할지 잘 모르는 경우에 유용합니다.

따라서 이 둘의 주요 차이점은 매개변수 튜닝의 자동화 수준에 있습니다. 로지스틱회귀는 C를 수동으로 조정해야 하지만 로지스틱회귀CV는 교차 검증을 통해 최적의 C 값을 자동으로 선택할 수 있습니다. 이 중 어떤 것을 선택할지는 모델 튜닝 프로세스의 요구 사항과 자동화에 대한 필요에 따라 달라집니다.



2.13. 로지스틱 회귀 분류기

로지스틱 회귀 분류기는 이진 및 다중 클래스 분류 작업에 사용되는 머신 러닝 메서드입니다. '회귀'라는 이름과는 달리 로지스틱 회귀는 실제로 객체가 클래스 중 하나에 속할 확률을 예측합니다. 이러한 확률을 바탕으로 최종 분류 결정이 내려집니다.

로지스틱 회귀 분류기의 원리:

  1. 확률 예측: 로지스틱 회귀는 로지스틱(시그모이드) 함수를 사용하여 객체가 특정 클래스에 속할 확률을 모델링합니다.
  2. 의사 결정 경계: 예측된 확률을 기반으로 로지스틱 회귀를 통해 클래스를 구분하는 결정 경계를 결정합니다. 확률이 특정 임계값(일반적으로 0.5)을 초과하면 객체는 한 클래스로 분류되고 그렇지 않으면 다른 클래스로 분류됩니다.
  3. 매개변수 학습: 로지스틱 회귀 모델은 손실 함수를 최소화하기 위해 피처와 관련된 가중치(계수)를 조정하여 학습 데이터 세트에 대해 학습됩니다.

로지스틱 회귀 분류기의 장점:

  • 단순성 및 해석 가능성: 로지스틱 회귀는 피처가 클래스 예측에 미치는 영향에 대해 쉽게 해석할 수 있는 결과를 제공하는 간단한 모델입니다.
  • 대규모 데이터 세트에서의 효율성: 로지스틱 회귀는 대규모 데이터 세트를 효율적으로 처리하고 빠르게 학습할 수 있습니다.
  • 앙상블 메서드에서 사용: 로지스틱 회귀는 스태킹과 같은 앙상블 메서드에서 기본 분류기의 역할을 할 수 있습니다.

로지스틱 회귀 분류기의 한계:

  • 선형성: 로지스틱 회귀는 피처와 확률의 로그 간의 선형 관계를 가정하므로 복잡한 작업에는 부적절할 수 있습니다.
  • 멀티클래스 컨스트레인트: 로지스틱 회귀는 원래 이진 분류를 위해 설계되었지만 다중 클래스 분류로 확장하기 위해 One-vs-All(One-vs-Rest)과 같은 메서드들이 있습니다.
  • 이상값에 대한 민감도: 로지스틱 회귀는 데이터의 이상값에 민감할 수 있습니다.

로지스틱 회귀는 분류 작업 특히 모델의 해석 가능성이 중요하고 데이터가 선형 또는 선형에 가까운 구조를 보일 때 실무에서 널리 사용되는 고전적인 머신 러닝 메서드입니다. 또한 통계 및 의료 데이터 분석에 사용되어 사건 발생 가능성에 대한 요인의 영향을 평가합니다.


2.13.1. 로지스틱 회귀 분류기 모델 생성 코드

이 코드는 붓꽃 데이터 세트에서 로지스틱 회귀 분류기 모델을 학습하고 이를 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 로지스틱 회귀 분류기 모델의 정확도: 0.9733333333333334
파이썬    
파이썬 분류 보고서:
파이썬 정밀 리콜 f1-점수 지원
파이썬    
파이썬 0 1.00 1.00 1.00 50
파이썬 1 0.98 0.94 0.96 50
파이썬 2 0.94 0.98 0.96 50
파이썬    
파이썬 정확도 0.97 150
파이썬 매크로 평균 0.97 0.97 0.97 150
파이썬 가중 평균 0.97 0.97 0.97 150
파이썬    
파이썬 모델은 C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\logistic_regression_iris.onnx에 저장됩니다.
파이썬    
파이썬 ONNX의 입력 텐서에 대한 정보입니다:
파이썬 1. Name: float_input, Data Type: tensor(float), Shape: [None, 4]
파이썬    
Python ONNX의 출력 텐서에 대한 정보:
파이썬 1. Name: output_label, Data Type: tensor(int64), Shape: [None]
파이썬 2. Name: output_probability, Data Type: seq(map(int64,tensor(float))), Shape: []
파이썬    
Python ONNX 형식의 로지스틱 회귀 분류기 모델의 정확도: 0.9733333333333334


2.13.2. 회귀 분류기 모델 작업을 위한 MQL5 코드

//+------------------------------------------------------------------+
//|                            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 모델의 정확도는 97.33%로, 원래 모델의 정확도와 일치합니다.


2.13.3. 로지스틱 회귀 분류기의 ONNX 표현

그림 27. Netron에서 로지스틱 회귀 분류기의 ONNX 표현

그림 27. 네트론에서 로지스틱 회귀 분류기의 ONNX 표현


2.14. 로지스틱 회귀 CV 분류기

LogisticRegressionCV(교차 검증을 사용한 로지스틱 회귀)는 이진 분류를 위한 강력하고 유연한 메서드입니다. 이 메서드를 사용하면 로지스틱 회귀를 기반으로 분류 모델을 만들 수 있을 뿐만 아니라 최상의 성능을 달성하기 위해 매개변수를 자동으로 조정할 수 있습니다.

로지스틱회귀CV의 작동 원리:

  1. 로지스틱 회귀: LogisticRegressionCV는 기본적으로 로지스틱 회귀를 기반으로 합니다. 로지스틱 회귀는 객체가 두 클래스 중 하나에 속할 확률을 모델링하는 데 사용되는 통계 메서드입니다. 이 모델은 종속 변수가 이진(두 클래스)이거나 이진으로 변환할 수 있는 경우에 적용됩니다.
  2. 교차 유효성 검사: 로지스틱회귀CV의 주요 장점은 통합 교차 검증 기능입니다. 즉 정규화 매개변수 C에 대한 최적의 값을 수동으로 선택하는 대신 이 메서드는 자동으로 다양한 C 값을 시도하고 교차 검증에서 가장 성능이 좋은 값을 선택합니다.
  3. 최적의 C 선택 로지스틱회귀CV는 교차 검증 전략을 사용하여 다양한 C 값에서 모델의 성능을 평가합니다. C는 모델 정규화의 정도를 제어하는 정규화 매개변수입니다. C 값이 작을수록 정규화가 강하고 C 값이 클수록 정규화가 약하다는 것을 나타냅니다. 교차 검증을 통해 과소 적합과 과적합의 균형을 맞출 수 있는 최적의 C 값을 선택할 수 있습니다.
  4. 정규화: 로지스틱 회귀 CV는 L1(올가미) 및 L2(릿지) 정규화를 포함한 다양한 유형의 정규화도 지원합니다. 이러한 정규화 유형은 모델의 일반화를 개선하고 과적합을 방지하는 데 도움이 됩니다.

로지스틱회귀CV의 장점:

    자동 매개변수 튜닝: 로지스틱 회귀 CV의 주요 장점 중 하나는 교차 검증을 통해 최적의 C 값을 자동으로 선택할 수 있다는 점입니다. 따라서 수동으로 모델을 조정할 필요가 없으며 데이터와 수행중인 작업에 집중할 수 있습니다.
    과적합 견고성: 로지스틱회귀CV에서 지원하는 정규화는 모델의 복잡성을 제어하고 특히 제한된 데이터에서 과적합의 위험을 줄이는 데 도움이 됩니다.
    투명성: 로지스틱 회귀는 해석 가능한 메서드입니다. 예측에 대한 각 피처의 기여도를 분석할 수 있어 피처의 중요도를 파악하는 데 유용합니다.
    고성능: 로지스틱 회귀는 특히 대량의 데이터에서 빠르고 효율적으로 작동할 수 있습니다.

로지스틱회귀CV의 한계:

    선형 종속성: 로지스틱회귀CV는 선형 및 거의 선형에 가까운 분류 문제를 해결하는 데 적합합니다. 피처와 대상 변수 간의 관계가 매우 비선형적인 경우 모델이 제대로 작동하지 않을 수 있습니다.
    많은 수의 피처 처리: 피처 수가 많은 로지스틱 회귀에서는 과적합을 방지하기 위해 상당한 양의 데이터나 차원을 축소 하는 기술이 필요할 수 있습니다.
    데이터 표현 종속성: 로지스틱 회귀의 효과는 데이터를 표현하는 방법과 선택한 피처에 따라 달라질 수 있습니다.

LogisticRegressionCV는 자동 매개변수 조정 및 과적합 강건성을 갖춘 강력한 이진 분류 도구이며 해석 가능한 분류 모델을 빠르게 구축해야 할 때 특히 유용합니다. 그러나 데이터가 선형 또는 거의 선형에 가까운 종속성을 보이는 경우에 가장 잘 작동한다는 점을 기억해야 합니다.


2.14.1. LogisticRegressionCV 분류기 모델 생성 코드

이 코드는 붓꽃 데이터 세트에서 LogisticRegressionCV 분류기 모델을 학습하고 이를 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 로지스틱 회귀 CV 모델의 정확도: 0.98
파이썬    
파이썬 분류 보고서:
파이썬 정밀 리콜 f1-점수 지원
파이썬    
파이썬 0 1.00 1.00 1.00 50
파이썬 1 0.98 0.96 0.97 50
파이썬 2 0.96 0.98 0.97 50
파이썬    
파이썬 정확도 0.98 150
파이썬 매크로 평균 0.98 0.98 0.98 150
파이썬 가중 평균 0.98 0.98 0.98 150
파이썬    
파이썬 모델은 C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\logistic_regression_iris.onnx에 저장됩니다.
파이썬    
파이썬 ONNX의 입력 텐서에 대한 정보입니다:
파이썬 1. Name: float_input, Data Type: tensor(float), Shape: [None, 4]
파이썬    
Python ONNX의 출력 텐서에 대한 정보:
파이썬 1. Name: output_label, Data Type: tensor(int64), Shape: [None]
파이썬 2. Name: output_probability, Data Type: seq(map(int64,tensor(float))), Shape: []
파이썬    
Python ONNX 형식의 LogisticRegressionCV 모델의 정확도: 0.98


2.14.2. 로지스틱 회귀 CV 분류기 모델 작업을 위한 MQL5 코드

//+------------------------------------------------------------------+
//|                          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 모델의 정확도는 98%로 원래 모델의 정확도와 일치합니다.


2.14.3. 로지스틱 회귀 CV 분류기의 ONNX 표현

그림 28. 네트론에서 로지스틱회귀CV 분류기의 ONNX 표현

그림 28. 네트론에서 로지스틱회귀CV 분류기의 ONNX 표현



2.15. 패시브-어그레시브(PA) 분류기

수동-공격(PA) 분류기는 분류 작업에 사용되는 머신러닝 메서드입니다. 이 메서드의 핵심 아이디어는 분류 오류를 최소화하기 위해 훈련 중에 모델의 가중치(계수)를 조정하는 것입니다. 패시브-어그레시브 분류기는 온라인 학습 시나리오와 시간이 지남에 따라 데이터가 변경되는 상황에서 유용할 수 있습니다.

패시브-어그레시브 분류기의 작동 원리:

  1. 가중치 적응: 패시브-어그레시브 분류기는 스토캐스틱 경사 하강에서처럼 손실 함수를 최소화하는 방향으로 모델의 가중치를 업데이트하는 대신 현재 예제에 대해 분류 오류를 최소화하는 방향으로 가중치를 조정합니다.
  2. 어그레시브 유지: 이 메서드에는 모델의 가중치가 얼마나 강하게 적용될할지를 결정하는 공격성(C)이라는 매개변수가 있습니다. C 값이 클수록 메서드가 더 적극적으로 적응되도록 하고 값이 작을수록 덜 공격적으로 적응되도록 합니다.

패시브-어그레시브 분류기의 장점:

  • 온라인 학습에 적합합니다: 패시브-어그레시브 분류기는 새로운 데이터가 도착하면 업데이트될 수 있으므로 데이터가 스트림으로 도착하는 온라인 학습 작업에 적합합니다.
  • 데이터 변화에 대한 적응력: 이 메서드는 새로운 상황에 맞게 모델을 조정하기 때문에 변화하는 데이터에 대해 우수한 성능을 발휘합니다.

패시브-어그레시브 분류기의 한계:

  • 어그레시브한 매개변수 선택에 대한 민감도: 어그레시브 매개변수 C에 대한 최적의 값을 선택하려면 데이터 특성에 따라 조정이 필요할 수 있습니다.
  • 복잡한 작업에 항상 적합한 것은 아닙니다: 패시브-어그레시브 분류기는 복잡한 피처 종속성을 고려해야 하는 복잡한 작업에서는 높은 정확도를 제공하지 못할 수 있습니다.
  • 가중치 해석: 이 메서드를 사용하여 얻은 모델 가중치는 선형 또는 로지스틱 회귀를 사용하여 얻은 가중치에 비해 해석하기가 어려울 수 있습니다.

패시브-어그레시브 분류기는 데이터가 진화하는 분류 작업과 새로운 상황에 대해 모델의 빠른 적응이 중요한 작업에 적합한 머신러닝 메서드로 텍스트 데이터 분석, 이미지 분류 및 기타 작업을 포함한 다양한 영역에서 응용 프로그램을 찾습니다.


2.15.1. 패시브-어그레시브(PA) 분류기 모델 생성 코드

이 코드는 붓꽃 데이터 세트에서 패시브-어그레시브(PA) 분류기 모델을 학습하고 이를 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)

출력:

파이썬 패시브-어그레시브(PA) 분류기 모델의 정확도: 0.96
파이썬    
파이썬 분류 보고서:
파이썬 정밀 리콜 f1-점수 지원
파이썬    
파이썬 0 1.00 1.00 1.00 50
파이썬 1 0.96 0.92 0.94 50
파이썬 2 0.92 0.96 0.94 50
파이썬    
파이썬 정확도 0.96 150
파이썬 매크로 평균 0.96 0.96 0.96 150
파이썬 가중 평균 0.96 0.96 0.96 150
파이썬    
파이썬 모델은 C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\pa_classifier_iris.onnx에 저잡됩니다.
파이썬    
파이썬 ONNX의 입력 텐서에 대한 정보입니다:
파이썬 1. Name: float_input, Data Type: tensor(float), Shape: [None, 4]
파이썬    
Python ONNX의 출력 텐서에 대한 정보:
파이썬 1. Name: output_label, Data Type: tensor(int64), Shape: [None]
파이썬 2. Name: output_probability, Data Type: seq(map(int64,tensor(float))), Shape: []
파이썬    
Python 패시브-어그레시브(PA) 분류기 모델의 정확도(ONNX 형식): 0.96


2.15.2. 패시브-어그레시브(PA) 분류기 모델 작업을 위한 MQL5 코드

//+------------------------------------------------------------------+
//|                              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 모델의 정확도는 96%로 원래 모델의 정확도와 일치합니다.


2.15.3. 패시브-어그레시브(PA) 분류기의 ONNX 표현

그림 29. Netron에서 패시브-어그레시브(PA) 분류기의 ONNX 표현

그림 29. 네트론에서 패시브-어그레시브(PA) 분류기의 ONNX 표현

2.16. 퍼셉트론 분류기

퍼셉트론 분류기는 선형 분리 초평면을 기반으로 두 클래스를 분리하는 데 사용되는 선형 이진 분류기입니다. 가장 간단하고 오래된 머신러닝 메서드 중 하나이며 핵심 원리는 모델의 가중치(계수)를 훈련하여 훈련 데이터 세트의 분류 정확도를 극대화하는 것입니다.

퍼셉트론 분류기의 작동 원리:

  1. 선형 하이퍼플레인: 퍼셉트론은 피처 공간에서 두 클래스를 구분하는 선형 하이퍼플레인을 구성합니다. 이 초평면은 모델의 가중치(계수)에 의해 결정됩니다.
  2. 가중치 학습: 처음에는 가중치가 무작위로 또는 0으로 초기화됩니다. 그런 다음 학습 데이터 세트의 각 객체에 대해 모델은 현재 가중치를 기반으로 클래스를 예측하고 오류 발생 시 가중치를 조정합니다. 모든 객체가 올바르게 분류될 때까지 또는 최대 반복 횟수에 도달할 때까지 학습이 계속됩니다.

퍼셉트론 분류기의 장점:

  • 단순성: 퍼셉트론은 매우 간단한 알고리즘으로 이해하고 구현하기 쉽습니다.
  • 빠른 훈련 속도: 퍼셉트론은 특히 대규모 데이터 세트에 대해 빠르게 학습할 수 있으며 온라인 학습 작업에 사용할 수 있습니다.

퍼셉트론 분류기의 한계:

  • 선형 분리 가능성 제약 조건: 퍼셉트론은 데이터가 선형적으로 분리 가능한 경우에만 잘 작동합니다. 데이터를 선형적으로 분리할 수 없는 경우 퍼셉트론은 높은 정확도를 달성하지 못할 수 있습니다.
  • 초기 가중치에 대한 민감도: 초기 가중치 선택은 알고리즘의 수렴에 영향을 미칠 수 있습니다. 초기 가중치 선택이 잘못되면 수렴이 느려지거나 클래스를 올바르게 구분하지 못하는 뉴런이 발생할 수 있습니다.
  • 확률을 결정할 수 없습니다: 퍼셉트론은 특정 작업에서 중요할 수 있는 클래스 멤버십의 확률 추정치를 제공하지 않습니다.

퍼셉트론 분류기는 이진 분류를 위한 기본 알고리즘으로 데이터가 선형적으로 분리 가능한 간단한 경우에 유용할 수 있습니다. 또한 다층 신경망과 같은 더 복잡한 메서드의 기반이 될 수도 있습니다. 데이터 구조가 복잡한 작업에서는 로지스틱 회귀나 서포트 벡터 머신(SVM)과 같은 다른 메서드이 더 높은 분류 정확도를 제공할 수 있다는 점을 기억하는 것이 중요합니다.


2.16.1. 퍼셉트론 분류기 모델 생성 코드

이 코드는 붓꽃 데이터 세트에 대해 퍼셉트론 분류기 모델을 훈련하고 이를 ONNX 형식으로 내보내고 ONNX 모델을 사용하여 분류를 수행하는 과정을 보여줍니다. 또한 원래 모델과 ONNX 모델 모두의 정확도를 평가합니다.

# 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 퍼셉트론 분류기 모델의 정확도: 0.6133333333333333
파이썬    
파이썬 분류 보고서:
파이썬 정밀 리콜 f1-점수 지원
파이썬    
파이썬 0 1.00 0.80 0.89 50
파이썬 1 0.46 1.00 0.63 50
파이썬 2 1.00 0.04 0.08 50
파이썬    
파이썬 정확도 0.61 150
파이썬 매크로 평균 0.82 0.61 0.53 150
파이썬 가중 평균 0.82 0.61 0.53 150
파이썬    
파이썬 모델은 C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\perceptron_classifier_iris.onnx에 저장됩니다.
파이썬    
파이썬 ONNX의 입력 텐서에 대한 정보입니다:
파이썬 1. Name: float_input, Data Type: tensor(float), Shape: [None, 4]
파이썬    
Python ONNX의 출력 텐서에 대한 정보:
파이썬 1. Name: output_label, Data Type: tensor(int64), Shape: [None]
파이썬 2. Name: output_probability, Data Type: seq(map(int64,tensor(float))), Shape: []
파이썬    
Python 퍼셉트론 분류기 모델의 정확도(ONNX 형식): 0.6133333333333333


2.16.2. 퍼셉트론 분류기 모델 작업을 위한 MQL5 코드

//+------------------------------------------------------------------+
//|                                    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 모델의 정확도는 61.33%로 원래 모델의 정확도와 일치합니다.

2.16.3. 퍼셉트론 분류기의 ONNX 표현

그림 30. 네트론에서 퍼셉트론 분류기의 ONNX 표현

그림 30. 네트론에서 퍼셉트론 분류기의 ONNX 표현


2.17. 스토캐스틱 경사 하강 분류기

SGD 분류기(스토캐스틱 경사 하강 분류기)는 분류 작업에 사용되는 머신 러닝 메서드입니다. 이는 선형 모델의 특정한 사례이며 스토캐스틱 경사 하강을 사용하여 학습된 선형 분류기입니다.

SGD 분류기의 원리:

  1. 선형 하이퍼플레인: SGD 분류기는 다차원 피처 공간에서 두 클래스를 구분하는 선형 하이퍼플레인을 구성합니다. 이 초평면은 모델의 가중치(계수)에 의해 결정됩니다.
  2. 스토캐스틱 경사 하강: 이 메서드는 스토캐스틱 경사 하강을 사용하여 학습되므로 전체 데이터 세트가 아닌 학습 데이터 세트의 각 객체(또는 무작위로 선택된 하위 집합)에 대해 가중치 업데이트가 수행됩니다. 따라서 SGD 분류기는 대량의 데이터와 온라인 학습에 적합합니다.
  3. 손실 함수: SGD 분류기는 이진 분류를 위한 로지스틱 손실 함수 또는 다중 클래스 분류를 위한 소프트맥스 손실 함수와 같은 손실 함수를 최적화합니다.

SGD 분류기의 장점:

  • 훈련 속도: SGD 분류기는 스토캐스틱 기울기 하강 덕분에 특히 대량의 데이터에 대해 빠르게 학습합니다.
  • 온라인 학습에 적합합니다: 이 메서드는 데이터가 스트리밍 메서드로 도착하고 새로운 데이터가 들어올 때마다 모델을 업데이트해야 하는 온라인 학습 작업에 적합합니다.

SGD 분류기의 한계:

  • 매개변수에 대한 민감도: SGD 분류기에는 학습 속도 및 정규화 매개변수와 같은 많은 하이퍼파라미터가 있으며 이를 신중하게 조정해야 합니다.
  • 가중치 초기화: 초기 가중치 선택은 컨버전스 및 모델 품질에 영향을 미칠 수 있습니다.
  • 로컬 미니멈으로 수렴: SGD 메서드의 스토캐스틱 특성으로 인해 SGD 분류기는 손실 함수의 국부적 최소값으로 수렴할 수 있으며 이는 모델 품질에 영향을 미칠 수 있습니다.

SGD 분류기는 특히 빠른 처리가 필요한 대량의 데이터를 처리할 때 이진 및 다중 클래스 분류 작업에 사용할 수 있는 다목적 머신 러닝 메서드입니다. 높은 분류 정확도를 달성하려면 하이퍼파라미터를 적절히 조정하고 컨버전스를 모니터링하는 것이 중요합니다.


2.17.1. 스토캐스틱 경사 하강 분류기 모델 생성 코드

이 코드는 붓꽃 데이터 세트에 대해 SGD 분류기 모델을 학습하고 이를 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 SGD 분류기 모델의 정확도: 0.9333333333333333
파이썬   
파이썬 분류 보고서:
파이썬 정밀 리콜 f1-점수 지원
파이썬   
파이썬 0 0.96 1.00 0.98 50
파이썬 1 0.88 0.92 0.90 50
파이썬 2 0.96 0.88 0.92 50
파이썬   
파이썬 정확도 0.93 150
파이썬 매크로 평균 0.93 0.93 0.93 150
파이썬 가중 평균 0.93 0.93 0.93 150
파이썬   
파이썬 모델은 C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\perceptron_classifier_iris.onnx에 저장됩니다.
파이썬   
파이썬 ONNX의 입력 텐서에 대한 정보입니다:
Python 1. Name: float_input, Data Type: tensor(float), Shape: [None, 4]
파이썬   
Python 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 ONNX 형식의 SGD 분류기 모델의 정확도: 0.9333333333333333


2.17.2. 스토캐스틱 경사 하강 분류기 모델 작업을 위한 MQL5 코드

//+------------------------------------------------------------------+
//|                                           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 모델의 정확도는 93.33%로 원래 모델의 정확도와 일치합니다.


2.17.3. 스토캐스틱 경사 하강 분류기의 ONNX 표현

그림 31. 네트론에서 스토캐스틱 경사 하강 분류기의 ONNX 표현

그림 31. 네트론에서 확률 경사 하강 분류기의 ONNX 표현


2.18. 가우시안 나이브 베이즈(GNB) 분류기

가우시안 나이브 베이즈(GNB) 분류기는 분류 작업에 사용되는 베이지안 확률 모델을 기반으로 하는 머신 러닝 메서드로서 나이브 베이즈 분류기 제품군에 속하며 모든 피처들이 독립적이고 정규 분포를 갖는다고 가정합니다.

가우시안 나이브 베이즈 분류기의 원리:

  1. 베이지안 접근법: GNB는 베이즈 정리를 사용하여 각 클래스에 속하는 객체의 확률을 계산하는 베이지안 분류 방식을 기반으로 합니다.
  2. 순진한 가정: GNB의 핵심 가정은 모든 피처가 독립적이며 정규(가우스) 분포를 따른다는 것입니다. 실제 데이터에서는 피처가 서로 연관되어 있는 경우가 많기 때문에 이 가정은 순진한 것으로 간주됩니다.
  3. 매개변수 추정: GNB 모델은 각 클래스 내의 각 피처에 대한 분포의 매개변수(평균 및 표준편차)를 계산하여 학습 데이터 세트에 대해 학습합니다.

가우시안 나이브 베이즈 분류기의 장점:

  • 단순성 및 교육 속도: GNB는 매우 간단한 알고리즘으로 대규모 데이터 세트에서도 빠르게 학습합니다.
  • 중소규모 데이터에 대한 효율성: GNB는 피처 수가 적거나 중간 정도인 분류 작업에 효과적일 수 있으며 특히 정규 피처 분포의 가정이 성립하는 경우 더욱 그렇습니다.

가우시안 나이브 베이즈 분류기의 한계:

  • 순진한 가정: 피처 독립성과 정규 분포에 대한 가정은 실제 데이터에서는 지나치게 단순하고 부정확할 수 있어 분류 정확도가 떨어질 수 있습니다.
  • 이상값에 대한 민감도: GNB는 정규 분포의 매개변수를 크게 왜곡할 수 있으므로 데이터의 이상값에 민감할 수 있습니다.
  • 피처 종속성을 캡처할 수 없습니다: 독립성 가정으로 인해 GNB는 피처 간의 종속성을 고려하지 않습니다.

가우스 나이브 베이즈 분류기는 특히 정규 피처 분포의 가정이 거의 충족되는 경우 간단한 분류 작업에 적합한 선택입니다. 그러나 피처가 상호 연관되어 있거나 정규 분포를 따르지 않는 더 복잡한 작업에서는 SVM(서포트 벡터 머신) 또는 경사 부스팅과 같은 다른 메서드들이 더 정확한 결과를 제공할 수 있습니다.


2.18.1. 가우시안 나이브 베이즈(GNB) 분류기 모델 생성 코드

이 코드는 붓꽃 데이터 세트에 대해 가우시안 나이브 베이즈(GNB) 분류기 모델을 훈련하고 이를 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 가우시안 나이브 베이즈(GNB) 분류기 모델의 정확도: 0.96
파이썬    
파이썬 분류 보고서:
파이썬 정밀 리콜 f1-점수 지원
파이썬    
파이썬 0 1.00 1.00 1.00 50
파이썬 1 0.94 0.94 0.94 50
파이썬 2 0.94 0.94 0.94 50
파이썬    
파이썬 정확도 0.96 150
파이썬 매크로 평균 0.96 0.96 0.96 150
파이썬 가중 평균 0.96 0.96 0.96 150
파이썬    
파이썬 모델은 C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\gnb_classifier_iris.onnx에 저장됩니다.
파이썬    
파이썬 ONNX의 입력 텐서에 대한 정보입니다:
파이썬 1. Name: float_input, Data Type: tensor(float), Shape: [None, 4]
파이썬    
Python ONNX의 출력 텐서에 대한 정보:
파이썬 1. Name: output_label, Data Type: tensor(int64), Shape: [None]
파이썬 2. Name: output_probability, Data Type: seq(map(int64,tensor(float))), Shape: []
파이썬    
Python 가우시안 나이브 베이즈(GNB) 분류기 모델의 정확도(ONNX 형식): 0.96


2.18.2. 가우시안 나이브 베이즈(GNB) 분류기 모델 작업을 위한 MQL5 코드

//+------------------------------------------------------------------+
//|                            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 모델의 정확도는 96%로 원래 모델의 정확도와 일치합니다.


2.18.3. 가우시안 나이브 베이즈(GNB) 분류기의 ONNX 표현

그림 32. 네트론에서 가우시안 나이브 베이즈(GNB) 분류기의 ONNX 표현

그림 32. 네트론에서 가우시안 나이브 베이즈(GNB) 분류기의 ONNX 표현


2.19. 다항식 나이브 베이즈(MNB) 분류기

다항식 나이브 베이즈(MNB) 분류기는 베이지안 확률 모델을 기반으로 하는 머신 러닝 메서드으로 특히 텍스트 처리에서 분류 작업에 사용되며 나이브 베이즈 분류기의 변형 중 하나로 피처가 텍스트에 포함된 단어의 수와 같은 개수를 나타낸다고 가정합니다.

다항식 나이브 베이즈 분류기의 원리:

  1. 베이지안 접근법: MNB는 또한 베이지안 분류 방식을 따르며 베이지의 정리를 사용하여 각 클래스에 속할 확률을 계산합니다.
  2. 다항 분포 가정: MNB의 기본 가정은 피처가 텍스트에 포함된 단어의 수와 같은 개수를 나타내며 다항식 분포를 따른다는 것입니다. 이 가정은 텍스트 데이터에 유효한 경우가 많습니다.
  3. 매개변수 추정: MNB 모델은 각 클래스 내의 각 피처에 대한 분포의 파라미터를 계산하여 학습 데이터 세트에 대해 학습됩니다.

다항식 나이브 베이즈 분류기의 장점:

  • 텍스트 처리의 효율성: MNB는 피처 개수를 가정하기 때문에 텍스트 분류나 스팸 필터링과 같은 텍스트 데이터 분석과 관련된 작업에서 우수한 성능을 발휘합니다.
  • 단순성 및 교육 속도: 다른 나이브 베이즈 분류기와 마찬가지로 MNB는 대량의 텍스트 데이터에서도 빠르게 학습하는 간단한 알고리즘입니다.

다항식 나이브 베이즈 분류기의 한계:

  • 순진한 가정: 피처의 다항식 분포 가정은 특히 피처의 구조가 복잡한 경우 실제 데이터에서는 지나치게 단순하고 부정확할 수 있습니다.
  • 단어 순서를 고려할 수 없습니다: MNB는 특정 텍스트 분석 작업에서 중요할 수 있는 텍스트의 단어 순서를 고려하지 않습니다.
  • 희귀 단어에 대한 민감도: MNB는 희귀 단어에 민감할 수 있으며 발생 횟수가 충분하지 않으면 분류 정확도가 떨어질 수 있습니다.

다항식 나이브 베이즈 분류기는 텍스트 분석 작업에 유용한 메서드으로 특히 텍스트의 단어 수와 같은 개수와 관련된 피처이 있을 때 유용하며 텍스트 분류, 문서 분류 및 기타 텍스트 분석을 위한 자연어 처리(NLP)에 널리 사용됩니다.

2.19.1. 다항식 나이브 베이즈(MNB) 분류기 모델 생성 코드

이 코드는 붓꽃 데이터 세트에서 다항식 나이브 베이즈(MNB) 분류기 모델을 훈련하고 이를 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 다항식 나이브 베이즈(MNB) 분류기 모델의 정확도: 0.9533333333333334
파이썬    
파이썬 분류 보고서:
파이썬 정밀 리콜 f1-점수 지원
파이썬    
파이썬 0 1.00 1.00 1.00 50
파이썬 1 0.94 0.92 0.93 50
파이썬 2 0.92 0.94 0.93 50
파이썬    
파이썬 정확도 0.95 150
파이썬 매크로 평균 0.95 0.95 0.95 150
파이썬 가중 평균 0.95 0.95 0.95 150
파이썬    
파이썬 모델은 C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\mnb_classifier_iris.onnx에 저장됩니다.
파이썬    
파이썬 ONNX의 입력 텐서에 대한 정보입니다:
파이썬 1. Name: float_input, Data Type: tensor(float), Shape: [None, 4]
파이썬    
Python ONNX의 출력 텐서에 대한 정보:
파이썬 1. Name: output_label, Data Type: tensor(int64), Shape: [None]
파이썬 2. Name: output_probability, Data Type: seq(map(int64,tensor(float))), Shape: []
파이썬    
Python 다항식 나이브 베이즈(MNB) 분류기 모델의 정확도(ONNX 형식): 0.9533333333333334


2.19.2. 다항식 나이브 베이즈(MNB) 분류기 모델 작업을 위한 MQL5 코드

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

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

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

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

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

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

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

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

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

출력:

Iris_MultinomialNaiveBayesClassifier (EURUSD,H1)        model:MultinomialNaiveBayesClassifier  sample=69 FAILED [class=2, true class=1] features=(6.20,2.20,4.50,1.50]
Iris_MultinomialNaiveBayesClassifier (EURUSD,H1)        model:MultinomialNaiveBayesClassifier  sample=71 FAILED [class=2, true class=1] features=(5.90,3.20,4.80,1.80]
Iris_MultinomialNaiveBayesClassifier (EURUSD,H1)        model:MultinomialNaiveBayesClassifier  sample=73 FAILED [class=2, true class=1] features=(6.30,2.50,4.90,1.50]
Iris_MultinomialNaiveBayesClassifier (EURUSD,H1)        model:MultinomialNaiveBayesClassifier  sample=84 FAILED [class=2, true class=1] features=(6.00,2.70,5.10,1.60]
Iris_MultinomialNaiveBayesClassifier (EURUSD,H1)        model:MultinomialNaiveBayesClassifier  sample=130 FAILED [class=1, true class=2] features=(7.20,3.00,5.80,1.60]
Iris_MultinomialNaiveBayesClassifier (EURUSD,H1)        model:MultinomialNaiveBayesClassifier  sample=132 FAILED [class=1, true class=2] features=(7.90,3.80,6.40,2.00]
Iris_MultinomialNaiveBayesClassifier (EURUSD,H1)        model:MultinomialNaiveBayesClassifier  sample=134 FAILED [class=1, true class=2] features=(6.30,2.80,5.10,1.50]
Iris_MultinomialNaiveBayesClassifier (EURUSD,H1)        model:MultinomialNaiveBayesClassifier   correct results: 95.33%
Iris_MultinomialNaiveBayesClassifier (EURUSD,H1)        model=MultinomialNaiveBayesClassifier all samples accuracy=0.953333
Iris_MultinomialNaiveBayesClassifier (EURUSD,H1)        model:MultinomialNaiveBayesClassifier  FAILED [class=2, true class=1] features=(6.30,2.50,4.90,1.50)
Iris_MultinomialNaiveBayesClassifier (EURUSD,H1)        model=MultinomialNaiveBayesClassifier batch test accuracy=0.000000

전체 붓꽃 데이터 세트에서 내보낸 ONNX 모델의 정확도는 95.33%로, 원래 모델의 정확도와 일치합니다.


2.19.3. 다항식 나이브 베이즈(MNB) 분류기의 ONNX 표현

그림 33. 네트론에서 다항식 나이브 베이즈(MNB) 분류기의 ONNX 표현

그림 33. 네트론에서 다항식 나이브 베이즈(MNB) 분류기의 ONNX 표현하기


2.20. 보완 나이브 베이즈(CNB) 분류기

보완 나이브 베이즈(CNB) 분류기는 나이브 베이즈 분류기의 변형으로 한 클래스가 다른 클래스보다 훨씬 더 많이 사용되는 불균형 데이터에서 작동하도록 특별히 고안된 분류기입니다. 이 분류기는 고전적인 나이브 베이즈 메서드을 채택하여 클래스 불균형을 해결합니다.

보완 나이브 베이즈 분류기의 원리:

  1. 베이지안 접근법: 다른 베이지안 분류기와 마찬가지로 CNB는 베이지안 분류 방식을 따르며 베이지안 정리를 사용해 각 클래스에 속하는 객체의 확률을 계산합니다.
  2. 클래스 불균형 해결: CNB의 주요 목적은 클래스 불균형을 바로잡는 것입니다. 표준 나이브 베이즈 메서드처럼 클래스 내 피처의 확률을 고려하는 대신 CNB는 클래스 외부의 피처 확률을 고려하려고 시도합니다. 이 기능은 한 클래스가 다른 클래스보다 현저히 적은 비율을 차지할 때 특히 유용합니다.
  3. 매개변수 추정: CNB 모델은 클래스 외부의 각 피처에 대한 분포의 매개 변수를 계산하여 학습 데이터 세트에 대해 학습됩니다.

보완 나이브 베이즈 분류기의 장점:

  • 불균형 데이터에 대한 적합성: CNB는 클래스의 주파수가 서로 다른 불균형 데이터의 분류 작업에서 우수한 성능을 발휘합니다.
  • 단순성 및 교육 속도: 다른 나이브 베이즈 분류기와 마찬가지로 CNB는 대량의 데이터에서도 빠르게 학습하는 간단한 알고리즘입니다.

보완 나이브 베이즈 분류기의 한계:

  • 정규화 매개변수 선택에 대한 민감도: 다른 베이지안 메서드와 마찬가지로 정규화 매개변수에 적합한 값을 선택하려면 조정과 평가가 필요할 수 있습니다.
  • 순진한 가정: 다른 나이브 베이즈 분류기와 마찬가지로 CNB는 피처 독립성을 가정하는데 일부 작업에서는 이러한가정이 지나치게 단순할 수 있습니다.

보완 나이브 베이즈 분류기는 불균형 데이터가 있는 분류 작업 특히 한 클래스가 다른 클래스보다 현저히 적게 대표되는 경우에 적합하며 감정 분석이나 스팸 필터링과 같이 클래스 간에 단어의 불균형이 심한 텍스트 분류 작업에 특히 유용할 수 있습니다.

2.20.1. 보완 나이브 베이즈(CNB) 분류기 모델 생성 코드

이 코드는 붓꽃 데이터 세트에 대한 보완 나이브 베이즈(CNB) 분류기 모델을 훈련하고 이를 ONNX 형식으로 내보내고 ONNX 모델을 사용하여 분류를 수행하는 과정을 보여줍니다. 또한 원래 모델과 ONNX 모델 모두의 정확도를 평가합니다.

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

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

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

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

# create a Complement Naive Bayes (CNB) Classifier model
cnb_model = ComplementNB()

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

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

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

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

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

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

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

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

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

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

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

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

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

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

출력:

Python 보완 나이브 베이즈(CNB) 분류기 모델의 정확도: 0.6666666666666666
파이썬    
파이썬 분류 보고서:
파이썬 정밀 리콜 f1-점수 지원
파이썬    
파이썬 0 0.96 1.00 0.98 50
파이썬 1 0.00 0.00 0.00 50
파이썬 2 0.51 1.00 0.68 50
파이썬    
파이썬 정확도 0.67 150
파이썬 매크로 평균 0.49 0.67 0.55 150
파이썬 가중 평균 0.49 0.67 0.55 150
파이썬    
파이썬 모델은 C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\cnb_classifier_iris.onnx에 저장됩니다.
파이썬    
파이썬 ONNX의 입력 텐서에 대한 정보입니다:
파이썬 1. Name: float_input, Data Type: tensor(float), Shape: [None, 4]
파이썬    
Python ONNX의 출력 텐서에 대한 정보:
파이썬 1. Name: output_label, Data Type: tensor(int64), Shape: [None]
파이썬 2. Name: output_probability, Data Type: seq(map(int64,tensor(float))), Shape: []
파이썬    
Python ONNX 형식의 보완 나이브 베이즈(CNB) 분류기 모델의 정확도: 0.6666666666666666


2.20.2. 보완 나이브 베이즈(CNB) 분류기 모델 작업을 위한 MQL5 코드

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

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

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

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

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

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

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

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

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

출력:

Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=51 FAILED [class=2, true class=1] features=(7.00,3.20,4.70,1.40]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=52 FAILED [class=2, true class=1] features=(6.40,3.20,4.50,1.50]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=53 FAILED [class=2, true class=1] features=(6.90,3.10,4.90,1.50]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=54 FAILED [class=2, true class=1] features=(5.50,2.30,4.00,1.30]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=55 FAILED [class=2, true class=1] features=(6.50,2.80,4.60,1.50]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=56 FAILED [class=2, true class=1] features=(5.70,2.80,4.50,1.30]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=57 FAILED [class=2, true class=1] features=(6.30,3.30,4.70,1.60]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=58 FAILED [class=2, true class=1] features=(4.90,2.40,3.30,1.00]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=59 FAILED [class=2, true class=1] features=(6.60,2.90,4.60,1.30]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=60 FAILED [class=2, true class=1] features=(5.20,2.70,3.90,1.40]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=61 FAILED [class=2, true class=1] features=(5.00,2.00,3.50,1.00]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=62 FAILED [class=2, true class=1] features=(5.90,3.00,4.20,1.50]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=63 FAILED [class=2, true class=1] features=(6.00,2.20,4.00,1.00]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=64 FAILED [class=2, true class=1] features=(6.10,2.90,4.70,1.40]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=65 FAILED [class=2, true class=1] features=(5.60,2.90,3.60,1.30]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=66 FAILED [class=2, true class=1] features=(6.70,3.10,4.40,1.40]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=67 FAILED [class=2, true class=1] features=(5.60,3.00,4.50,1.50]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=68 FAILED [class=2, true class=1] features=(5.80,2.70,4.10,1.00]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=69 FAILED [class=2, true class=1] features=(6.20,2.20,4.50,1.50]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=70 FAILED [class=2, true class=1] features=(5.60,2.50,3.90,1.10]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=71 FAILED [class=2, true class=1] features=(5.90,3.20,4.80,1.80]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=72 FAILED [class=2, true class=1] features=(6.10,2.80,4.00,1.30]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=73 FAILED [class=2, true class=1] features=(6.30,2.50,4.90,1.50]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=74 FAILED [class=2, true class=1] features=(6.10,2.80,4.70,1.20]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=75 FAILED [class=2, true class=1] features=(6.40,2.90,4.30,1.30]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=76 FAILED [class=2, true class=1] features=(6.60,3.00,4.40,1.40]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=77 FAILED [class=2, true class=1] features=(6.80,2.80,4.80,1.40]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=78 FAILED [class=2, true class=1] features=(6.70,3.00,5.00,1.70]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=79 FAILED [class=2, true class=1] features=(6.00,2.90,4.50,1.50]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=80 FAILED [class=0, true class=1] features=(5.70,2.60,3.50,1.00]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=81 FAILED [class=2, true class=1] features=(5.50,2.40,3.80,1.10]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=82 FAILED [class=2, true class=1] features=(5.50,2.40,3.70,1.00]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=83 FAILED [class=2, true class=1] features=(5.80,2.70,3.90,1.20]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=84 FAILED [class=2, true class=1] features=(6.00,2.70,5.10,1.60]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=85 FAILED [class=2, true class=1] features=(5.40,3.00,4.50,1.50]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=86 FAILED [class=2, true class=1] features=(6.00,3.40,4.50,1.60]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=87 FAILED [class=2, true class=1] features=(6.70,3.10,4.70,1.50]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=88 FAILED [class=2, true class=1] features=(6.30,2.30,4.40,1.30]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=89 FAILED [class=2, true class=1] features=(5.60,3.00,4.10,1.30]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=90 FAILED [class=2, true class=1] features=(5.50,2.50,4.00,1.30]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=91 FAILED [class=2, true class=1] features=(5.50,2.60,4.40,1.20]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=92 FAILED [class=2, true class=1] features=(6.10,3.00,4.60,1.40]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=93 FAILED [class=2, true class=1] features=(5.80,2.60,4.00,1.20]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=94 FAILED [class=2, true class=1] features=(5.00,2.30,3.30,1.00]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=95 FAILED [class=2, true class=1] features=(5.60,2.70,4.20,1.30]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=96 FAILED [class=2, true class=1] features=(5.70,3.00,4.20,1.20]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=97 FAILED [class=2, true class=1] features=(5.70,2.90,4.20,1.30]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=98 FAILED [class=2, true class=1] features=(6.20,2.90,4.30,1.30]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=99 FAILED [class=0, true class=1] features=(5.10,2.50,3.00,1.10]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  sample=100 FAILED [class=2, true class=1] features=(5.70,2.80,4.10,1.30]
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier   correct results: 66.67%
Iris_CNBClassifier (EURUSD,H1)  model=CNBClassifier all samples accuracy=0.666667
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  FAILED [class=2, true class=1] features=(6.30,2.50,4.90,1.50)
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  FAILED [class=2, true class=1] features=(7.00,3.20,4.70,1.40)
Iris_CNBClassifier (EURUSD,H1)  model:CNBClassifier  FAILED [class=2, true class=1] features=(6.40,3.20,4.50,1.50)
Iris_CNBClassifier (EURUSD,H1)  model=CNBClassifier batch test accuracy=0.000000
전체 붓꽃 데이터 세트에서 내보낸 ONNX 모델의 정확도는 66.67%로 원래 모델의 정확도와 일치합니다.


2.20.3. 보완 나이브 베이즈(CNB) 분류기의 ONNX 표현

그림 34. 네트론에서 보완 나이브 베이즈(CNB) 분류기의 ONNX 표현

그림 34. 네트론에서 보완 나이브 베이즈(CNB) 분류기의 ONNX 표현


2.21. 베르누이 나이브 베이즈(BNB) 분류기

베르누이 나이브 베이즈(BNB) 분류기는 이진 분류 작업에 사용되는 나이브 베이즈 분류기의 또 다른 변형입니다. 이 분류기는 텍스트 분석과 같이 텍스트에 단어의 유무가 피처일 수 있는 이진 데이터로 피처가 표현되는 상황에서 특히 유용합니다.

베르누이 나이브 베이즈 분류기의 원리:

  1. 베이지안 접근법: 다른 베이지안 분류기와 마찬가지로 BNB는 베이지안 분류 방식을 따르며 베이지안 정리를 사용해 각 클래스에 속하는 객체의 확률을 계산합니다.
  2. 이진 피처의 가정: BNB의 기본 가정은 피처가 이진 데이터로 표현된다는 것으로 1과 0, 즉 1은 피처의 존재를 나타내고 0은 부재를 나타내는 두 가지 값만 가질 수 있다는 것입니다.
  3. 매개변수 추정: BNB 모델은 각 클래스의 각 피처에 대한 분포의 매개 변수를 계산하여 학습 데이터 세트에 대해 학습됩니다.

베르누이 나이브 베이즈 분류기의 장점:

  • 이진 데이터에 대한 효율성: BNB는 피처가 이진 데이터로 표현되는 작업에서 잘 작동하며 특히 텍스트 분석이나 이벤트 분류에 유용할 수 있습니다.
  • 단순성 및 교육 속도: 다른 나이브 베이즈 분류기와 마찬가지로 BNB는 빠르게 학습하는 간단한 알고리즘입니다.

베르누이 나이브 베이즈 분류기의 한계:

  • 바이너리 피처에 대한 제한: BNB는 피처가 바이너리가 아닌 작업에는 적합하지 않습니다. 피처에 두 개 이상의 값이 있는 경우 BNB는 해당 정보를 고려하지 않습니다.
  • 순진한 가정: 다른 나이브 베이즈 분류기와 마찬가지로 BNB는 피처 독립성을 가정하는데 일부 작업에서는 지나치게 단순할 수 있습니다.

베르누이 나이브 베이즈 분류기는 텍스트의 감정 분석이나 스팸 분류와 같이 이진 피처이 있는 이진 분류 작업에 적합하며 사용하기 쉽고 이러한 유형의 데이터에서 우수한 성능을 발휘합니다.


2.21.1. 베르누이 나이브 베이즈(BNB) 분류기 모델을 생성하는 코드

이 코드는 붓꽃 데이터 세트에 대해 베르누이 나이브 베이즈(BNB) 분류기 모델을 훈련하고 이를 ONNX 형식으로 내보내고 ONNX 모델을 사용하여 분류를 수행하는 과정을 보여줍니다. 또한 원래 모델과 ONNX 모델 모두의 정확도를 평가합니다.

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

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

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

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

# create a Bernoulli Naive Bayes (BNB) Classifier model
bnb_model = BernoulliNB()

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

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

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

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

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

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

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

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

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

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

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

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

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

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

출력:

Python 베르누이 나이브 베이즈(BNB) 분류기 모델의 정확도: 0.3333333333333333
파이썬    
파이썬 분류 보고서:
파이썬 정밀 리콜 f1-점수 지원
파이썬    
파이썬 0 0.33 1.00 0.50 50
파이썬 1 0.00 0.00 0.00 50
파이썬 2 0.00 0.00 0.00 50
파이썬    
파이썬 정확도 0.33 150
파이썬 매크로 평균 0.11 0.33 0.17 150
파이썬 가중 평균 0.11 0.33 0.17 150
파이썬    
파이썬 모델은 C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\bnb_classifier_iris.onnx에 저장됩니다.
파이썬    
파이썬 ONNX의 입력 텐서에 대한 정보입니다:
파이썬 1. Name: float_input, Data Type: tensor(float), Shape: [None, 4]
파이썬    
Python ONNX의 출력 텐서에 대한 정보:
파이썬 1. Name: output_label, Data Type: tensor(int64), Shape: [None]
파이썬 2. Name: output_probability, Data Type: seq(map(int64,tensor(float))), Shape: []
파이썬    
Python ONNX 형식의 베르누이 나이브 베이즈(BNB) 분류기 모델의 정확도: 0.3333333333333333


2.21.2. 베르누이 나이브 베이즈(BNB) 분류기 모델 작업을 위한 MQL5 코드

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

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

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

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

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

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

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

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

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

출력:

Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=51 FAILED [class=0, true class=1] features=(7.00,3.20,4.70,1.40]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=52 FAILED [class=0, true class=1] features=(6.40,3.20,4.50,1.50]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=53 FAILED [class=0, true class=1] features=(6.90,3.10,4.90,1.50]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=54 FAILED [class=0, true class=1] features=(5.50,2.30,4.00,1.30]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=55 FAILED [class=0, true class=1] features=(6.50,2.80,4.60,1.50]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=56 FAILED [class=0, true class=1] features=(5.70,2.80,4.50,1.30]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=57 FAILED [class=0, true class=1] features=(6.30,3.30,4.70,1.60]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=58 FAILED [class=0, true class=1] features=(4.90,2.40,3.30,1.00]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=59 FAILED [class=0, true class=1] features=(6.60,2.90,4.60,1.30]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=60 FAILED [class=0, true class=1] features=(5.20,2.70,3.90,1.40]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=61 FAILED [class=0, true class=1] features=(5.00,2.00,3.50,1.00]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=62 FAILED [class=0, true class=1] features=(5.90,3.00,4.20,1.50]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=63 FAILED [class=0, true class=1] features=(6.00,2.20,4.00,1.00]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=64 FAILED [class=0, true class=1] features=(6.10,2.90,4.70,1.40]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=65 FAILED [class=0, true class=1] features=(5.60,2.90,3.60,1.30]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=66 FAILED [class=0, true class=1] features=(6.70,3.10,4.40,1.40]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=67 FAILED [class=0, true class=1] features=(5.60,3.00,4.50,1.50]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=68 FAILED [class=0, true class=1] features=(5.80,2.70,4.10,1.00]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=69 FAILED [class=0, true class=1] features=(6.20,2.20,4.50,1.50]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=70 FAILED [class=0, true class=1] features=(5.60,2.50,3.90,1.10]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=71 FAILED [class=0, true class=1] features=(5.90,3.20,4.80,1.80]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=72 FAILED [class=0, true class=1] features=(6.10,2.80,4.00,1.30]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=73 FAILED [class=0, true class=1] features=(6.30,2.50,4.90,1.50]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=74 FAILED [class=0, true class=1] features=(6.10,2.80,4.70,1.20]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=75 FAILED [class=0, true class=1] features=(6.40,2.90,4.30,1.30]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=76 FAILED [class=0, true class=1] features=(6.60,3.00,4.40,1.40]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=77 FAILED [class=0, true class=1] features=(6.80,2.80,4.80,1.40]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=78 FAILED [class=0, true class=1] features=(6.70,3.00,5.00,1.70]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=79 FAILED [class=0, true class=1] features=(6.00,2.90,4.50,1.50]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=80 FAILED [class=0, true class=1] features=(5.70,2.60,3.50,1.00]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=81 FAILED [class=0, true class=1] features=(5.50,2.40,3.80,1.10]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=82 FAILED [class=0, true class=1] features=(5.50,2.40,3.70,1.00]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=83 FAILED [class=0, true class=1] features=(5.80,2.70,3.90,1.20]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=84 FAILED [class=0, true class=1] features=(6.00,2.70,5.10,1.60]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=85 FAILED [class=0, true class=1] features=(5.40,3.00,4.50,1.50]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=86 FAILED [class=0, true class=1] features=(6.00,3.40,4.50,1.60]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=87 FAILED [class=0, true class=1] features=(6.70,3.10,4.70,1.50]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=88 FAILED [class=0, true class=1] features=(6.30,2.30,4.40,1.30]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=89 FAILED [class=0, true class=1] features=(5.60,3.00,4.10,1.30]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=90 FAILED [class=0, true class=1] features=(5.50,2.50,4.00,1.30]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=91 FAILED [class=0, true class=1] features=(5.50,2.60,4.40,1.20]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=92 FAILED [class=0, true class=1] features=(6.10,3.00,4.60,1.40]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=93 FAILED [class=0, true class=1] features=(5.80,2.60,4.00,1.20]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=94 FAILED [class=0, true class=1] features=(5.00,2.30,3.30,1.00]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=95 FAILED [class=0, true class=1] features=(5.60,2.70,4.20,1.30]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=96 FAILED [class=0, true class=1] features=(5.70,3.00,4.20,1.20]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=97 FAILED [class=0, true class=1] features=(5.70,2.90,4.20,1.30]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=98 FAILED [class=0, true class=1] features=(6.20,2.90,4.30,1.30]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=99 FAILED [class=0, true class=1] features=(5.10,2.50,3.00,1.10]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=100 FAILED [class=0, true class=1] features=(5.70,2.80,4.10,1.30]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=101 FAILED [class=0, true class=2] features=(6.30,3.30,6.00,2.50]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=102 FAILED [class=0, true class=2] features=(5.80,2.70,5.10,1.90]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=103 FAILED [class=0, true class=2] features=(7.10,3.00,5.90,2.10]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=104 FAILED [class=0, true class=2] features=(6.30,2.90,5.60,1.80]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=105 FAILED [class=0, true class=2] features=(6.50,3.00,5.80,2.20]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=106 FAILED [class=0, true class=2] features=(7.60,3.00,6.60,2.10]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=107 FAILED [class=0, true class=2] features=(4.90,2.50,4.50,1.70]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=108 FAILED [class=0, true class=2] features=(7.30,2.90,6.30,1.80]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=109 FAILED [class=0, true class=2] features=(6.70,2.50,5.80,1.80]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=110 FAILED [class=0, true class=2] features=(7.20,3.60,6.10,2.50]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=111 FAILED [class=0, true class=2] features=(6.50,3.20,5.10,2.00]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=112 FAILED [class=0, true class=2] features=(6.40,2.70,5.30,1.90]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=113 FAILED [class=0, true class=2] features=(6.80,3.00,5.50,2.10]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=114 FAILED [class=0, true class=2] features=(5.70,2.50,5.00,2.00]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=115 FAILED [class=0, true class=2] features=(5.80,2.80,5.10,2.40]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=116 FAILED [class=0, true class=2] features=(6.40,3.20,5.30,2.30]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=117 FAILED [class=0, true class=2] features=(6.50,3.00,5.50,1.80]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=118 FAILED [class=0, true class=2] features=(7.70,3.80,6.70,2.20]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=119 FAILED [class=0, true class=2] features=(7.70,2.60,6.90,2.30]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=120 FAILED [class=0, true class=2] features=(6.00,2.20,5.00,1.50]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=121 FAILED [class=0, true class=2] features=(6.90,3.20,5.70,2.30]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=122 FAILED [class=0, true class=2] features=(5.60,2.80,4.90,2.00]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=123 FAILED [class=0, true class=2] features=(7.70,2.80,6.70,2.00]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=124 FAILED [class=0, true class=2] features=(6.30,2.70,4.90,1.80]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=125 FAILED [class=0, true class=2] features=(6.70,3.30,5.70,2.10]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=126 FAILED [class=0, true class=2] features=(7.20,3.20,6.00,1.80]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=127 FAILED [class=0, true class=2] features=(6.20,2.80,4.80,1.80]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=128 FAILED [class=0, true class=2] features=(6.10,3.00,4.90,1.80]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=129 FAILED [class=0, true class=2] features=(6.40,2.80,5.60,2.10]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=130 FAILED [class=0, true class=2] features=(7.20,3.00,5.80,1.60]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=131 FAILED [class=0, true class=2] features=(7.40,2.80,6.10,1.90]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=132 FAILED [class=0, true class=2] features=(7.90,3.80,6.40,2.00]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=133 FAILED [class=0, true class=2] features=(6.40,2.80,5.60,2.20]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=134 FAILED [class=0, true class=2] features=(6.30,2.80,5.10,1.50]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=135 FAILED [class=0, true class=2] features=(6.10,2.60,5.60,1.40]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=136 FAILED [class=0, true class=2] features=(7.70,3.00,6.10,2.30]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=137 FAILED [class=0, true class=2] features=(6.30,3.40,5.60,2.40]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=138 FAILED [class=0, true class=2] features=(6.40,3.10,5.50,1.80]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=139 FAILED [class=0, true class=2] features=(6.00,3.00,4.80,1.80]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=140 FAILED [class=0, true class=2] features=(6.90,3.10,5.40,2.10]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=141 FAILED [class=0, true class=2] features=(6.70,3.10,5.60,2.40]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=142 FAILED [class=0, true class=2] features=(6.90,3.10,5.10,2.30]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=143 FAILED [class=0, true class=2] features=(5.80,2.70,5.10,1.90]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=144 FAILED [class=0, true class=2] features=(6.80,3.20,5.90,2.30]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=145 FAILED [class=0, true class=2] features=(6.70,3.30,5.70,2.50]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=146 FAILED [class=0, true class=2] features=(6.70,3.00,5.20,2.30]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=147 FAILED [class=0, true class=2] features=(6.30,2.50,5.00,1.90]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=148 FAILED [class=0, true class=2] features=(6.50,3.00,5.20,2.00]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=149 FAILED [class=0, true class=2] features=(6.20,3.40,5.40,2.30]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  sample=150 FAILED [class=0, true class=2] features=(5.90,3.00,5.10,1.80]
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier   correct results: 33.33%
Iris_BNBClassifier (EURUSD,H1)  model=BNBClassifier all samples accuracy=0.333333
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  FAILED [class=0, true class=1] features=(6.30,2.50,4.90,1.50)
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  FAILED [class=0, true class=2] features=(6.30,2.70,4.90,1.80)
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  FAILED [class=0, true class=1] features=(7.00,3.20,4.70,1.40)
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  FAILED [class=0, true class=1] features=(6.40,3.20,4.50,1.50)
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  FAILED [class=0, true class=2] features=(6.30,3.30,6.00,2.50)
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  FAILED [class=0, true class=2] features=(5.80,2.70,5.10,1.90)
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  FAILED [class=0, true class=2] features=(7.10,3.00,5.90,2.10)
Iris_BNBClassifier (EURUSD,H1)  model:BNBClassifier  FAILED [class=0, true class=2] features=(6.30,2.90,5.60,1.80)
Iris_BNBClassifier (EURUSD,H1)  model=BNBClassifier batch test accuracy=0.000000

전체 붓꽃 데이터 세트에서 내보낸 ONNX 모델의 정확도는 33.33%로 원래 모델의 정확도와 일치합니다.


2.21.3. 베르누이 나이브 베이즈(BNB) 분류기의 ONNX 표현

그림 35. 네트론에서 베르누이 나이브 베이즈(BNB) 분류기의 ONNX 표현

그림 35. 네트론에서 베르누이 나이브 베이즈(BNB) 분류기의 ONNX 표현


2.22. 다층 퍼셉트론 분류기

다층 퍼셉트론(MLP) 분류기는 분류 작업에 사용되는 다층 신경망으로 입력 레이어, 숨겨진 레이어, 출력 레이어 등 여러 개의 뉴런 레이어로 구성됩니다. MLP 분류기는 데이터의 복잡한 비선형 종속성을 학습할 수 있습니다.

MLP 분류기의 원리:

  1. 다층 아키텍처: MLP 분류기는 입력 레이어, 하나 이상의 숨겨진 레이어, 출력 레이어를 포함하는 다층 레이어 아키텍처를 가지고 있습니다. 레이어의 각 뉴런은 학습된 가중치로 인접한 레이어의 뉴런과 연결됩니다.
  2. 활성화 함수: 각 뉴런 내부에는 활성화 함수가 적용되어 모델에 비선형성을 도입하고 MLP 분류기가 복잡한 데이터 종속성을 모델링할 수 있도록 합니다.
  3. 역전파를 사용한 교육: MLP 분류기는 모델의 예측과 실제 클래스 레이블 간의 오차를 최소화하는 역전파 메서드를 사용하여 학습됩니다.

MLP 분류기의 장점:

  • 복잡한 종속성을 모델링하는 기능: MLP 분류기는 데이터의 복잡한 비선형 종속성을 학습할 수 있으므로 단순한 선형 모델로는 불충분한 작업에서 우수한 성능을 발휘할 수 있습니다.
  • 다용도성: MLP 분류기는 다중 클래스 분류 및 다중 작업 문제를 포함한 광범위한 분류 작업에 사용할 수 있습니다.

MLP 분류기의 한계:

  • 하이퍼파라미터에 대한 민감도: MLP 분류기에는 숨겨진 계층의 수, 각 계층의 뉴런 수, 학습 속도 등과 같은 많은 하이퍼파라미터가 있습니다. 이러한 매개변수를 조정하는 데는 많은 시간과 리소스가 소요될 수 있습니다.
  • 대량의 데이터에 대한 요구 사항: MLP 분류기는 특히 모델에 많은 매개변수가 있는 경우 과적합을 방지하기 위해 상당한 양의 학습 데이터가 필요합니다.
  • 오버피팅: 모델에 매개변수가 너무 많거나 데이터가 충분하지 않으면 모델이 과도하게 적합하여 새로운 데이터에서 성능이 저하될 수 있습니다.

MLP 분류기는 특히 데이터에 복잡한 종속성이 있는 경우 분류 작업을 위한 강력한 도구이며 머신러닝 및 딥러닝 분야에서 다양한 분류 문제를 해결하기 위해 일반적으로 사용됩니다. 그러나 이 모델을 성공적으로 적용하려면 하이퍼파라미터를 적절히 조정하고 적절한 양의 학습 데이터를 확보하는 것이 필수적입니다.


2.22.1. 다층 퍼셉트론 분류기 모델 생성 코드

이 코드는 붓꽃 데이터 세트에 대해 다층 퍼셉트론 분류기를 훈련하고 이를 ONNX 형식으로 내보내고 ONNX 모델을 사용하여 분류를 수행하는 과정을 보여줍니다. 또한 원래 모델과 ONNX 모델 모두의 정확도를 평가합니다.

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

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

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

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

# create a Multilayer Perceptron (MLP) Classifier model
mlp_model = MLPClassifier(max_iter=1000, random_state=42)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

출력:

Python 다층 퍼셉트론(MLP) 분류기 모델의 정확도: 0.98
파이썬    
파이썬 분류 보고서:
파이썬 정밀 리콜 f1-점수 지원
파이썬    
파이썬 0 1.00 1.00 1.00 50
파이썬 1 1.00 0.94 0.97 50
파이썬 2 0.94 1.00 0.97 50
파이썬    
파이썬 정확도 0.98 150
파이썬 매크로 평균 0.98 0.98 0.98 150
파이썬 가중 평균 0.98 0.98 0.98 150
파이썬    
파이썬 모델은 C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\mlp_classifier_iris.onnx에 저장됩니다.
파이썬    
파이썬 ONNX의 입력 텐서에 대한 정보입니다:
파이썬 1. Name: float_input, Data Type: tensor(float), Shape: [None, 4]
파이썬    
Python ONNX의 출력 텐서에 대한 정보:
파이썬 1. Name: output_label, Data Type: tensor(int64), Shape: [None]
파이썬 2. Name: output_probability, Data Type: seq(map(int64,tensor(float))), Shape: []
파이썬    
Python 다층 퍼셉트론(MLP) 분류기 모델의 정확도(ONNX 형식): 0.98


2.22.2. 다층 퍼셉트론 분류기 모델 작업을 위한 MQL5 코드

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

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

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

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

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

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

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

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

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

출력:

Iris_MLPClassifier (EURUSD,H1)  model:MLPClassifier  sample=71 FAILED [class=2, true class=1] features=(5.90,3.20,4.80,1.80]
Iris_MLPClassifier (EURUSD,H1)  model:MLPClassifier  sample=73 FAILED [class=2, true class=1] features=(6.30,2.50,4.90,1.50]
Iris_MLPClassifier (EURUSD,H1)  model:MLPClassifier  sample=84 FAILED [class=2, true class=1] features=(6.00,2.70,5.10,1.60]
Iris_MLPClassifier (EURUSD,H1)  model:MLPClassifier   correct results: 98.00%
Iris_MLPClassifier (EURUSD,H1)  model=MLPClassifier all samples accuracy=0.980000
Iris_MLPClassifier (EURUSD,H1)  model:MLPClassifier  FAILED [class=2, true class=1] features=(6.30,2.50,4.90,1.50)
Iris_MLPClassifier (EURUSD,H1)  model=MLPClassifier batch test accuracy=0.000000

전체 붓꽃 데이터 세트에서 내보낸 ONNX 모델의 정확도는 98%로 원래 모델의 정확도와 일치합니다.


2.22.3. 다층 퍼셉트론 분류기의 ONNX 표현

그림 36. Netron에서 다층 퍼셉트론 분류기의 ONNX 표현

그림 36. 네트론에서 다층 퍼셉트론 분류기의 ONNX 표현


2.23. 선형 판별 분석(LDA) 분류기

선형 판별 분석(LDA) 분류기는 분류 작업에 사용되는 머신 러닝 메서드입니다. 이는 저차원 공간에서의 차원 축소 메서드 및 분류 계열에 속합니다. LDA는 클래스 간 분리를 극대화하기 위해 하이퍼플레인을 구성합니다.

LDA 분류기의 원리:

  1. 차원 감소: LDA의 핵심 아이디어는 차원 축소입니다. 데이터 클래스가 최대한 분리된 새로운 피처 공간을 찾는 것을 목표로 합니다.
  2. 분리 극대화: LDA는 서로 다른 클래스에 있는 피처의 평균값의 차이를 최대화하고 각 클래스 내의 분산을 최소화하는 하이퍼플레인(피처의 선형 조합)을 구성합니다.
  3. 훈련 매개변수: LDA 모델은 훈련 데이터 세트에 대해 훈련되어 하이퍼플레인의 매개변수와 새로운 피처 공간으로의 데이터 투영을 계산합니다.

LDA 분류기의 장점:

  • 클래스 분리의 개선: LDA는 특히 원래 피처 공간에서 클래스가 심하게 겹치는 경우 데이터의 클래스 분리를 크게 개선할 수 있습니다.
  • 차원 감소: LDA는 데이터 차원 축소에도 사용할 수 있어 시각화 및 계산 복잡성 감소에 유용하게 사용될 수 있습니다.

LDA 분류기의 한계:

  • 정상 가정: LDA는 피처가 정규 분포를 따르고 클래스가 동일한 공분산 행렬을 갖는다고 가정합니다. 이러한 가정이 충족되지 않으면 LDA는 정확도가 떨어지는 결과를 제공할 수 있습니다.
  • 이상값에 대한 민감도: LDA는 데이터의 이상값이 모델 매개변수 계산에 영향을 미칠 수 있으므로 데이터의 이상값에 민감할 수 있습니다.
  • 멀티클래스 분류의 과제: LDA는 원래 이진 분류를 위해 개발되었기 때문에 멀티클래스 작업으로 확장하려면 적응이 필요합니다.

LDA 분류기는 분류 및 차원 축소 작업에 유용한 메서드로 특히 클래스 분리를 개선해야 할 때 유용하며 통계, 생물학, 의학 분석 및 기타 분야에서 데이터 분석 및 분류를 위해 자주 사용됩니다.


2.23.1. 선형 판별 분석(LDA) 분류기 모델 생성 코드

이 코드는 붓꽃 데이터 세트에서 선형 판별 분석(LDA) 분류기를 훈련하고 이를 ONNX 형식으로 내보내고 ONNX 모델을 사용하여 분류를 수행하는 과정을 보여 줍니다. 또한 원래 모델과 ONNX 모델 모두의 정확도를 평가합니다.

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

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

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

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

# create a Linear Discriminant Analysis (LDA) Classifier model
lda_model = LinearDiscriminantAnalysis()

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

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

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

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

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

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

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

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

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

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

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

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

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

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

출력:

Python 선형 판별 분석(LDA) 분류기 모델의 정확도: 0.98
파이썬    
파이썬 분류 보고서:
파이썬 정밀 리콜 f1-점수 지원
파이썬    
파이썬 0 1.00 1.00 1.00 50
파이썬 1 0.98 0.96 0.97 50
파이썬 2 0.96 0.98 0.97 50
파이썬    
파이썬 정확도 0.98 150
파이썬 매크로 평균 0.98 0.98 0.98 150
파이썬 가중 평균 0.98 0.98 0.98 150
파이썬    
파이썬 모델은 C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\lda_classifier_iris.onnx에 저장됩니다.
파이썬    
파이썬 ONNX의 입력 텐서에 대한 정보입니다:
파이썬 1. Name: float_input, Data Type: tensor(float), Shape: [None, 4]
파이썬    
Python ONNX의 출력 텐서에 대한 정보:
파이썬 1. Name: output_label, Data Type: tensor(int64), Shape: [None]
파이썬 2. Name: output_probability, Data Type: seq(map(int64,tensor(float))), Shape: []
파이썬    
Python ONNX 형식의 선형 판별 분석(LDA) 분류기 모델의 정확도: 0.98


2.23.2. 선형 판별 분석(LDA) 분류기 모델 작업을 위한 MQL5 코드

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

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

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

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

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

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

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

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

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

출력:

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

전체 붓꽃 데이터 세트에서 내보낸 ONNX 모델의 정확도는 98%로 원래 모델의 정확도와 일치합니다.


2.23.3. 선형 판별 분석(LDA) 분류기의 ONNX 표현

그림 37. 네트론에서 선형 판별 분석(LDA) 분류기의 ONNX 표현

그림 37. 네트론의 선형 판별 분석(LDA) 분류기의 ONNX 표현


2.24. 히스트 경사 부스팅

Hist 경사 부스팅 분류기는 경사 부스팅 계열에 속하는 머신러닝 알고리즘으로 분류 작업을 위해 설계되었으며 데이터 분석과 머신 러닝에 널리 사용되는 효율적이고 강력한 메서드입니다.

히스트 경사 부스팅 분류기의 원리:

  1. 경사 부스팅: 히스트 경사 부스팅 분류기는 분류를 개선하기 위해 의사 결정 트리의 앙상블을 구축하는 경사 부스팅 메서드를 기반으로 하며 취약한 모델을 순차적으로 학습시키고 이전 모델의 오류를 수정하는 방식으로 이를 수행합니다.
  2. 히스토그램 사용량: 이름에 포함된 'Hist'는 이 알고리즘이 효율적인 데이터 처리를 위해 히스토그램을 사용한다는 의미입니다. 히스토그램 경사 부스팅은 피처를 일일이 열거하는 대신 피처의 히스토그램을 구성하여 신속한 의사 결정 트리를 구축할 수 있습니다.
  3. 잔여 훈련: 다른 경사 부스팅 메서드와 마찬가지로 히스트 경사 부스팅은 각각의 새로운 트리를 이전 모델의 잔여 데이터로 학습시켜 예측을 개선합니다.

히스트 경사 부스팅 분류기의 장점:

  • 높은 정확도: 히스트 경사 부스팅 분류기는 일반적으로 특히 많은 수의 트리를 사용할 때 높은 분류 정확도를 제공합니다.
  • 효율성: 히스토그램을 사용하면 알고리즘이 대규모 데이터 세트를 효율적으로 처리하고 앙상블을 빠르게 구축할 수 있습니다.
  • 이기종 데이터 처리 능력: 이 알고리즘은 범주형 및 숫자형 피처을 포함한 이질적인 데이터를 처리할 수 있습니다.

히스트 경사 부스팅 분류기의 한계:

  • 과적합에 대한 민감도: 매개변수를 적절하게 조정하지 않거나 많은 수의 트리를 사용하면 히스트 경사 부스팅 분류기가 과적합할 수 있습니다.
  • 매개변수 조정: 다른 경사 부스팅 알고리즘과 마찬가지로 히스트 경사 부스팅도 최적의 성능을 위해 세심한 매개변수의 튜닝이 필요합니다.

히스트 경사 부스팅 분류기는 분류 및 회귀 작업을 위한 강력한 알고리즘으로 데이터 처리의 정확성과 효율성을 높이며 데이터 분석, 생물 정보학, 금융 등 다양한 분야에서 활용되고 있습니다.


2.24.1. 히스토그램 기반 경사 부스팅 분류기 모델 생성 코드

이 코드는 붓꽃 데이터 세트에서 히스토그램 기반의 경사 부스팅 분류기를 훈련하고 이를 ONNX 형식으로 내보내고 ONNX 모델을 사용하여 분류를 수행하는 과정을 보여줍니다. 또한 원래 모델과 ONNX 모델 모두의 정확도를 평가합니다.

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

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

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

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

# create a Histogram-Based Gradient Boosting Classifier model
hist_gradient_boosting_model = HistGradientBoostingClassifier(random_state=42)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

출력:

Python 히스트 경사 부스팅 분류기 모델의 정확도: 1.0
파이썬    
파이썬 분류 보고서:
파이썬 정밀 리콜 f1-점수 지원
파이썬    
파이썬 0 1.00 1.00 1.00 50
파이썬 1 1.00 1.00 1.00 50
파이썬 2 1.00 1.00 1.00 50
파이썬    
파이썬 정확도 1.00 150
파이썬 매크로 평균 1.00 1.00 1.00 150
파이썬 가중 평균 1.00 1.00 1.00 150
파이썬    
파이썬 모델은 C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\hist_gradient_boosting_classifier_iris.onnx에 저장됩니다.
파이썬    
파이썬 ONNX의 입력 텐서에 대한 정보입니다:
파이썬 1. Name: float_input, Data Type: tensor(float), Shape: [None, 4]
파이썬    
Python ONNX의 출력 텐서에 대한 정보:
파이썬 1. Name: output_label, Data Type: tensor(int64), Shape: [None]
파이썬 2. Name: output_probability, Data Type: seq(map(int64,tensor(float))), Shape: []
파이썬    
Python 히스트 경사 부스팅 분류기 모델의 정확도(ONNX 형식): 1.0


2.24.2. 히스토그램 기반 경사 부스팅 분류기 모델 작업을 위한 MQL5 코드

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

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

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

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

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

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

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

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

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

출력:

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

전체 붓꽃 데이터 세트에서 내보낸 ONNX 모델의 정확도는 100%이며 이는 원래 모델의 정확도와 일치합니다.


2.24.3. 히스토그램 기반 경사 부스팅 분류기의 ONNX 표현

그림 38. 네트론에서 히스토그램 기반 경사 부스팅 분류기의 ONNX 표현

그림 38. 네트론에서 히스토그램 기반 경사 부스팅 분류기의 ONNX 표현


2.25. CategoricalNB 분류기

CategoricalNB는 베이즈 정리에 기반한 분류 알고리즘으로 범주형 피처를 가진 데이터 세트를 위해 특별히 설계되었으며 텍스트 분류, 스팸 탐지 및 불연속 데이터를 포함하는 기타 애플리케이션에 널리 사용됩니다.

CategoricalNB의 원리:

  1. 나이브 베이즈 분류기: CategoricalNB는 베이즈 정리를 기반으로 하는 나이브 베이즈 분류기의 한 유형입니다. 클래스가 주어진 각 피처의 조건부 확률을 사용하여 피처 집합에 대해 특정 클래스에 속할 확률을 계산합니다.
  2. 범주형 피처: 정규 분포의 연속적인 피처을 가정하는 가우시안 나이브 베이즈 분류기와 달리 CategoricalNB는 범주형 피처을 가진 데이터 세트에 적합합니다. 각 클래스에 대한 각 피처의 확률 분포를 모델링합니다.
  3. 독립성 가정: 나이브 베이즈 분류기의 '나이브'는 피처 독립성이라는 가정에서 비롯됩니다. CategoricalNB는 클래스가 주어지면 피처가 조건부로 독립적이라고 가정합니다. 실제로 이 가정이 항상 충족되는 것은 아니지만 나이브 베이즈 메서드는 많은 실제 현실의 데이터 세트에서 좋은 성능을 발휘할 수 있습니다.

CategoricalNB의 장점:

  • 효율성: CategoricalNB는 계산 효율이 높고 대규모 데이터 세트에 확장할 수 있습니다. 또한 최소한의 메모리를 필요로 하며 빠른 예측을 제공할 수 있습니다.
  • 해석 가능성: 확률적 특성으로 인해 CategoricalNB는 해석이 가능합니다. 어떤 피처가 예측에 영향을 미치는지에 대한 인사이트를 제공할 수 있습니다.
  • 범주형 데이터 처리하기: CategoricalNB는 범주형 피처가 있는 데이터 집합을 위해 특별히 설계되었습니다. 텍스트 데이터 및 기타 개별 피처 유형을 효율적으로 처리할 수 있습니다.
  • 기준 성능: 텍스트 분류 작업의 강력한 기준 모델 역할을 하는 경우가 많으며 소규모 데이터 세트에서 복잡한 알고리즘을 능가하는 성능을 발휘할 수 있습니다.

CategoricalNB의 한계:

  • 독립성 가정: 피처 독립성이라는 가정이 모든 데이터 세트에 적용되지 않을 수도 있습니다. 피처의 의존성이 높은 경우 CategoricalNB의 성능이 저하될 수 있습니다.
  • 피처 스케일링에 대한 민감도: 범주형 데이터로 작동하기 때문에 CategoricalNB는 피처 스케일링이 필요하지 않습니다. 그러나 경우에 따라 범주형 피처를 다른 방식으로 정규화하거나 인코딩하면 성능에 영향을 미칠 수 있습니다.
  • 제한된 표현력: CategoricalNB는 복잡한 데이터 종속성과 딥러닝 모델과 같은 더 복잡한 알고리즘을 캡처하지 못할 수 있습니다.
  • 누락된 데이터 처리하기: 데이터 집합에 누락된 값이 없으며 누락된 값은 전처리해야 한다고 가정합니다.

CategoricalNB는 유용한 분류 알고리즘으로 특히 범주형 피처를 가진 데이터 세트에 적합합니다. 단순성, 효율성, 해석 가능성 덕분에 다양한 분류 작업에 유용한 도구입니다. 독립성 가정과 같은 한계에도 불구하고 텍스트 분류 및 불연속형 데이터가 주를 이루는 기타 작업에 여전히 널리 사용되고 있습니다. 범주형 데이터로 작업할 때 기준 모델로 CategoricalNB를 고려하는 것이 합리적인 선택인 경우가 많습니다. 그러나 데이터에 피처 종속성이 있는 경우 특히 더 복잡한 모델과 비교하여 성능을 평가하는 것이 중요합니다.


2.25.1. CategoricalNB 분류기 모델 생성 코드

이 코드는 붓꽃 데이터 세트에 대해 CategoricalNB 분류기를 훈련하고 이를 ONNX 형식으로 내보내고 ONNX 모델을 사용하여 분류를 수행하는 과정을 보여줍니다. 또한 원래 모델과 ONNX 모델 모두의 정확도를 평가합니다.

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

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

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

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

# create a CategoricalNB model
categorical_nb_model = CategoricalNB()

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

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

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

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

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

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

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

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

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

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

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

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

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

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

출력:

Python CategoricalNB 모델의 정확도: 0.9333333333333333
파이썬    
파이썬 분류 보고서:
파이썬 정밀 리콜 f1-점수 지원
파이썬    
파이썬 0 1.00 1.00 1.00 50
파이썬 1 0.86 0.96 0.91 50
파이썬 2 0.95 0.84 0.89 50
파이썬    
파이썬 정확도 0.93 150
파이썬 매크로 평균 0.94 0.93 0.93 150
파이썬 가중 평균 0.94 0.93 0.93 150
파이썬    
파이썬 모델은 C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\categorical_nb_iris.onnx
파이썬    
파이썬 ONNX의 입력 텐서에 대한 정보입니다:
파이썬 1. Name: float_input, Data Type: tensor(float), Shape: [None, 4]
파이썬    
Python ONNX의 출력 텐서에 대한 정보:
파이썬 1. Name: output_label, Data Type: tensor(int64), Shape: [None]
파이썬 2. Name: output_probability, Data Type: seq(map(int64,tensor(float))), Shape: []
파이썬    
Python ONNX 형식의 CategoricalNB 모델의 정확도: 0.9333333333333333


2.25.2. CategoricalNB 분류기 모델 작업을 위한 MQL5 코드

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

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

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

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

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

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

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

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

//--- calculate accuracy
   model_accuracy=correct_results/total_results;
//---
   return(res);
  }
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
int OnStart(void)
  {
   string model_name="CategoricalNBClassifier";
//---
   long model=OnnxCreateFromBuffer(ExtModel,ONNX_DEFAULT);
   if(model==INVALID_HANDLE)
     {
      PrintFormat("model_name=%s OnnxCreate error %d for",model_name,GetLastError());
     }
   else
     {
      //--- test all dataset
      double model_accuracy=0;
      //-- test sample by sample execution for all Iris dataset
      if(TestAllIrisDataset(model,model_name,model_accuracy))
         PrintFormat("model=%s all samples accuracy=%f",model_name,model_accuracy);
      else
         PrintFormat("error in testing model=%s ",model_name);
      //--- test batch execution for several samples
      if(TestBatchExecution(model,model_name,model_accuracy))
         PrintFormat("model=%s batch test accuracy=%f",model_name,model_accuracy);
      else
         PrintFormat("error in testing model=%s ",model_name);
      //--- release model
      OnnxRelease(model);
     }
   return(0);
  }
//+------------------------------------------------------------------+
출력:
Iris_CategoricalNBClassifier (EURUSD,H1)        model:CategoricalNBClassifier  sample=78 FAILED [class=2, true class=1] features=(6.70,3.00,5.00,1.70]
Iris_CategoricalNBClassifier (EURUSD,H1)        model:CategoricalNBClassifier  sample=84 FAILED [class=2, true class=1] features=(6.00,2.70,5.10,1.60]
Iris_CategoricalNBClassifier (EURUSD,H1)        model:CategoricalNBClassifier  sample=102 FAILED [class=1, true class=2] features=(5.80,2.70,5.10,1.90]
Iris_CategoricalNBClassifier (EURUSD,H1)        model:CategoricalNBClassifier  sample=107 FAILED [class=1, true class=2] features=(4.90,2.50,4.50,1.70]
Iris_CategoricalNBClassifier (EURUSD,H1)        model:CategoricalNBClassifier  sample=122 FAILED [class=1, true class=2] features=(5.60,2.80,4.90,2.00]
Iris_CategoricalNBClassifier (EURUSD,H1)        model:CategoricalNBClassifier  sample=124 FAILED [class=1, true class=2] features=(6.30,2.70,4.90,1.80]
Iris_CategoricalNBClassifier (EURUSD,H1)        model:CategoricalNBClassifier  sample=127 FAILED [class=1, true class=2] features=(6.20,2.80,4.80,1.80]
Iris_CategoricalNBClassifier (EURUSD,H1)        model:CategoricalNBClassifier  sample=128 FAILED [class=1, true class=2] features=(6.10,3.00,4.90,1.80]
Iris_CategoricalNBClassifier (EURUSD,H1)        model:CategoricalNBClassifier  sample=139 FAILED [class=1, true class=2] features=(6.00,3.00,4.80,1.80]
Iris_CategoricalNBClassifier (EURUSD,H1)        model:CategoricalNBClassifier  sample=143 FAILED [class=1, true class=2] features=(5.80,2.70,5.10,1.90]
Iris_CategoricalNBClassifier (EURUSD,H1)        model:CategoricalNBClassifier   correct results: 93.33%
Iris_CategoricalNBClassifier (EURUSD,H1)        model=CategoricalNBClassifier all samples accuracy=0.933333
Iris_CategoricalNBClassifier (EURUSD,H1)        model:CategoricalNBClassifier  FAILED [class=1, true class=2] features=(6.30,2.70,4.90,1.80)
Iris_CategoricalNBClassifier (EURUSD,H1)        model:CategoricalNBClassifier  FAILED [class=1, true class=2] features=(5.80,2.70,5.10,1.90)
Iris_CategoricalNBClassifier (EURUSD,H1)        model=CategoricalNBClassifier batch test accuracy=0.000000

전체 붓꽃 데이터 세트에서 내보낸 ONNX 모델의 정확도는 93.33%로 원래 모델의 정확도와 일치합니다.


2.25.3. CategoricalNB 분류기의 ONNX 표현

그림 39. Netron에서 CategoricalNB 분류기의 ONNX 표현

그림 39. 네트론에서 CategoricalNB 분류기의 ONNX 표현

ExtraTreeClassifier 및 ExtraTreesClassifier 모델에 대한 참고 사항

이 두 분류기는 서로 다른 분류기이며 주요 차이점은 작동 방식에 있습니다:

ExtraTreeClassifier(극도로 무작위화된 트리 분류기):

  • 이 분류기는 극도로 무작위화된 트리 또는 추가 트리라고도 합니다.
  • 이는 무작위 의사 결정 트리의 개념을 기반으로 합니다.
  • ExtraTreeClassifier에서는 최적의 분할을 위한 사전 검색 없이 각 트리 노드에 대한 분할 선택이 무작위로 이루어집니다.
  • 따라서 분류기는 각 노드에 대한 최적의 분할을 계산할 필요가 없으므로 기존 랜덤 숲보다 계산 집약도가 낮습니다.
  • ExtraTreeClassifier는 종종 피처와 무작위 분할에 무작위 임계값을 사용하므로 더 많은 무작위 트리를 생성하며.
  • 최적의 분할에 대한 검색이 없기 때문에 ExtraTreeClassifier는 랜덤 숲에 비해 더 빠르지만 정확도는 떨어집니다.

ExtraTreesClassifier(극도로 무작위화된 트리 분류기):

  • ExtraTreesClassifier 또한 매우 무작위화된 트리 메서드에 기반한 분류기입니다.
  • ExtraTreeClassifier와 ExtraTreesClassifier의 주요 차이점은 ExtraTreesClassifier는 무작위 분할을 수행하여 각 트리 노드에서 최상의 분할을 선택한다는 점입니다.
  • 즉 ExtraTreesClassifier는 최적의 분할을 선택할 때 추가적인 수준의 무작위성을 가진 무작위 숲를 적용합니다.
  • ExtraTreesClassifier는 일반적으로 무작위 분할을 수행하여 분할에 가장 적합한 피처를 찾기 때문에 ExtraTreeClassifier보다 정확도가 높습니다.
  • 그러나 최적의 분할을 위해 더 광범위한 검색을 수행해야 하기 때문에 ExtraTreesClassifier는 계산 집약적일 수 있습니다.

요약하자면 이 두 분류기의 주요 차이점은 분할 선택의 무작위성의 수준에 있습니다. ExtraTreeClassifier는 최적의 분할을 찾지 않고 각 노드에 대해 무작위 선택을 하는 반면 ExtraTreesClassifier는 각 노드에서 최적의 분할을 찾으면서 무작위 분할을 수행합니다.


2.26. ExtraTreeClassifier

ExtraTreeClassifier는 분류 및 회귀 작업에 사용되는 강력한 머신러닝 알고리즘으로 매우 무작위화된 트리를 의미합니다. 이 알고리즘은 의사 결정 트리의 아이디어를 기반으로 하며 기존의 랜덤 숲와 의사 결정 트리에 비해 개선된 기능을 제공합니다.

ExtraTreeClassifier의 원리:
  1. 랜덤 노드 분할: ExtraTreeClassifier의 주요 원리는 각 트리 노드에 대해 무작위로 분할을 선택한다는 것입니다. 이는 분할에 가장 적합한 피처를 선택하는 기존의 의사 결정 트리와는 다릅니다. ExtraTreesClassifier는 최적의 분할을 고려하지 않고 분할을 수행하므로 더 무작위적이고 과적합에 강합니다.
  2. 결과 집계: 앙상블을 구성하는 동안 ExtraTreeClassifier는 여러 개의 무작위 트리를 생성하고 그 결과를 집계합니다. 이는 모델의 일반화를 개선하고 편차를 줄이기 위해서입니다. 트리의 앙상블은 과적합을 방지하고 예측 안정성을 높이는 데 도움이 됩니다.
  3. 임의 임계값: 노드를 분할할 때 ExtraTreeClassifier는 특정 최적값이 아닌 각 피처에 대해 무작위 임계값을 선택합니다. 이렇게 하면 더 많은 무작위성과 모델 안정성이 아루어집니다.
ExtraTreeClassifier의 장점:
  • 과적합에 대한 저항: 무작위 분할과 최적의 분할 선택이 없기 때문에 ExtraTreeClassifier는 일반적으로 일반 의사 결정 트리에 비해 과적합 경향이 적습니다.
  • 빠른 훈련 속도: ExtraTreeClassifier는 랜덤 숲와 같은 다른 알고리즘에 비해 훈련에 필요한 계산 리소스가 적습니다. 따라서 대규모 데이터 세트에 빠르고 효율적으로 사용할 수 있습니다.
  • 다용도성: ExtraTreeClassifier는 분류와 회귀 작업에 모두 사용할 수 있어 다양한 유형의 문제에 다용도로 활용할 수 있는 알고리즘입니다.
ExtraTreeClassifier의 한계:
  • 무작위성: 무작위 분할을 사용하면 경우에 따라 모델의 정확도가 떨어질 수 있습니다. 매개변수를 신중하게 조정하는 것이 중요합니다.
  • 이상값에 대한 민감도: ExtraTreeClassifier는 무작위 분할을 생성하기 때문에 데이터의 이상값에 민감할 수 있습니다. 이로 인해 경우에 따라 예측이 불안정해질 수 있습니다.
  • 낮은 해석 가능성: 일반 의사 결정 트리에 비해 ExtraTreeClassifier는 해석이 어렵고 설명하기 어렵습니다.
ExtraTreeClassifier는 과적합에 대한 저항성과 빠른 학습 속도를 결합한 강력한 머신러닝 알고리즘입니다. 특히 컴퓨팅 리소스가 제한되어 있는 경우 다양한 분류 및 회귀 작업에 유용하게 사용할 수 있습니다. 그러나 사용시에는 이 알고리즘의 무작위적 특성과 이상값에 대한 민감도 및 해석 가능성 저하 등의 한계를 고려하는 것이 중요합니다. ExtraTreeClassifier를 사용할 때는 신중한 매개변수 조정과 데이터 특성을 고려하는 것이 필수적입니다.


2.26.1. ExtraTreeClassifier 모델 생성 코드

이 코드는 붓꽃 데이터 세트에 대해 ExtraTreeClassifier 모델을 학습시키고 이를 ONNX 형식으로 내보내고 ONNX 모델을 사용하여 분류를 수행하는 과정을 보여줍니다. 또한 원래 모델과 ONNX 모델 모두의 정확도를 평가합니다.

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

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

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

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

# create an ExtraTreeClassifier model
extra_tree_model = ExtraTreeClassifier()

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

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

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

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

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

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

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

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

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

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

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

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

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

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

출력:

Python ExtraTreeClassifier 모델의 정확도: 1.0
파이썬    
파이썬 분류 보고서:
파이썬 정밀 리콜 f1-점수 지원
파이썬    
파이썬 0 1.00 1.00 1.00 50
파이썬 1 1.00 1.00 1.00 50
파이썬 2 1.00 1.00 1.00 50
파이썬    
파이썬 정확도 1.00 150
파이썬 매크로 평균 1.00 1.00 1.00 150
파이썬 가중 평균 1.00 1.00 1.00 150
파이썬    
파이썬 모델은 C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\extra_tree_iris.onnx
파이썬    
파이썬 ONNX의 입력 텐서에 대한 정보입니다:
파이썬 1. Name: float_input, Data Type: tensor(float), Shape: [None, 4]
파이썬    
Python ONNX의 출력 텐서에 대한 정보:
파이썬 1. Name: output_label, Data Type: tensor(int64), Shape: [None]
파이썬 2. Name: output_probability, Data Type: seq(map(int64,tensor(float))), Shape: []
파이썬    
파이썬 ONNX 형식dptj ExtraTreeClassifier 모델의 정확도: 1.0


2.26.2. ExtraTreeClassifier 모델 작업을 위한 MQL5 코드

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

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

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

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

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

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

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

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

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

출력:

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

전체 붓꽃 데이터 세트에서 내보낸 ONNX 모델의 정확도는 100%이며 이는 원래 모델의 정확도와 일치합니다.


2.26.3. ExtraTreeClassifier의 ONNX 표현

그림 40. 네트론에서 ExtraTreeClassifier의 ONNX 표현

그림 40. 네트론에서 ExtraTreeClassifier의 ONNX 표현


2.27. ExtraTreesClassifier

ExtraTreesClassifier는 분류 작업에 사용되는 강력한 머신러닝 알고리즘입니다. 이 알고리즘은 랜덤 숲보다 확장되고 개선된 알고리즘으로 몇 가지 장단점을 제공합니다.

ExtraTreesClassifier의 원리:
  1. 부트스트랩 샘플링: 랜덤 숲와 유사하게 ExtraTreesClassifier는 부트스트랩 메서드을 사용하여 훈련 데이터 세트에서 여러 개의 하위 샘플을 생성합니다. 즉, 각 트리에 대해 원본 데이터에서 교체가 포함된 무작위 하위 샘플이 생성됩니다.
  2. 무작위 분할: 각 트리 노드에 대해 분할에 가장 적합한 피처가 선택되는 랜덤 숲와 달리 ExtraTreesClassifier는 무작위 피처와 무작위 임계값을 사용하여 노드를 분할합니다. 이렇게 하면 트리가 더 무작위로 생성되고 과적합이 줄어듭니다.
  3. 투표: 트리 집합을 구성한 후 각 트리는 객체의 클래스에 투표합니다. 최종적으로 가장 많은 표를 얻은 클래스가 예측 클래스가 됩니다.
ExtraTreesClassifier의 장점:
  • 감소된 과적합: 무작위 분할과 무작위 피처를 사용하면 기존의 의사 결정 트리에 비해 과적합이 덜 발생하기 때문에 ExtraTreesClassifier가 더 잘 작동합니다.
  • 빠른 훈련 속도: ExtraTreesClassifier는 경사 부스팅과 같은 다른 알고리즘에 비해 훈련에 더 적은 계산 리소스를 필요로 합니다. 따라서 특히 대규모 데이터 세트의 경우 빠르고 효율적입니다.
  • 이상값 견고성: 나무와 무작위 분할의 앙상블 덕분에 ExtraTreesClassifier는 일반적으로 데이터의 이상값에 더 강력합니다.
ExtraTreesClassifier의 한계:
  • 복잡한 해석 가능성: 무작위 분할과 피처가 많기 때문에 ExtraTreesClassifier 모델을 분석하고 해석하는 것은 어려울 수 있습니다.
  • 매개변수 조정: 효율성에도 불구하고 최적의 성능을 달성하려면 ExtraTreesClassifier의 하이퍼파라미터를 세심하게 조정해야 할 수 있습니다.
  • 항상 최고의 성능을 발휘하는 것은 아닙니다: 일부 작업에서는 경사 부스팅과 같은 다른 알고리즘보다 ExtraTreesClassifier의 정확도가 떨어질 수 있습니다.
ExtraTreesClassifier는 과적합에 대한 저항성, 빠른 학습 속도, 이상값에 대한 견고성으로 잘 알려진 강력한 분류 알고리즘입니다. 특히 효율적인 솔루션이 필요한 대규모 데이터 세트를 다룰 때 데이터 분석 및 분류 작업에 유용한 도구가 될 수 있습니다. 하지만 알고리즘이 항상 최선의 선택은 아니며 그 효과는 특정 작업과 데이터에 따라 달라질 수 있다는 점에 유의해야 합니다.


2.27.1. ExtraTreesClassifier 모델 생성 코드

이 코드는 붓꽃 데이터 세트에 대해 ExtraTreesClassifier 모델을 학습하고 이를 ONNX 형식으로 내보내고 ONNX 모델을 사용하여 분류를 수행하는 과정을 보여줍니다. 또한 원래 모델과 ONNX 모델 모두의 정확도를 평가합니다.

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

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

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

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

# create an ExtraTreesClassifier model
extra_trees_model = ExtraTreesClassifier()

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

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

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

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

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

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

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

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

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

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

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

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

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

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

출력:

파이썬 ExtraTreesClassifier 모델의 정확도: 1.0
파이썬    
파이썬 분류 보고서:
파이썬 정밀 리콜 f1-점수 지원
파이썬    
파이썬 0 1.00 1.00 1.00 50
파이썬 1 1.00 1.00 1.00 50
파이썬 2 1.00 1.00 1.00 50
파이썬    
파이썬 정확도 1.00 150
파이썬 매크로 평균 1.00 1.00 1.00 150
파이썬 가중 평균 1.00 1.00 1.00 150
파이썬    
파이썬 모델은 C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\extra_trees_iris.onnx에 저장됩니다.
파이썬    
파이썬 ONNX의 입력 텐서에 대한 정보입니다:
파이썬 1. Name: float_input, Data Type: tensor(float), Shape: [None, 4]
파이썬    
Python ONNX의 출력 텐서에 대한 정보:
파이썬 1. Name: output_label, Data Type: tensor(int64), Shape: [None]
파이썬 2. Name: output_probability, Data Type: seq(map(int64,tensor(float))), Shape: []
파이썬    
파이썬 ONNX 형식의 ExtraTreesClassifier 모델의 정확도: 1.


2.27.2. ExtraTreesClassifier 모델 작업을 위한 MQL5 코드

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

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

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

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

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

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

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

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

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

출력:

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

전체 붓꽃 데이터 세트에서 내보낸 ONNX 모델의 정확도는 100%이며 이는 원래 모델의 정확도와 일치합니다.


2.27.3. ExtraTreesClassifier의 ONNX 표현

그림 41. 네트론에서 엑스트라트리 분류기의 ONNX 표현

그림 41. 네트론에서 ExtraTreesClassifier의 ONNX 표현


2.28. 모든 모델의 정확도 비교하기

이제 모든 모델의 성능을 비교해 보겠습니다. 먼저 파이썬을 사용하여 비교를 하고 저장된 ONNX 모델을 MetaTrader 5에서 로드하고 실행합니다.

2.28.1. 모든 모델을 계산하고 정확도 비교 차트를 작성하는 코드

이 스크립트는 전체 피셔 붓꽃 데이터 세트에서 Scikit-learn 패키지의 27개 분류 모델을 계산하고 모델을 ONNX 형식으로 내보낸 후 실행하고 원래 모델과 ONNX 모델의 정확도를 비교합니다.

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

# import necessary libraries
from sklearn import datasets
from sklearn.metrics import accuracy_score
from skl2onnx import convert_sklearn
from skl2onnx.common.data_types import FloatTensorType
import onnxruntime as ort
import numpy as np
import matplotlib.pyplot as plt
from sys import argv

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

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

# create and train each classifier model
from sklearn.svm import SVC
svc_model = SVC()
svc_model.fit(X, y)

from sklearn.ensemble import RandomForestClassifier
random_forest_model = RandomForestClassifier(random_state=42)
random_forest_model.fit(X, y)

from sklearn.ensemble import GradientBoostingClassifier
gradient_boosting_model = GradientBoostingClassifier(random_state=42)
gradient_boosting_model.fit(X, y)

from sklearn.ensemble import AdaBoostClassifier
adaboost_model = AdaBoostClassifier(random_state=42)
adaboost_model.fit(X, y)

from sklearn.ensemble import BaggingClassifier
bagging_model = BaggingClassifier(random_state=42)
bagging_model.fit(X, y)

from sklearn.neighbors import KNeighborsClassifier
knn_model = KNeighborsClassifier()
knn_model.fit(X, y)

from sklearn.neighbors import RadiusNeighborsClassifier
radius_neighbors_model = RadiusNeighborsClassifier(radius=1.0)
radius_neighbors_model.fit(X, y)

from sklearn.tree import DecisionTreeClassifier
decision_tree_model = DecisionTreeClassifier(random_state=42)
decision_tree_model.fit(X, y)

from sklearn.linear_model import LogisticRegression
logistic_regression_model = LogisticRegression(max_iter=1000, random_state=42)
logistic_regression_model.fit(X, y)

from sklearn.linear_model import RidgeClassifier
ridge_classifier_model = RidgeClassifier(random_state=42)
ridge_classifier_model.fit(X, y)

from sklearn.linear_model import PassiveAggressiveClassifier
passive_aggressive_model = PassiveAggressiveClassifier(max_iter=1000, random_state=42)
passive_aggressive_model.fit(X, y)

from sklearn.linear_model import Perceptron
perceptron_model = Perceptron(max_iter=1000, random_state=42)
perceptron_model.fit(X, y)

from sklearn.linear_model import SGDClassifier
sgd_model = SGDClassifier(max_iter=1000, random_state=42)
sgd_model.fit(X, y)

from sklearn.naive_bayes import GaussianNB
gaussian_nb_model = GaussianNB()
gaussian_nb_model.fit(X, y)

from sklearn.naive_bayes import MultinomialNB
multinomial_nb_model = MultinomialNB()
multinomial_nb_model.fit(X, y)

from sklearn.naive_bayes import ComplementNB
complement_nb_model = ComplementNB()
complement_nb_model.fit(X, y)

from sklearn.naive_bayes import BernoulliNB
bernoulli_nb_model = BernoulliNB()
bernoulli_nb_model.fit(X, y)

from sklearn.naive_bayes import CategoricalNB
categorical_nb_model = CategoricalNB()
categorical_nb_model.fit(X, y)

from sklearn.tree import ExtraTreeClassifier
extra_tree_model = ExtraTreeClassifier(random_state=42)
extra_tree_model.fit(X, y)

from sklearn.ensemble import ExtraTreesClassifier
extra_trees_model = ExtraTreesClassifier(random_state=42)
extra_trees_model.fit(X, y)

from sklearn.svm import LinearSVC  # Import LinearSVC
linear_svc_model = LinearSVC(random_state=42)
linear_svc_model.fit(X, y)

from sklearn.svm import NuSVC
nu_svc_model = NuSVC()
nu_svc_model.fit(X, y)

from sklearn.linear_model import LogisticRegressionCV
logistic_regression_cv_model = LogisticRegressionCV(cv=5, max_iter=1000, random_state=42)
logistic_regression_cv_model.fit(X, y)

from sklearn.neural_network import MLPClassifier
mlp_model = MLPClassifier(max_iter=1000, random_state=42)
mlp_model.fit(X, y)

from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
lda_model = LinearDiscriminantAnalysis()
lda_model.fit(X, y)

from sklearn.experimental import enable_hist_gradient_boosting
from sklearn.ensemble import HistGradientBoostingClassifier
hist_gradient_boosting_model = HistGradientBoostingClassifier(random_state=42)
hist_gradient_boosting_model.fit(X, y)

from sklearn.linear_model import RidgeClassifierCV
ridge_classifier_cv_model = RidgeClassifierCV()
ridge_classifier_cv_model.fit(X, y)

# define a dictionary to store results
results = {}

# loop through the models
for model_name, classifier_model in [
    ('SVC Classifier', svc_model),
    ('Random Forest Classifier', random_forest_model),
    ('Gradient Boosting Classifier', gradient_boosting_model),
    ('AdaBoost Classifier', adaboost_model),
    ('Bagging Classifier', bagging_model),
    ('K-NN Classifier', knn_model),
    ('Radius Neighbors Classifier', radius_neighbors_model),
    ('Decision Tree Classifier', decision_tree_model),
    ('Logistic Regression Classifier', logistic_regression_model),
    ('Ridge Classifier', ridge_classifier_model),
    ('Ridge ClassifierCV', ridge_classifier_cv_model),
    ('Passive-Aggressive Classifier', passive_aggressive_model),
    ('Perceptron Classifier', perceptron_model),
    ('SGD Classifier', sgd_model),
    ('Gaussian Naive Bayes Classifier', gaussian_nb_model),
    ('Multinomial Naive Bayes Classifier', multinomial_nb_model),
    ('Complement Naive Bayes Classifier', complement_nb_model),
    ('Bernoulli Naive Bayes Classifier', bernoulli_nb_model),
    ('Categorical Naive Bayes Classifier', categorical_nb_model),
    ('Extra Tree Classifier', extra_tree_model),
    ('Extra Trees Classifier', extra_trees_model),
    ('LinearSVC Classifier', linear_svc_model),
    ('NuSVC Classifier', nu_svc_model),
    ('Logistic RegressionCV Classifier', logistic_regression_cv_model),
    ('MLP Classifier', mlp_model),
    ('Linear Discriminant Analysis Classifier', lda_model),
    ('Hist Gradient Boosting Classifier', hist_gradient_boosting_model)
]:
    # predict classes for the entire dataset
    y_pred = classifier_model.predict(X)

    # evaluate the model's accuracy
    accuracy = accuracy_score(y, y_pred)

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

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

    # save the model to a file
    onnx_filename = data_path + f"{model_name.lower().replace(' ', '_')}_iris.onnx"
    with open(onnx_filename, "wb") as f:
        f.write(onnx_model.SerializeToString())

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

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

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

    # evaluate the accuracy of the ONNX model
    accuracy_onnx = accuracy_score(y, y_pred_onnx)

    # store results
    results[model_name] = {
        'accuracy': accuracy,
        'accuracy_onnx': accuracy_onnx
    }

    # print the accuracy of the original model and the ONNX model
    #print(f"{model_name} - Original Accuracy: {accuracy}, ONNX Accuracy: {accuracy_onnx}")

# sort the models based on accuracy
sorted_results = dict(sorted(results.items(), key=lambda item: item[1]['accuracy'], reverse=True))

# print the sorted results
print("Sorted Results:")
for model_name, metrics in sorted_results.items():
    print(f"{model_name} - Original Accuracy: {metrics['accuracy']:.4f}, ONNX Accuracy: {metrics['accuracy_onnx']:.4f}")

# create comparison plots for sorted results
fig, ax = plt.subplots(figsize=(12, 8))

model_names = list(sorted_results.keys())
accuracies = [sorted_results[model_name]['accuracy'] for model_name in model_names]
accuracies_onnx = [sorted_results[model_name]['accuracy_onnx'] for model_name in model_names]

bar_width = 0.35
index = range(len(model_names))

bar1 = plt.bar(index, accuracies, bar_width, label='Model Accuracy')
bar2 = plt.bar([i + bar_width for i in index], accuracies_onnx, bar_width, label='ONNX Accuracy')

plt.xlabel('Models')
plt.ylabel('Accuracy')
plt.title('Comparison of Model and ONNX Accuracy (Sorted)')
plt.xticks([i + bar_width / 2 for i in index], model_names, rotation=90, ha='center')
plt.legend()

plt.tight_layout()
plt.show()

출력:

Python  Sorted Results:
Python  Random Forest Classifier - Original Accuracy: 1.0000, ONNX Accuracy: 1.0000
Python  Gradient Boosting Classifier - Original Accuracy: 1.0000, ONNX Accuracy: 1.0000
Python  Bagging Classifier - Original Accuracy: 1.0000, ONNX Accuracy: 1.0000
Python  Decision Tree Classifier - Original Accuracy: 1.0000, ONNX Accuracy: 1.0000
Python  Extra Tree Classifier - Original Accuracy: 1.0000, ONNX Accuracy: 1.0000
Python  Extra Trees Classifier - Original Accuracy: 1.0000, ONNX Accuracy: 1.0000
Python  Hist Gradient Boosting Classifier - Original Accuracy: 1.0000, ONNX Accuracy: 1.0000
Python  Logistic RegressionCV Classifier - Original Accuracy: 0.9800, ONNX Accuracy: 0.9800
Python  MLP Classifier - Original Accuracy: 0.9800, ONNX Accuracy: 0.9800
Python  Linear Discriminant Analysis Classifier - Original Accuracy: 0.9800, ONNX Accuracy: 0.9800
Python  SVC Classifier - Original Accuracy: 0.9733, ONNX Accuracy: 0.9733
Python  Radius Neighbors Classifier - Original Accuracy: 0.9733, ONNX Accuracy: 0.9733
Python  Logistic Regression Classifier - Original Accuracy: 0.9733, ONNX Accuracy: 0.9733
Python  NuSVC Classifier - Original Accuracy: 0.9733, ONNX Accuracy: 0.9733
Python  K-NN Classifier - Original Accuracy: 0.9667, ONNX Accuracy: 0.9667
Python  LinearSVC Classifier - Original Accuracy: 0.9667, ONNX Accuracy: 0.9667
Python  AdaBoost Classifier - Original Accuracy: 0.9600, ONNX Accuracy: 0.9600
Python  Passive-Aggressive Classifier - Original Accuracy: 0.9600, ONNX Accuracy: 0.9600
Python  Gaussian Naive Bayes Classifier - Original Accuracy: 0.9600, ONNX Accuracy: 0.9600
Python  Multinomial Naive Bayes Classifier - Original Accuracy: 0.9533, ONNX Accuracy: 0.9533
Python  SGD Classifier - Original Accuracy: 0.9333, ONNX Accuracy: 0.9333
Python  Categorical Naive Bayes Classifier - Original Accuracy: 0.9333, ONNX Accuracy: 0.9333
Python  Ridge Classifier - Original Accuracy: 0.8533, ONNX Accuracy: 0.8533
Python  Ridge ClassifierCV - Original Accuracy: 0.8533, ONNX Accuracy: 0.8533
Python  Complement Naive Bayes Classifier - Original Accuracy: 0.6667, ONNX Accuracy: 0.6667
Python  Perceptron Classifier - Original Accuracy: 0.6133, ONNX Accuracy: 0.6133
Python  Bernoulli Naive Bayes Classifier - Original Accuracy: 0.3333, ONNX Accuracy: 0.3333
스크립트는 27개 모델 모두에 대한 요약 결과가 포함된 이미지도 생성합니다.

그림 42. 붓꽃 데이터 세트에서 27개 분류 모델과 해당 ONNX 버전에 대한 정확도 비교

그림 42. 붓꽃 데이터 세트에서 27개 분류 모델과 해당 ONNX 버전에 대한 정확도 비교



원래 모델과 해당 ONNX 버전의 정확도 평가 결과를 바탕으로 우리는 다음과 같은 결론을 도출할 수 있습니다:

7개 모델이 오리지널 버전과 ONNX 버전 모두에서 완벽한 정확도(1.0000)를 보였습니다. 이러한 모델에는 다음이 포함됩니다:

  1. 랜덤 숲 분류기
  2. 경사 부스팅 분류기
  3. 배깅 분류기
  4. 의사 결정 트리 분류기
  5. Extra Tree Classifier
  6. Extra Trees Classifier
  7. 히스트 경사 부스팅 분류기

이러한 모델의 ONNX 표현은 또한 높은 정확도를 유지합니다.

로지스틱 회귀 CV 분류기, MLP 분류기, 선형 판별 분석 분류기 등 세 가지 모델은 오리지널 버전과 ONNX 버전 모두에서 0.9800의 정확도로 높은 정확도를 달성했습니다. 이러한 모델은 두 가지 표현 모두에서 우수한 성능을 발휘합니다.

SVC 분류기, 반경 이웃 분류기, NuSVC 분류기, K-NN 분류기, 선형SVC 분류기, AdaBoost 분류기, 패시브-공격 분류기, 가우시안 나이브 베이즈 분류기, 다항식 나이브 베이즈 분류기 등 여러 모델이 오리지널 버전과 ONNX 버전 모두에서 정확도 점수 0.9733, 0.9667, 0.9600으로 우수한 정확도를 보여주었습니다. 이러한 모델은 ONNX 표현에서도 정확성을 유지합니다.

SGD 분류기, 범주형 나이브 베이즈 분류기, 릿지 분류기, 보완 나이브 베이즈 분류기, 퍼셉트론 분류기, 베르누이 나이브 베이즈 분류기와 같은 모델은 정확도는 낮지만 ONNX에서 정확도를 유지하는 데는 여전히 우수한 성능을 발휘합니다.

살펴본 모든 모델은 ONNX 형식으로 내보낼 때 정확성을 유지했으며 이는 ONNX가 머신 러닝 모델을 저장하고 복원하는 효율적인 방법을 제공한다는 것을 나타냅니다. 그러나 내보낸 모델의 품질은 특정 모델 알고리즘과 매개변수에 따라 달라질 수 있다는 점을 기억하세요.


2.28.2. 모든 ONNX 모델 실행을 위한 MQL5 코드

이 스크립트는 전체 피셔 붓꽃 데이터 세트에서 2.28.1의 스크립트로 저장된 모든 ONNX 모델을 실행합니다.

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

#include "iris.mqh"

//+------------------------------------------------------------------+
//| TestSampleSequenceMapOutput                                      |
//+------------------------------------------------------------------+
bool TestSampleSequenceMapOutput(long model,sIRISsample &iris_sample, int &model_class_id)
  {
//---
   model_class_id=-1;
   float input_data[1][4];
   for(int k=0; k<4; k++)
     {
      input_data[0][k]=(float)iris_sample.features[k];
     }
//---
   float out1[];
//---
   struct Map
     {
      ulong          key[];
      float          value[];
     } out2[];
//---
   bool res=ArrayResize(out1,input_data.Range(0))==input_data.Range(0);
//---
   if(res)
     {
      ulong input_shape[]= { input_data.Range(0), input_data.Range(1) };
      ulong output_shape[]= { input_data.Range(0) };
      //---
      OnnxSetInputShape(model,0,input_shape);
      OnnxSetOutputShape(model,0,output_shape);
      //---
      res=OnnxRun(model,0,input_data,out1,out2);
      //---
      if(res)
        {
         //--- postprocessing of sequence map data
         //--- find class with maximum probability
         ulong output_keys[];
         float output_values[];
         //---
         model_class_id=-1;
         int max_idx=-1;
         float max_value=-1;
         //---
         for(uint n=0; n<out2.Size(); n++)
           {
            //--- copy to arrays
            ArrayCopy(output_keys,out2[n].key);
            ArrayCopy(output_values,out2[n].value);
            //--- find the key with maximum probability
            for(int k=0; k<ArraySize(output_values); k++)
              {
               if(k==0)
                 {
                  max_idx=0;
                  max_value=output_values[max_idx];
                  model_class_id=(int)output_keys[max_idx];
                 }
               else
                 {
                  if(output_values[k]>max_value)
                    {
                     max_idx=k;
                     max_value=output_values[max_idx];
                     model_class_id=(int)output_keys[max_idx];
                    }
                 }
              }
           }
        }
     }
//---
   return(res);
  }

//+------------------------------------------------------------------+
//| TestSampleTensorOutput                                           |
//+------------------------------------------------------------------+
bool TestSampleTensorOutput(long model,sIRISsample &iris_sample, int &model_class_id)
  {
//---
   model_class_id=-1;
   float input_data[1][4];
   for(int k=0; k<4; k++)
     {
      input_data[0][k]=(float)iris_sample.features[k];
     }
//---
   ulong input_shape[]= { 1, 4};
   OnnxSetInputShape(model,0,input_shape);
//---
   int output1[1];
   float output2[1,3];
//---
   ulong output_shape[]= {1};
   OnnxSetOutputShape(model,0,output_shape);
//---
   ulong output_shape2[]= {1,3};
   OnnxSetOutputShape(model,1,output_shape2);
//---
   bool res=OnnxRun(model,0,input_data,output1,output2);
//--- class for these models in output1[0];
   if(res)
      model_class_id=output1[0];
//---
   return(res);
  }

//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
int OnStart(void)
  {
   sIRISsample iris_samples[];
//--- load dataset from file
   PrepareIrisDataset(iris_samples);
//--- test
   int total_samples=ArraySize(iris_samples);
   if(total_samples==0)
     {
      Print("error in loading iris dataset from iris.csv");
      return(false);
     }
   /*for(int k=0; k<total_samples; k++)
     {
      PrintFormat("%d (%.2f,%.2f,%.2f,%.2f) class %d (%s)",iris_samples[k].sample_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3],iris_samples[k].class_id,iris_samples[k].class_name);
     }*/
//----

   string iris_models[]=
     {
      "random_forest_classifier_iris.onnx",
      "gradient_boosting_classifier_iris.onnx",
      "bagging_classifier_iris.onnx",
      "decision_tree_classifier_iris.onnx",
      "extra_tree_classifier_iris.onnx",
      "extra_trees_classifier_iris.onnx",
      "hist_gradient_boosting_classifier_iris.onnx",
      "logistic_regressioncv_classifier_iris.onnx",
      "mlp_classifier_iris.onnx",
      "linear_discriminant_analysis_classifier_iris.onnx",
      "svc_classifier_iris.onnx",
      "radius_neighbors_classifier_iris.onnx",
      "logistic_regression_classifier_iris.onnx",
      "nusvc_classifier_iris.onnx",
      "k-nn_classifier_iris.onnx",
      "linearsvc_classifier_iris.onnx",
      "adaboost_classifier_iris.onnx",
      "passive-aggressive_classifier_iris.onnx",
      "gaussian_naive_bayes_classifier_iris.onnx",
      "multinomial_naive_bayes_classifier_iris.onnx",
      "sgd_classifier_iris.onnx",
      "categorical_naive_bayes_classifier_iris.onnx",
      "ridge_classifier_iris.onnx",
      "ridge_classifiercv_iris.onnx",
      "complement_naive_bayes_classifier_iris.onnx",
      "perceptron_classifier_iris.onnx",
      "bernoulli_naive_bayes_classifier_iris.onnx"
     };

//--- test all iris dataset sample by sample
   for(int i=0; i<ArraySize(iris_models); i++)
     {
      //--- load ONNX-model
      string model_name="IRIS_models\\"+iris_models[i];
      //---
      long model=OnnxCreate(model_name,0);
      if(model==INVALID_HANDLE)
        {
         PrintFormat("model_name=%s OnnxCreate error %d for",model_name,GetLastError());
        }
      else
        {
         //--- check all samples
         int correct_results=0;
         for(int k=0; k<total_samples; k++)
           {
            int model_class_id=-1;
            //--- select data output processor
            string current_model=iris_models[i];
            if(current_model=="svc_classifier_iris.onnx" || current_model=="linearsvc_classifier_iris.onnx" || current_model=="nusvc_classifier_iris.onnx" || current_model=="ridge_classifier_iris.onnx" || current_model=="ridge_classifiercv_iris.onnx" || current_model=="radius_neighbors_classifier_iris.onnx")
              {
               TestSampleTensorOutput(model,iris_samples[k],model_class_id);
              }
            else
              {
               TestSampleSequenceMapOutput(model,iris_samples[k],model_class_id);
              }
            //---
            if(model_class_id==iris_samples[k].class_id)
              {
               correct_results++;
               //PrintFormat("sample=%d OK [class=%d]",iris_samples[k].sample_id,model_class_id);
              }
            else
              {
               //PrintFormat("model:%s  sample=%d FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f]",model_name,iris_samples[k].sample_id,model_class_id,iris_samples[k].class_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3]);
              }
           }
         PrintFormat("%d model:%s   accuracy: %.4f",i+1,model_name,1.0*correct_results/total_samples);
         //--- release model
         OnnxRelease(model);
        }
      //---
     }
   return(0);
  }
//+------------------------------------------------------------------+

출력:

Iris_AllClassifiers (EURUSD,H1) 1 model:IRIS_models\random_forest_classifier_iris.onnx   accuracy: 1.0000
Iris_AllClassifiers (EURUSD,H1) 2 model:IRIS_models\gradient_boosting_classifier_iris.onnx   accuracy: 1.0000
Iris_AllClassifiers (EURUSD,H1) 3 model:IRIS_models\bagging_classifier_iris.onnx   accuracy: 1.0000
Iris_AllClassifiers (EURUSD,H1) 4 model:IRIS_models\decision_tree_classifier_iris.onnx   accuracy: 1.0000
Iris_AllClassifiers (EURUSD,H1) 5 model:IRIS_models\extra_tree_classifier_iris.onnx   accuracy: 1.0000
Iris_AllClassifiers (EURUSD,H1) 6 model:IRIS_models\extra_trees_classifier_iris.onnx   accuracy: 1.0000
Iris_AllClassifiers (EURUSD,H1) 7 model:IRIS_models\hist_gradient_boosting_classifier_iris.onnx   accuracy: 1.0000
Iris_AllClassifiers (EURUSD,H1) 8 model:IRIS_models\logistic_regressioncv_classifier_iris.onnx   accuracy: 0.9800
Iris_AllClassifiers (EURUSD,H1) 9 model:IRIS_models\mlp_classifier_iris.onnx   accuracy: 0.9800
Iris_AllClassifiers (EURUSD,H1) 10 model:IRIS_models\linear_discriminant_analysis_classifier_iris.onnx   accuracy: 0.9800
Iris_AllClassifiers (EURUSD,H1) 11 model:IRIS_models\svc_classifier_iris.onnx   accuracy: 0.9733
Iris_AllClassifiers (EURUSD,H1) 12 model:IRIS_models\radius_neighbors_classifier_iris.onnx   accuracy: 0.9733
Iris_AllClassifiers (EURUSD,H1) 13 model:IRIS_models\logistic_regression_classifier_iris.onnx   accuracy: 0.9733
Iris_AllClassifiers (EURUSD,H1) 14 model:IRIS_models\nusvc_classifier_iris.onnx   accuracy: 0.9733
Iris_AllClassifiers (EURUSD,H1) 15 model:IRIS_models\k-nn_classifier_iris.onnx   accuracy: 0.9667
Iris_AllClassifiers (EURUSD,H1) 16 model:IRIS_models\linearsvc_classifier_iris.onnx   accuracy: 0.9667
Iris_AllClassifiers (EURUSD,H1) 17 model:IRIS_models\adaboost_classifier_iris.onnx   accuracy: 0.9600
Iris_AllClassifiers (EURUSD,H1) 18 model:IRIS_models\passive-aggressive_classifier_iris.onnx   accuracy: 0.9600
Iris_AllClassifiers (EURUSD,H1) 19 model:IRIS_models\gaussian_naive_bayes_classifier_iris.onnx   accuracy: 0.9600
Iris_AllClassifiers (EURUSD,H1) 20 model:IRIS_models\multinomial_naive_bayes_classifier_iris.onnx   accuracy: 0.9533
Iris_AllClassifiers (EURUSD,H1) 21 model:IRIS_models\sgd_classifier_iris.onnx   accuracy: 0.9333
Iris_AllClassifiers (EURUSD,H1) 22 model:IRIS_models\categorical_naive_bayes_classifier_iris.onnx   accuracy: 0.9333
Iris_AllClassifiers (EURUSD,H1) 23 model:IRIS_models\ridge_classifier_iris.onnx   accuracy: 0.8533
Iris_AllClassifiers (EURUSD,H1) 24 model:IRIS_models\ridge_classifiercv_iris.onnx   accuracy: 0.8533
Iris_AllClassifiers (EURUSD,H1) ONNX: Removing initializer 'class_log_prior'. It is not used by any node and should be removed from the model.
Iris_AllClassifiers (EURUSD,H1) 25 model:IRIS_models\complement_naive_bayes_classifier_iris.onnx   accuracy: 0.6667
Iris_AllClassifiers (EURUSD,H1) 26 model:IRIS_models\perceptron_classifier_iris.onnx   accuracy: 0.6133
Iris_AllClassifiers (EURUSD,H1) 27 model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx   accuracy: 0.3333

스크립트 2.28.1.1의 결과와 비교:

Python  Random Forest Classifier - Original Accuracy: 1.0000, ONNX Accuracy: 1.0000
1 model:IRIS_models\random_forest_classifier_iris.onnx   accuracy: 1.0000

Python  Gradient Boosting Classifier - Original Accuracy: 1.0000, ONNX Accuracy: 1.0000
2 model:IRIS_models\gradient_boosting_classifier_iris.onnx   accuracy: 1.0000

Python  Bagging Classifier - Original Accuracy: 1.0000, ONNX Accuracy: 1.0000
3 model:IRIS_models\bagging_classifier_iris.onnx   accuracy: 1.0000

Python  Decision Tree Classifier - Original Accuracy: 1.0000, ONNX Accuracy: 1.0000
4 model:IRIS_models\decision_tree_classifier_iris.onnx   accuracy: 1.0000

Python  Extra Tree Classifier - Original Accuracy: 1.0000, ONNX Accuracy: 1.0000
5 model:IRIS_models\extra_tree_classifier_iris.onnx   accuracy: 1.0000

Python  Extra Trees Classifier - Original Accuracy: 1.0000, ONNX Accuracy: 1.0000
6 model:IRIS_models\extra_trees_classifier_iris.onnx   accuracy: 1.0000

Python  Hist Gradient Boosting Classifier - Original Accuracy: 1.0000, ONNX Accuracy: 1.0000
7 model:IRIS_models\hist_gradient_boosting_classifier_iris.onnx   accuracy: 1.0000

Python  Logistic RegressionCV Classifier - Original Accuracy: 0.9800, ONNX Accuracy: 0.9800
8 model:IRIS_models\logistic_regressioncv_classifier_iris.onnx   accuracy: 0.9800

Python  MLP Classifier - Original Accuracy: 0.9800, ONNX Accuracy: 0.9800
9 model:IRIS_models\mlp_classifier_iris.onnx   accuracy: 0.9800

Python  Linear Discriminant Analysis Classifier - Original Accuracy: 0.9800, ONNX Accuracy: 0.9800
10 model:IRIS_models\linear_discriminant_analysis_classifier_iris.onnx   accuracy: 0.9800

Python  SVC Classifier - Original Accuracy: 0.9733, ONNX Accuracy: 0.9733
11 model:IRIS_models\svc_classifier_iris.onnx   accuracy: 0.9733

Python  Radius Neighbors Classifier - Original Accuracy: 0.9733, ONNX Accuracy: 0.9733
12 model:IRIS_models\radius_neighbors_classifier_iris.onnx   accuracy: 0.9733

Python  Logistic Regression Classifier - Original Accuracy: 0.9733, ONNX Accuracy: 0.9733
13 model:IRIS_models\logistic_regression_classifier_iris.onnx   accuracy: 0.9733

Python  NuSVC Classifier - Original Accuracy: 0.9733, ONNX Accuracy: 0.9733
14 model:IRIS_models\nusvc_classifier_iris.onnx   accuracy: 0.9733

Python  K-NN Classifier - Original Accuracy: 0.9667, ONNX Accuracy: 0.9667
15 model:IRIS_models\k-nn_classifier_iris.onnx   accuracy: 0.9667

Python  LinearSVC Classifier - Original Accuracy: 0.9667, ONNX Accuracy: 0.9667
16 model:IRIS_models\linearsvc_classifier_iris.onnx   accuracy: 0.9667

Python  AdaBoost Classifier - Original Accuracy: 0.9600, ONNX Accuracy: 0.9600
17 model:IRIS_models\adaboost_classifier_iris.onnx   accuracy: 0.9600

Python  Passive-Aggressive Classifier - Original Accuracy: 0.9600, ONNX Accuracy: 0.9600
18 model:IRIS_models\passive-aggressive_classifier_iris.onnx   accuracy: 0.9600

Python  Gaussian Naive Bayes Classifier - Original Accuracy: 0.9600, ONNX Accuracy: 0.9600
19 model:IRIS_models\gaussian_naive_bayes_classifier_iris.onnx   accuracy: 0.9600

Python  Multinomial Naive Bayes Classifier - Original Accuracy: 0.9533, ONNX Accuracy: 0.9533
20 model:IRIS_models\multinomial_naive_bayes_classifier_iris.onnx   accuracy: 0.9533

Python  SGD Classifier - Original Accuracy: 0.9333, ONNX Accuracy: 0.9333
21 model:IRIS_models\sgd_classifier_iris.onnx   accuracy: 0.9333

Python  Categorical Naive Bayes Classifier - Original Accuracy: 0.9333, ONNX Accuracy: 0.9333
22 model:IRIS_models\categorical_naive_bayes_classifier_iris.onnx   accuracy: 0.9333

Python  Ridge Classifier - Original Accuracy: 0.8533, ONNX Accuracy: 0.8533
23 model:IRIS_models\ridge_classifier_iris.onnx   accuracy: 0.8533

Python  Ridge ClassifierCV - Original Accuracy: 0.8533, ONNX Accuracy: 0.8533
24 model:IRIS_models\ridge_classifiercv_iris.onnx   accuracy: 0.8533

Python  Complement Naive Bayes Classifier - Original Accuracy: 0.6667, ONNX Accuracy: 0.6667
25 model:IRIS_models\complement_naive_bayes_classifier_iris.onnx   accuracy: 0.6667

Python  Perceptron Classifier - Original Accuracy: 0.6133, ONNX Accuracy: 0.6133
26 model:IRIS_models\perceptron_classifier_iris.onnx   accuracy: 0.6133

Python  Bernoulli Naive Bayes Classifier - Original Accuracy: 0.3333, ONNX Accuracy: 0.3333
27 model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx   accuracy: 0.3333

MQL5에서 저장된 모든 ONNX 모델을 실행하면 2.28.1의 결과와 완전히 일치한다는 점에 주목할 필요가 있습니다.

따라서 우리가 조사한 모델들은 ONNX 형식으로 변환되어 분류 정확도를 유지했습니다.

붓꽃 데이터 세트에 대해 7개의 모델이 완벽한 분류 정확도(정확도=1.0)를 달성했다는 점을 주목할 필요가 있습니다:

  1. 랜덤 숲 분류기;
  2. 경사 부스팅 분류기;
  3. 배깅 분류기;
  4. 의사 결정 트리 분류기;
  5. Extra Tree Classifier;
  6. Extra Trees Classifier;
  7. 히스토그램 경사 부스팅 분류기.

나머지 20개 모델에서는 분류 오류가 발생했습니다.

208행의 주석을 제거하면 스크립트에는 각 모델에서 잘못 분류된 붓꽃 데이터 세트 샘플도 표시됩니다:

Iris_AllClassifiers (EURUSD,H1) 1 model:IRIS_models\random_forest_classifier_iris.onnx   accuracy: 1.0000
Iris_AllClassifiers (EURUSD,H1) 2 model:IRIS_models\gradient_boosting_classifier_iris.onnx   accuracy: 1.0000
Iris_AllClassifiers (EURUSD,H1) 3 model:IRIS_models\bagging_classifier_iris.onnx   accuracy: 1.0000
Iris_AllClassifiers (EURUSD,H1) 4 model:IRIS_models\decision_tree_classifier_iris.onnx   accuracy: 1.0000
Iris_AllClassifiers (EURUSD,H1) 5 model:IRIS_models\extra_tree_classifier_iris.onnx   accuracy: 1.0000
Iris_AllClassifiers (EURUSD,H1) 6 model:IRIS_models\extra_trees_classifier_iris.onnx   accuracy: 1.0000
Iris_AllClassifiers (EURUSD,H1) 7 model:IRIS_models\hist_gradient_boosting_classifier_iris.onnx   accuracy: 1.0000
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\logistic_regressioncv_classifier_iris.onnx  sample=71 FAILED [class=2, true class=1] features=(5.90,3.20,4.80,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\logistic_regressioncv_classifier_iris.onnx  sample=84 FAILED [class=2, true class=1] features=(6.00,2.70,5.10,1.60]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\logistic_regressioncv_classifier_iris.onnx  sample=134 FAILED [class=1, true class=2] features=(6.30,2.80,5.10,1.50]
Iris_AllClassifiers (EURUSD,H1) 8 model:IRIS_models\logistic_regressioncv_classifier_iris.onnx   accuracy: 0.9800
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\mlp_classifier_iris.onnx  sample=71 FAILED [class=2, true class=1] features=(5.90,3.20,4.80,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\mlp_classifier_iris.onnx  sample=73 FAILED [class=2, true class=1] features=(6.30,2.50,4.90,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\mlp_classifier_iris.onnx  sample=84 FAILED [class=2, true class=1] features=(6.00,2.70,5.10,1.60]
Iris_AllClassifiers (EURUSD,H1) 9 model:IRIS_models\mlp_classifier_iris.onnx   accuracy: 0.9800
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\linear_discriminant_analysis_classifier_iris.onnx  sample=71 FAILED [class=2, true class=1] features=(5.90,3.20,4.80,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\linear_discriminant_analysis_classifier_iris.onnx  sample=84 FAILED [class=2, true class=1] features=(6.00,2.70,5.10,1.60]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\linear_discriminant_analysis_classifier_iris.onnx  sample=134 FAILED [class=1, true class=2] features=(6.30,2.80,5.10,1.50]
Iris_AllClassifiers (EURUSD,H1) 10 model:IRIS_models\linear_discriminant_analysis_classifier_iris.onnx   accuracy: 0.9800
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\svc_classifier_iris.onnx  sample=78 FAILED [class=2, true class=1] features=(6.70,3.00,5.00,1.70]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\svc_classifier_iris.onnx  sample=84 FAILED [class=2, true class=1] features=(6.00,2.70,5.10,1.60]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\svc_classifier_iris.onnx  sample=107 FAILED [class=1, true class=2] features=(4.90,2.50,4.50,1.70]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\svc_classifier_iris.onnx  sample=139 FAILED [class=1, true class=2] features=(6.00,3.00,4.80,1.80]
Iris_AllClassifiers (EURUSD,H1) 11 model:IRIS_models\svc_classifier_iris.onnx   accuracy: 0.9733
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\radius_neighbors_classifier_iris.onnx  sample=78 FAILED [class=2, true class=1] features=(6.70,3.00,5.00,1.70]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\radius_neighbors_classifier_iris.onnx  sample=107 FAILED [class=1, true class=2] features=(4.90,2.50,4.50,1.70]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\radius_neighbors_classifier_iris.onnx  sample=127 FAILED [class=1, true class=2] features=(6.20,2.80,4.80,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\radius_neighbors_classifier_iris.onnx  sample=139 FAILED [class=1, true class=2] features=(6.00,3.00,4.80,1.80]
Iris_AllClassifiers (EURUSD,H1) 12 model:IRIS_models\radius_neighbors_classifier_iris.onnx   accuracy: 0.9733
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\logistic_regression_classifier_iris.onnx  sample=71 FAILED [class=2, true class=1] features=(5.90,3.20,4.80,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\logistic_regression_classifier_iris.onnx  sample=78 FAILED [class=2, true class=1] features=(6.70,3.00,5.00,1.70]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\logistic_regression_classifier_iris.onnx  sample=84 FAILED [class=2, true class=1] features=(6.00,2.70,5.10,1.60]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\logistic_regression_classifier_iris.onnx  sample=107 FAILED [class=1, true class=2] features=(4.90,2.50,4.50,1.70]
Iris_AllClassifiers (EURUSD,H1) 13 model:IRIS_models\logistic_regression_classifier_iris.onnx   accuracy: 0.9733
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\nusvc_classifier_iris.onnx  sample=78 FAILED [class=2, true class=1] features=(6.70,3.00,5.00,1.70]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\nusvc_classifier_iris.onnx  sample=84 FAILED [class=2, true class=1] features=(6.00,2.70,5.10,1.60]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\nusvc_classifier_iris.onnx  sample=107 FAILED [class=1, true class=2] features=(4.90,2.50,4.50,1.70]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\nusvc_classifier_iris.onnx  sample=139 FAILED [class=1, true class=2] features=(6.00,3.00,4.80,1.80]
Iris_AllClassifiers (EURUSD,H1) 14 model:IRIS_models\nusvc_classifier_iris.onnx   accuracy: 0.9733
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\k-nn_classifier_iris.onnx  sample=71 FAILED [class=2, true class=1] features=(5.90,3.20,4.80,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\k-nn_classifier_iris.onnx  sample=73 FAILED [class=2, true class=1] features=(6.30,2.50,4.90,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\k-nn_classifier_iris.onnx  sample=84 FAILED [class=2, true class=1] features=(6.00,2.70,5.10,1.60]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\k-nn_classifier_iris.onnx  sample=107 FAILED [class=1, true class=2] features=(4.90,2.50,4.50,1.70]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\k-nn_classifier_iris.onnx  sample=120 FAILED [class=1, true class=2] features=(6.00,2.20,5.00,1.50]
Iris_AllClassifiers (EURUSD,H1) 15 model:IRIS_models\k-nn_classifier_iris.onnx   accuracy: 0.9667
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\linearsvc_classifier_iris.onnx  sample=71 FAILED [class=2, true class=1] features=(5.90,3.20,4.80,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\linearsvc_classifier_iris.onnx  sample=84 FAILED [class=2, true class=1] features=(6.00,2.70,5.10,1.60]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\linearsvc_classifier_iris.onnx  sample=85 FAILED [class=2, true class=1] features=(5.40,3.00,4.50,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\linearsvc_classifier_iris.onnx  sample=130 FAILED [class=1, true class=2] features=(7.20,3.00,5.80,1.60]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\linearsvc_classifier_iris.onnx  sample=134 FAILED [class=1, true class=2] features=(6.30,2.80,5.10,1.50]
Iris_AllClassifiers (EURUSD,H1) 16 model:IRIS_models\linearsvc_classifier_iris.onnx   accuracy: 0.9667
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\adaboost_classifier_iris.onnx  sample=71 FAILED [class=2, true class=1] features=(5.90,3.20,4.80,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\adaboost_classifier_iris.onnx  sample=78 FAILED [class=2, true class=1] features=(6.70,3.00,5.00,1.70]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\adaboost_classifier_iris.onnx  sample=120 FAILED [class=1, true class=2] features=(6.00,2.20,5.00,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\adaboost_classifier_iris.onnx  sample=130 FAILED [class=1, true class=2] features=(7.20,3.00,5.80,1.60]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\adaboost_classifier_iris.onnx  sample=134 FAILED [class=1, true class=2] features=(6.30,2.80,5.10,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\adaboost_classifier_iris.onnx  sample=135 FAILED [class=1, true class=2] features=(6.10,2.60,5.60,1.40]
Iris_AllClassifiers (EURUSD,H1) 17 model:IRIS_models\adaboost_classifier_iris.onnx   accuracy: 0.9600
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\passive-aggressive_classifier_iris.onnx  sample=67 FAILED [class=2, true class=1] features=(5.60,3.00,4.50,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\passive-aggressive_classifier_iris.onnx  sample=71 FAILED [class=2, true class=1] features=(5.90,3.20,4.80,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\passive-aggressive_classifier_iris.onnx  sample=84 FAILED [class=2, true class=1] features=(6.00,2.70,5.10,1.60]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\passive-aggressive_classifier_iris.onnx  sample=85 FAILED [class=2, true class=1] features=(5.40,3.00,4.50,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\passive-aggressive_classifier_iris.onnx  sample=130 FAILED [class=1, true class=2] features=(7.20,3.00,5.80,1.60]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\passive-aggressive_classifier_iris.onnx  sample=134 FAILED [class=1, true class=2] features=(6.30,2.80,5.10,1.50]
Iris_AllClassifiers (EURUSD,H1) 18 model:IRIS_models\passive-aggressive_classifier_iris.onnx   accuracy: 0.9600
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\gaussian_naive_bayes_classifier_iris.onnx  sample=53 FAILED [class=2, true class=1] features=(6.90,3.10,4.90,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\gaussian_naive_bayes_classifier_iris.onnx  sample=71 FAILED [class=2, true class=1] features=(5.90,3.20,4.80,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\gaussian_naive_bayes_classifier_iris.onnx  sample=78 FAILED [class=2, true class=1] features=(6.70,3.00,5.00,1.70]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\gaussian_naive_bayes_classifier_iris.onnx  sample=107 FAILED [class=1, true class=2] features=(4.90,2.50,4.50,1.70]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\gaussian_naive_bayes_classifier_iris.onnx  sample=120 FAILED [class=1, true class=2] features=(6.00,2.20,5.00,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\gaussian_naive_bayes_classifier_iris.onnx  sample=134 FAILED [class=1, true class=2] features=(6.30,2.80,5.10,1.50]
Iris_AllClassifiers (EURUSD,H1) 19 model:IRIS_models\gaussian_naive_bayes_classifier_iris.onnx   accuracy: 0.9600
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\multinomial_naive_bayes_classifier_iris.onnx  sample=69 FAILED [class=2, true class=1] features=(6.20,2.20,4.50,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\multinomial_naive_bayes_classifier_iris.onnx  sample=71 FAILED [class=2, true class=1] features=(5.90,3.20,4.80,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\multinomial_naive_bayes_classifier_iris.onnx  sample=73 FAILED [class=2, true class=1] features=(6.30,2.50,4.90,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\multinomial_naive_bayes_classifier_iris.onnx  sample=84 FAILED [class=2, true class=1] features=(6.00,2.70,5.10,1.60]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\multinomial_naive_bayes_classifier_iris.onnx  sample=130 FAILED [class=1, true class=2] features=(7.20,3.00,5.80,1.60]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\multinomial_naive_bayes_classifier_iris.onnx  sample=132 FAILED [class=1, true class=2] features=(7.90,3.80,6.40,2.00]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\multinomial_naive_bayes_classifier_iris.onnx  sample=134 FAILED [class=1, true class=2] features=(6.30,2.80,5.10,1.50]
Iris_AllClassifiers (EURUSD,H1) 20 model:IRIS_models\multinomial_naive_bayes_classifier_iris.onnx   accuracy: 0.9533
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\sgd_classifier_iris.onnx  sample=65 FAILED [class=0, true class=1] features=(5.60,2.90,3.60,1.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\sgd_classifier_iris.onnx  sample=71 FAILED [class=2, true class=1] features=(5.90,3.20,4.80,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\sgd_classifier_iris.onnx  sample=84 FAILED [class=2, true class=1] features=(6.00,2.70,5.10,1.60]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\sgd_classifier_iris.onnx  sample=86 FAILED [class=0, true class=1] features=(6.00,3.40,4.50,1.60]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\sgd_classifier_iris.onnx  sample=120 FAILED [class=1, true class=2] features=(6.00,2.20,5.00,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\sgd_classifier_iris.onnx  sample=124 FAILED [class=1, true class=2] features=(6.30,2.70,4.90,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\sgd_classifier_iris.onnx  sample=127 FAILED [class=1, true class=2] features=(6.20,2.80,4.80,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\sgd_classifier_iris.onnx  sample=130 FAILED [class=1, true class=2] features=(7.20,3.00,5.80,1.60]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\sgd_classifier_iris.onnx  sample=134 FAILED [class=1, true class=2] features=(6.30,2.80,5.10,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\sgd_classifier_iris.onnx  sample=135 FAILED [class=1, true class=2] features=(6.10,2.60,5.60,1.40]
Iris_AllClassifiers (EURUSD,H1) 21 model:IRIS_models\sgd_classifier_iris.onnx   accuracy: 0.9333
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\categorical_naive_bayes_classifier_iris.onnx  sample=78 FAILED [class=2, true class=1] features=(6.70,3.00,5.00,1.70]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\categorical_naive_bayes_classifier_iris.onnx  sample=84 FAILED [class=2, true class=1] features=(6.00,2.70,5.10,1.60]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\categorical_naive_bayes_classifier_iris.onnx  sample=102 FAILED [class=1, true class=2] features=(5.80,2.70,5.10,1.90]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\categorical_naive_bayes_classifier_iris.onnx  sample=107 FAILED [class=1, true class=2] features=(4.90,2.50,4.50,1.70]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\categorical_naive_bayes_classifier_iris.onnx  sample=122 FAILED [class=1, true class=2] features=(5.60,2.80,4.90,2.00]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\categorical_naive_bayes_classifier_iris.onnx  sample=124 FAILED [class=1, true class=2] features=(6.30,2.70,4.90,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\categorical_naive_bayes_classifier_iris.onnx  sample=127 FAILED [class=1, true class=2] features=(6.20,2.80,4.80,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\categorical_naive_bayes_classifier_iris.onnx  sample=128 FAILED [class=1, true class=2] features=(6.10,3.00,4.90,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\categorical_naive_bayes_classifier_iris.onnx  sample=139 FAILED [class=1, true class=2] features=(6.00,3.00,4.80,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\categorical_naive_bayes_classifier_iris.onnx  sample=143 FAILED [class=1, true class=2] features=(5.80,2.70,5.10,1.90]
Iris_AllClassifiers (EURUSD,H1) 22 model:IRIS_models\categorical_naive_bayes_classifier_iris.onnx   accuracy: 0.9333
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifier_iris.onnx  sample=51 FAILED [class=2, true class=1] features=(7.00,3.20,4.70,1.40]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifier_iris.onnx  sample=52 FAILED [class=2, true class=1] features=(6.40,3.20,4.50,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifier_iris.onnx  sample=53 FAILED [class=2, true class=1] features=(6.90,3.10,4.90,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifier_iris.onnx  sample=57 FAILED [class=2, true class=1] features=(6.30,3.30,4.70,1.60]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifier_iris.onnx  sample=62 FAILED [class=2, true class=1] features=(5.90,3.00,4.20,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifier_iris.onnx  sample=65 FAILED [class=2, true class=1] features=(5.60,2.90,3.60,1.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifier_iris.onnx  sample=66 FAILED [class=2, true class=1] features=(6.70,3.10,4.40,1.40]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifier_iris.onnx  sample=67 FAILED [class=2, true class=1] features=(5.60,3.00,4.50,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifier_iris.onnx  sample=71 FAILED [class=2, true class=1] features=(5.90,3.20,4.80,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifier_iris.onnx  sample=76 FAILED [class=2, true class=1] features=(6.60,3.00,4.40,1.40]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifier_iris.onnx  sample=78 FAILED [class=2, true class=1] features=(6.70,3.00,5.00,1.70]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifier_iris.onnx  sample=79 FAILED [class=2, true class=1] features=(6.00,2.90,4.50,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifier_iris.onnx  sample=85 FAILED [class=2, true class=1] features=(5.40,3.00,4.50,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifier_iris.onnx  sample=86 FAILED [class=2, true class=1] features=(6.00,3.40,4.50,1.60]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifier_iris.onnx  sample=87 FAILED [class=2, true class=1] features=(6.70,3.10,4.70,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifier_iris.onnx  sample=89 FAILED [class=2, true class=1] features=(5.60,3.00,4.10,1.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifier_iris.onnx  sample=92 FAILED [class=2, true class=1] features=(6.10,3.00,4.60,1.40]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifier_iris.onnx  sample=109 FAILED [class=1, true class=2] features=(6.70,2.50,5.80,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifier_iris.onnx  sample=120 FAILED [class=1, true class=2] features=(6.00,2.20,5.00,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifier_iris.onnx  sample=130 FAILED [class=1, true class=2] features=(7.20,3.00,5.80,1.60]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifier_iris.onnx  sample=134 FAILED [class=1, true class=2] features=(6.30,2.80,5.10,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifier_iris.onnx  sample=135 FAILED [class=1, true class=2] features=(6.10,2.60,5.60,1.40]
Iris_AllClassifiers (EURUSD,H1) 23 model:IRIS_models\ridge_classifier_iris.onnx   accuracy: 0.8533
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifiercv_iris.onnx  sample=51 FAILED [class=2, true class=1] features=(7.00,3.20,4.70,1.40]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifiercv_iris.onnx  sample=52 FAILED [class=2, true class=1] features=(6.40,3.20,4.50,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifiercv_iris.onnx  sample=53 FAILED [class=2, true class=1] features=(6.90,3.10,4.90,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifiercv_iris.onnx  sample=57 FAILED [class=2, true class=1] features=(6.30,3.30,4.70,1.60]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifiercv_iris.onnx  sample=62 FAILED [class=2, true class=1] features=(5.90,3.00,4.20,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifiercv_iris.onnx  sample=65 FAILED [class=2, true class=1] features=(5.60,2.90,3.60,1.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifiercv_iris.onnx  sample=66 FAILED [class=2, true class=1] features=(6.70,3.10,4.40,1.40]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifiercv_iris.onnx  sample=67 FAILED [class=2, true class=1] features=(5.60,3.00,4.50,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifiercv_iris.onnx  sample=71 FAILED [class=2, true class=1] features=(5.90,3.20,4.80,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifiercv_iris.onnx  sample=76 FAILED [class=2, true class=1] features=(6.60,3.00,4.40,1.40]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifiercv_iris.onnx  sample=78 FAILED [class=2, true class=1] features=(6.70,3.00,5.00,1.70]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifiercv_iris.onnx  sample=79 FAILED [class=2, true class=1] features=(6.00,2.90,4.50,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifiercv_iris.onnx  sample=85 FAILED [class=2, true class=1] features=(5.40,3.00,4.50,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifiercv_iris.onnx  sample=86 FAILED [class=2, true class=1] features=(6.00,3.40,4.50,1.60]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifiercv_iris.onnx  sample=87 FAILED [class=2, true class=1] features=(6.70,3.10,4.70,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifiercv_iris.onnx  sample=89 FAILED [class=2, true class=1] features=(5.60,3.00,4.10,1.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifiercv_iris.onnx  sample=92 FAILED [class=2, true class=1] features=(6.10,3.00,4.60,1.40]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifiercv_iris.onnx  sample=109 FAILED [class=1, true class=2] features=(6.70,2.50,5.80,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifiercv_iris.onnx  sample=120 FAILED [class=1, true class=2] features=(6.00,2.20,5.00,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifiercv_iris.onnx  sample=130 FAILED [class=1, true class=2] features=(7.20,3.00,5.80,1.60]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifiercv_iris.onnx  sample=134 FAILED [class=1, true class=2] features=(6.30,2.80,5.10,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\ridge_classifiercv_iris.onnx  sample=135 FAILED [class=1, true class=2] features=(6.10,2.60,5.60,1.40]
Iris_AllClassifiers (EURUSD,H1) 24 model:IRIS_models\ridge_classifiercv_iris.onnx   accuracy: 0.8533
Iris_AllClassifiers (EURUSD,H1) ONNX: Removing initializer 'class_log_prior'. It is not used by any node and should be removed from the model.
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=51 FAILED [class=2, true class=1] features=(7.00,3.20,4.70,1.40]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=52 FAILED [class=2, true class=1] features=(6.40,3.20,4.50,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=53 FAILED [class=2, true class=1] features=(6.90,3.10,4.90,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=54 FAILED [class=2, true class=1] features=(5.50,2.30,4.00,1.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=55 FAILED [class=2, true class=1] features=(6.50,2.80,4.60,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=56 FAILED [class=2, true class=1] features=(5.70,2.80,4.50,1.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=57 FAILED [class=2, true class=1] features=(6.30,3.30,4.70,1.60]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=58 FAILED [class=2, true class=1] features=(4.90,2.40,3.30,1.00]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=59 FAILED [class=2, true class=1] features=(6.60,2.90,4.60,1.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=60 FAILED [class=2, true class=1] features=(5.20,2.70,3.90,1.40]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=61 FAILED [class=2, true class=1] features=(5.00,2.00,3.50,1.00]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=62 FAILED [class=2, true class=1] features=(5.90,3.00,4.20,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=63 FAILED [class=2, true class=1] features=(6.00,2.20,4.00,1.00]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=64 FAILED [class=2, true class=1] features=(6.10,2.90,4.70,1.40]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=65 FAILED [class=2, true class=1] features=(5.60,2.90,3.60,1.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=66 FAILED [class=2, true class=1] features=(6.70,3.10,4.40,1.40]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=67 FAILED [class=2, true class=1] features=(5.60,3.00,4.50,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=68 FAILED [class=2, true class=1] features=(5.80,2.70,4.10,1.00]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=69 FAILED [class=2, true class=1] features=(6.20,2.20,4.50,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=70 FAILED [class=2, true class=1] features=(5.60,2.50,3.90,1.10]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=71 FAILED [class=2, true class=1] features=(5.90,3.20,4.80,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=72 FAILED [class=2, true class=1] features=(6.10,2.80,4.00,1.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=73 FAILED [class=2, true class=1] features=(6.30,2.50,4.90,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=74 FAILED [class=2, true class=1] features=(6.10,2.80,4.70,1.20]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=75 FAILED [class=2, true class=1] features=(6.40,2.90,4.30,1.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=76 FAILED [class=2, true class=1] features=(6.60,3.00,4.40,1.40]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=77 FAILED [class=2, true class=1] features=(6.80,2.80,4.80,1.40]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=78 FAILED [class=2, true class=1] features=(6.70,3.00,5.00,1.70]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=79 FAILED [class=2, true class=1] features=(6.00,2.90,4.50,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=80 FAILED [class=0, true class=1] features=(5.70,2.60,3.50,1.00]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=81 FAILED [class=2, true class=1] features=(5.50,2.40,3.80,1.10]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=82 FAILED [class=2, true class=1] features=(5.50,2.40,3.70,1.00]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=83 FAILED [class=2, true class=1] features=(5.80,2.70,3.90,1.20]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=84 FAILED [class=2, true class=1] features=(6.00,2.70,5.10,1.60]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=85 FAILED [class=2, true class=1] features=(5.40,3.00,4.50,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=86 FAILED [class=2, true class=1] features=(6.00,3.40,4.50,1.60]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=87 FAILED [class=2, true class=1] features=(6.70,3.10,4.70,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=88 FAILED [class=2, true class=1] features=(6.30,2.30,4.40,1.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=89 FAILED [class=2, true class=1] features=(5.60,3.00,4.10,1.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=90 FAILED [class=2, true class=1] features=(5.50,2.50,4.00,1.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=91 FAILED [class=2, true class=1] features=(5.50,2.60,4.40,1.20]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=92 FAILED [class=2, true class=1] features=(6.10,3.00,4.60,1.40]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=93 FAILED [class=2, true class=1] features=(5.80,2.60,4.00,1.20]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=94 FAILED [class=2, true class=1] features=(5.00,2.30,3.30,1.00]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=95 FAILED [class=2, true class=1] features=(5.60,2.70,4.20,1.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=96 FAILED [class=2, true class=1] features=(5.70,3.00,4.20,1.20]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=97 FAILED [class=2, true class=1] features=(5.70,2.90,4.20,1.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=98 FAILED [class=2, true class=1] features=(6.20,2.90,4.30,1.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=99 FAILED [class=0, true class=1] features=(5.10,2.50,3.00,1.10]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\complement_naive_bayes_classifier_iris.onnx  sample=100 FAILED [class=2, true class=1] features=(5.70,2.80,4.10,1.30]
Iris_AllClassifiers (EURUSD,H1) 25 model:IRIS_models\complement_naive_bayes_classifier_iris.onnx   accuracy: 0.6667
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=2 FAILED [class=1, true class=0] features=(4.90,3.00,1.40,0.20]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=9 FAILED [class=1, true class=0] features=(4.40,2.90,1.40,0.20]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=10 FAILED [class=1, true class=0] features=(4.90,3.10,1.50,0.10]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=13 FAILED [class=1, true class=0] features=(4.80,3.00,1.40,0.10]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=21 FAILED [class=1, true class=0] features=(5.40,3.40,1.70,0.20]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=26 FAILED [class=1, true class=0] features=(5.00,3.00,1.60,0.20]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=31 FAILED [class=1, true class=0] features=(4.80,3.10,1.60,0.20]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=35 FAILED [class=1, true class=0] features=(4.90,3.10,1.50,0.20]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=42 FAILED [class=1, true class=0] features=(4.50,2.30,1.30,0.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=46 FAILED [class=1, true class=0] features=(4.80,3.00,1.40,0.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=102 FAILED [class=1, true class=2] features=(5.80,2.70,5.10,1.90]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=103 FAILED [class=1, true class=2] features=(7.10,3.00,5.90,2.10]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=104 FAILED [class=1, true class=2] features=(6.30,2.90,5.60,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=105 FAILED [class=1, true class=2] features=(6.50,3.00,5.80,2.20]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=106 FAILED [class=1, true class=2] features=(7.60,3.00,6.60,2.10]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=107 FAILED [class=1, true class=2] features=(4.90,2.50,4.50,1.70]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=108 FAILED [class=1, true class=2] features=(7.30,2.90,6.30,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=109 FAILED [class=1, true class=2] features=(6.70,2.50,5.80,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=110 FAILED [class=1, true class=2] features=(7.20,3.60,6.10,2.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=111 FAILED [class=1, true class=2] features=(6.50,3.20,5.10,2.00]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=112 FAILED [class=1, true class=2] features=(6.40,2.70,5.30,1.90]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=113 FAILED [class=1, true class=2] features=(6.80,3.00,5.50,2.10]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=114 FAILED [class=1, true class=2] features=(5.70,2.50,5.00,2.00]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=116 FAILED [class=1, true class=2] features=(6.40,3.20,5.30,2.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=117 FAILED [class=1, true class=2] features=(6.50,3.00,5.50,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=118 FAILED [class=1, true class=2] features=(7.70,3.80,6.70,2.20]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=119 FAILED [class=1, true class=2] features=(7.70,2.60,6.90,2.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=120 FAILED [class=1, true class=2] features=(6.00,2.20,5.00,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=121 FAILED [class=1, true class=2] features=(6.90,3.20,5.70,2.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=122 FAILED [class=1, true class=2] features=(5.60,2.80,4.90,2.00]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=123 FAILED [class=1, true class=2] features=(7.70,2.80,6.70,2.00]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=124 FAILED [class=1, true class=2] features=(6.30,2.70,4.90,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=125 FAILED [class=1, true class=2] features=(6.70,3.30,5.70,2.10]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=126 FAILED [class=1, true class=2] features=(7.20,3.20,6.00,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=127 FAILED [class=1, true class=2] features=(6.20,2.80,4.80,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=128 FAILED [class=1, true class=2] features=(6.10,3.00,4.90,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=129 FAILED [class=1, true class=2] features=(6.40,2.80,5.60,2.10]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=130 FAILED [class=1, true class=2] features=(7.20,3.00,5.80,1.60]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=131 FAILED [class=1, true class=2] features=(7.40,2.80,6.10,1.90]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=132 FAILED [class=1, true class=2] features=(7.90,3.80,6.40,2.00]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=133 FAILED [class=1, true class=2] features=(6.40,2.80,5.60,2.20]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=134 FAILED [class=1, true class=2] features=(6.30,2.80,5.10,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=135 FAILED [class=1, true class=2] features=(6.10,2.60,5.60,1.40]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=136 FAILED [class=1, true class=2] features=(7.70,3.00,6.10,2.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=137 FAILED [class=1, true class=2] features=(6.30,3.40,5.60,2.40]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=138 FAILED [class=1, true class=2] features=(6.40,3.10,5.50,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=139 FAILED [class=1, true class=2] features=(6.00,3.00,4.80,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=140 FAILED [class=1, true class=2] features=(6.90,3.10,5.40,2.10]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=141 FAILED [class=1, true class=2] features=(6.70,3.10,5.60,2.40]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=142 FAILED [class=1, true class=2] features=(6.90,3.10,5.10,2.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=143 FAILED [class=1, true class=2] features=(5.80,2.70,5.10,1.90]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=144 FAILED [class=1, true class=2] features=(6.80,3.20,5.90,2.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=145 FAILED [class=1, true class=2] features=(6.70,3.30,5.70,2.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=146 FAILED [class=1, true class=2] features=(6.70,3.00,5.20,2.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=147 FAILED [class=1, true class=2] features=(6.30,2.50,5.00,1.90]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=148 FAILED [class=1, true class=2] features=(6.50,3.00,5.20,2.00]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=149 FAILED [class=1, true class=2] features=(6.20,3.40,5.40,2.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\perceptron_classifier_iris.onnx  sample=150 FAILED [class=1, true class=2] features=(5.90,3.00,5.10,1.80]
Iris_AllClassifiers (EURUSD,H1) 26 model:IRIS_models\perceptron_classifier_iris.onnx   accuracy: 0.6133
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=51 FAILED [class=0, true class=1] features=(7.00,3.20,4.70,1.40]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=52 FAILED [class=0, true class=1] features=(6.40,3.20,4.50,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=53 FAILED [class=0, true class=1] features=(6.90,3.10,4.90,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=54 FAILED [class=0, true class=1] features=(5.50,2.30,4.00,1.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=55 FAILED [class=0, true class=1] features=(6.50,2.80,4.60,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=56 FAILED [class=0, true class=1] features=(5.70,2.80,4.50,1.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=57 FAILED [class=0, true class=1] features=(6.30,3.30,4.70,1.60]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=58 FAILED [class=0, true class=1] features=(4.90,2.40,3.30,1.00]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=59 FAILED [class=0, true class=1] features=(6.60,2.90,4.60,1.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=60 FAILED [class=0, true class=1] features=(5.20,2.70,3.90,1.40]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=61 FAILED [class=0, true class=1] features=(5.00,2.00,3.50,1.00]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=62 FAILED [class=0, true class=1] features=(5.90,3.00,4.20,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=63 FAILED [class=0, true class=1] features=(6.00,2.20,4.00,1.00]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=64 FAILED [class=0, true class=1] features=(6.10,2.90,4.70,1.40]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=65 FAILED [class=0, true class=1] features=(5.60,2.90,3.60,1.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=66 FAILED [class=0, true class=1] features=(6.70,3.10,4.40,1.40]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=67 FAILED [class=0, true class=1] features=(5.60,3.00,4.50,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=68 FAILED [class=0, true class=1] features=(5.80,2.70,4.10,1.00]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=69 FAILED [class=0, true class=1] features=(6.20,2.20,4.50,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=70 FAILED [class=0, true class=1] features=(5.60,2.50,3.90,1.10]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=71 FAILED [class=0, true class=1] features=(5.90,3.20,4.80,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=72 FAILED [class=0, true class=1] features=(6.10,2.80,4.00,1.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=73 FAILED [class=0, true class=1] features=(6.30,2.50,4.90,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=74 FAILED [class=0, true class=1] features=(6.10,2.80,4.70,1.20]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=75 FAILED [class=0, true class=1] features=(6.40,2.90,4.30,1.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=76 FAILED [class=0, true class=1] features=(6.60,3.00,4.40,1.40]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=77 FAILED [class=0, true class=1] features=(6.80,2.80,4.80,1.40]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=78 FAILED [class=0, true class=1] features=(6.70,3.00,5.00,1.70]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=79 FAILED [class=0, true class=1] features=(6.00,2.90,4.50,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=80 FAILED [class=0, true class=1] features=(5.70,2.60,3.50,1.00]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=81 FAILED [class=0, true class=1] features=(5.50,2.40,3.80,1.10]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=82 FAILED [class=0, true class=1] features=(5.50,2.40,3.70,1.00]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=83 FAILED [class=0, true class=1] features=(5.80,2.70,3.90,1.20]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=84 FAILED [class=0, true class=1] features=(6.00,2.70,5.10,1.60]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=85 FAILED [class=0, true class=1] features=(5.40,3.00,4.50,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=86 FAILED [class=0, true class=1] features=(6.00,3.40,4.50,1.60]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=87 FAILED [class=0, true class=1] features=(6.70,3.10,4.70,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=88 FAILED [class=0, true class=1] features=(6.30,2.30,4.40,1.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=89 FAILED [class=0, true class=1] features=(5.60,3.00,4.10,1.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=90 FAILED [class=0, true class=1] features=(5.50,2.50,4.00,1.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=91 FAILED [class=0, true class=1] features=(5.50,2.60,4.40,1.20]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=92 FAILED [class=0, true class=1] features=(6.10,3.00,4.60,1.40]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=93 FAILED [class=0, true class=1] features=(5.80,2.60,4.00,1.20]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=94 FAILED [class=0, true class=1] features=(5.00,2.30,3.30,1.00]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=95 FAILED [class=0, true class=1] features=(5.60,2.70,4.20,1.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=96 FAILED [class=0, true class=1] features=(5.70,3.00,4.20,1.20]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=97 FAILED [class=0, true class=1] features=(5.70,2.90,4.20,1.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=98 FAILED [class=0, true class=1] features=(6.20,2.90,4.30,1.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=99 FAILED [class=0, true class=1] features=(5.10,2.50,3.00,1.10]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=100 FAILED [class=0, true class=1] features=(5.70,2.80,4.10,1.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=101 FAILED [class=0, true class=2] features=(6.30,3.30,6.00,2.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=102 FAILED [class=0, true class=2] features=(5.80,2.70,5.10,1.90]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=103 FAILED [class=0, true class=2] features=(7.10,3.00,5.90,2.10]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=104 FAILED [class=0, true class=2] features=(6.30,2.90,5.60,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=105 FAILED [class=0, true class=2] features=(6.50,3.00,5.80,2.20]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=106 FAILED [class=0, true class=2] features=(7.60,3.00,6.60,2.10]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=107 FAILED [class=0, true class=2] features=(4.90,2.50,4.50,1.70]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=108 FAILED [class=0, true class=2] features=(7.30,2.90,6.30,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=109 FAILED [class=0, true class=2] features=(6.70,2.50,5.80,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=110 FAILED [class=0, true class=2] features=(7.20,3.60,6.10,2.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=111 FAILED [class=0, true class=2] features=(6.50,3.20,5.10,2.00]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=112 FAILED [class=0, true class=2] features=(6.40,2.70,5.30,1.90]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=113 FAILED [class=0, true class=2] features=(6.80,3.00,5.50,2.10]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=114 FAILED [class=0, true class=2] features=(5.70,2.50,5.00,2.00]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=115 FAILED [class=0, true class=2] features=(5.80,2.80,5.10,2.40]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=116 FAILED [class=0, true class=2] features=(6.40,3.20,5.30,2.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=117 FAILED [class=0, true class=2] features=(6.50,3.00,5.50,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=118 FAILED [class=0, true class=2] features=(7.70,3.80,6.70,2.20]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=119 FAILED [class=0, true class=2] features=(7.70,2.60,6.90,2.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=120 FAILED [class=0, true class=2] features=(6.00,2.20,5.00,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=121 FAILED [class=0, true class=2] features=(6.90,3.20,5.70,2.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=122 FAILED [class=0, true class=2] features=(5.60,2.80,4.90,2.00]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=123 FAILED [class=0, true class=2] features=(7.70,2.80,6.70,2.00]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=124 FAILED [class=0, true class=2] features=(6.30,2.70,4.90,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=125 FAILED [class=0, true class=2] features=(6.70,3.30,5.70,2.10]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=126 FAILED [class=0, true class=2] features=(7.20,3.20,6.00,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=127 FAILED [class=0, true class=2] features=(6.20,2.80,4.80,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=128 FAILED [class=0, true class=2] features=(6.10,3.00,4.90,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=129 FAILED [class=0, true class=2] features=(6.40,2.80,5.60,2.10]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=130 FAILED [class=0, true class=2] features=(7.20,3.00,5.80,1.60]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=131 FAILED [class=0, true class=2] features=(7.40,2.80,6.10,1.90]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=132 FAILED [class=0, true class=2] features=(7.90,3.80,6.40,2.00]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=133 FAILED [class=0, true class=2] features=(6.40,2.80,5.60,2.20]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=134 FAILED [class=0, true class=2] features=(6.30,2.80,5.10,1.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=135 FAILED [class=0, true class=2] features=(6.10,2.60,5.60,1.40]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=136 FAILED [class=0, true class=2] features=(7.70,3.00,6.10,2.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=137 FAILED [class=0, true class=2] features=(6.30,3.40,5.60,2.40]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=138 FAILED [class=0, true class=2] features=(6.40,3.10,5.50,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=139 FAILED [class=0, true class=2] features=(6.00,3.00,4.80,1.80]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=140 FAILED [class=0, true class=2] features=(6.90,3.10,5.40,2.10]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=141 FAILED [class=0, true class=2] features=(6.70,3.10,5.60,2.40]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=142 FAILED [class=0, true class=2] features=(6.90,3.10,5.10,2.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=143 FAILED [class=0, true class=2] features=(5.80,2.70,5.10,1.90]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=144 FAILED [class=0, true class=2] features=(6.80,3.20,5.90,2.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=145 FAILED [class=0, true class=2] features=(6.70,3.30,5.70,2.50]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=146 FAILED [class=0, true class=2] features=(6.70,3.00,5.20,2.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=147 FAILED [class=0, true class=2] features=(6.30,2.50,5.00,1.90]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=148 FAILED [class=0, true class=2] features=(6.50,3.00,5.20,2.00]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=149 FAILED [class=0, true class=2] features=(6.20,3.40,5.40,2.30]
Iris_AllClassifiers (EURUSD,H1) model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx  sample=150 FAILED [class=0, true class=2] features=(5.90,3.00,5.10,1.80]
Iris_AllClassifiers (EURUSD,H1) 27 model:IRIS_models\bernoulli_naive_bayes_classifier_iris.onnx   accuracy: 0.3333


2.29. ONNX로 변환할 수 없는 Scikit-Learn의 분류 모델

convert_sklearn 프로세스의 오류로 인해 일부 분류 모델은 ONNX 형식으로 변환될 수 없습니다.


2.29.1. DummyClassifier

DummyClassifier는 분류 작업의 간단한 기준 모델로 사용되는 Scikit-learn 라이브러리의 분류기로 보다 복잡한 분류 모델의 성능을 테스트하고 평가하기 위해 설계되었습니다.

작동 원리:

DummyClassifier는 매우 간단하게 작동하며 입력 데이터를 고려하지 않고 무작위 또는 순진한 예측을 수행합니다. 또한 다양한 전략을 제공합니다('전략' 매개변수를 통해 선택):

  1. "most_frequent"(가장 빈번한 클래스): 이 전략은 학습 데이터 세트에서 가장 자주 나타나는 클래스를 예측하며 클래스가 불균형하고 우세한 클래스를 예측해야 하는 상황에서 유용할 수 있습니다.
  2. "계층화"(계층화 선택): 이 전략은 학습 데이터 세트의 클래스 분포와 일치하는 예측을 시도합니다. 무작위 추측을 사용하지만 클래스 비율을 고려합니다.
  3. "균일"(균일 배포): 이 전략은 각 클래스에 대해 동일한 확률로 무작위 예측을 수행하며 클래스의 균형을 맞추고 모델의 평균 성능을 테스트하려는 경우에 유용합니다.

기능:

  • 단순성: DummyClassifier는 기준 모델을 얼마나 빨리 훈련할 수 있는지와 어떤 결과를 생성하는지 테스트하는 데 유용합니다. 이는 다른 분류기의 성능을 빠르게 평가하는 데 도움이 될 수 있습니다.
  • 파이프라인 사용량: 여러분은 비교 및 테스트를 위해 다른 변환 및 모델과 결합하여 파이프라인 내에서 DummyClassifier를 기준 모델로 사용할 수 있습니다.

제한 사항:

  • 데이터를 활용하지 않습니다: DummyClassifier는 실제 데이터를 고려하지 않고 무작위 또는 순진한 예측을 합니다. DummyClassifier는 데이터에서 학습하거나 패턴을 발견할 수 없습니다.
  • 복잡한 작업에는 적합하지 않습니다: 이 분류기는 복잡한 분류 작업을 해결하도록 설계되지 않았으며 일반적으로 대규모 데이터 세트와 복잡한 패턴이 있는 작업에서는 좋은 결과를 얻지 못합니다.
  • 정보성 부족: DummyClassifier를 사용하여 얻은 결과는 정보성이 부족하고 모델 성능에 대한 유용한 정보를 제공하지 못할 수 있습니다. 결과들은 코드 테스트 및 평가에 더 유용합니다.


DummyClassifier는 분류 모델을 초기 테스트하고 평가하는 데 유용한 도구이지만 복잡한 작업에서는 사용이 제한되며 고급 분류 알고리즘을 대체할 수는 없습니다.

2.29.1.1. DummyClassifier 모델을 생성하는 코드

# Iris_DummyClassifier.py
# 이 코드는 붓꽃 데이터 세트에서 DummyClassifier 모델을 학습하고 이를 ONNX 형식으로 내보내고 ONNX 모델을 사용하여 예측하는 과정을 보여줍니다.
# 또한 원래 모델과 ONNX 모델 모두의 정확도를 평가합니다.
# Copyright 2023, MetaQuotes Ltd.
# https://www.mql5.com

# 필요한 라이브러리 가져오기
sklearn에서 데이터 세트 import
sklearn.dummy에서 더미 분류기 import
sklearn.metrics에서 정확도_점수, 분류_보고서 import
skl2onnx에서 convert_sklearn import
skl2onnx.common.data_types에서 FloatTensorType import
onnxruntime import ort
numpyimport np
argv 시스템 가져오기

# 모델 저장 경로를 정의합니다.
data_path = argv[0]
last_index = data_path.rfind("\\")+ 1
data_path = data_path[0:last_index]

# 붓꽃 데이터 세트 로드
iris = datasets.load_iris()
X = iris.data
y = iris.target

# "most_frequent" 전략을 사용하여 DummyClassifier 모델을 생성합니다.
dummy_classifier = DummyClassifier(strategy="most_frequent")

# 전체 데이터 세트에 대해 모델 훈련하기
dummy_classifier.fit(X, y)

# 전체 데이터 집합에 대한 클래스 예측
y_pred = dummy_classifier.predict(X)

# 모델의 정확도 평가
accuracy = accuracy_score(y, y_pred)
print("Accuracy of DummyClassifier model:", accuracy)

# 분류 보고서 표시
print("\nClassification Report:\n", classification_report(y, y_pred))

# 입력 데이터 유형 정의
initial_type = [(('float_input', FloatTensorType([None, X.shape[1]]))]

# float 데이터 유형을 사용하여 모델을 ONNX 형식으로 내보내기
onnx_model = convert_sklearn(dummy_classifier, initial_types=initial_type, target_opset=12)

# 파일에 모델 저장
onnx_filename = data_path + "dummy_classifier_iris.onnx"
with open(onnx_filename, "wb") as f:
    f.write(onnx_model.SerializeToString())

# 모델 경로 인쇄
print(f"Model saved to {onnx_filename}")

# ONNX 모델을 로드하고 예측을 수행합니다.
onnx_session = ort.InferenceSession(onnx_filename)
input_name = onnx_session.get_inputs()[0].name
output_name = onnx_session.get_outputs()[0].name

# 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}")

# 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}")

# 데이터를 부동 소수점 형식(float32)으로 변환합니다.
X_float32 = X.astype(np.float32)

# ONNX를 사용하여 전체 데이터 세트의 클래스 예측하기
y_pred_onnx = onnx_session.run([output_name], {input_name: X_float32})[0]

# ONNX 모델의 정확도를 평가합니다.
accuracy_onnx = accuracy_score(y, y_pred_onnx)
print("\nAccuracy of DummyClassifier model in ONNX format:", accuracy_onnx)

출력:

Python DummyClassifier 모델의 정확도: 0.3333333333333333
파이썬    
파이썬 분류 보고서:
파이썬 정밀 리콜 f1-점수 지원
파이썬    
파이썬 0 0.33 1.00 0.50 50
파이썬 1 0.00 0.00 0.00 50
파이썬 2 0.00 0.00 0.00 50
파이썬    
파이썬 정확도 0.33 150
파이썬 매크로 평균 0.11 0.33 0.17 150
파이썬 가중 평균 0.11 0.33 0.17 150
파이썬   

모델이 Scikit-learn에서 성공적으로 빌드되고 실행되었지만 ONNX로 변환하는 동안 오류가 발생했습니다.

'오류' 탭에는 ONNX 형식으로의 변환 오류와 관련된 메시지가 표시됩니다:

    onnx_model = convert_sklearn(dummy_classifier, initial_types=initial_type, target_opset=12) Iris_DummyClassifier.py 44 1
    onnx_model = convert_topology( convert.py 208 1
    topology.convert_operators(container=container, verbose=verbose)    _topology.py    1532    1
    self.call_shape_calculator(operator)    _topology.py    1348    1
    operator.infer_types() _topology.py 1163 1
    raise MissingShapeCalculator( _topology.py 629 1
skl2onnx.common.exceptions.MissingShapeCalculator: '<class 'sklearn.dummy.DummyClassifier'>' 유형에 대한 shape 계산기를 찾을 수 없습니다. _topology.py 629 1
이는 일반적으로 변환 중인 파이프라인이 _topology.py 629 1를 포함하고 있다는 것을 의미합니다
변환기 또는 해당 변환기가 없는 예측자 _topology.py 629 1
SKLEARN-onnx에서 구현되었습니다. 만약 변환이 구현된 경우 _topology.py 629 1
여러분은 다른 라이브러리에 _topology.py 629 1을 등록해야 합니다.
sklearn-onnx (function    _topology.py    629    1에서 사용될 수 있도록 변환됩니다(함수 _topology.py 629 1
update_registered_converter). 모델이 아직 다루지 않은 경우 _topology.py 629 1
여러분은 sklearn-onnx로 _topology.py 629 1에 문제를 제기할 수 있습니다.
https://github.com/onnx/sklearn-onnx/issues _topology.py 629 1
변환기를 구현하거나 _topology.py 629 1에 기여할 수 있습니다.
프로젝트. 모델이 사용자 지정 모델인 경우 새 변환기는 _topology.py 629 1이어야 합니다.
구현해야 합니다. 예제는 _topology.py 629 1 갤러리에서 찾을 수 있습니다.
2071ms 19 1초만에 Iris_DummyClassifier.py 완료

따라서 DummyClassifier 모델은 ONNX로 변환될 수 없습니다.


2.29.2. GaussianProcessClassifier

GaussianProcessClassifier는 분류 작업에 가우시안 프로세스를 사용하는 분류기로 가우스 프로세스를 사용하는 모델 계열에 속하며 확률적 클래스 추정이 필요한 작업에 유용할 수 있습니다.

작동 원리:

  1. GaussianProcessClassifier는 가우스 프로세스를 사용하여 피처 공간에서 클래스 확률 추정 공간으로의 매핑을 모델링하고
  2. 각 클래스에 속하는 점의 확률을 평가하여 각 클래스에 대한 확률 모델을 구축합니다.
  3. 분류 동안 주어진 포인트에 대해 가장 높은 확률을 가진 클래스를 선택합니다.

기능:

  • 확률적 분류: GaussianProcessClassifier는 확률적 클래스 추정치를 제공하여 모델 불확실성을 평가하는 데 유용할 수 있습니다.
  • 적응력: 이 분류기는 데이터에 적응하고 새로운 관찰을 기반으로 예측을 업데이트할 수 있습니다.
  • 보정: '보정' 메서드를 사용하여 모델을 보정하여 확률 추정치를 개선할 수 있습니다.

제한 사항:

  • 계산의 복잡성: GaussianProcessClassifier는 대규모 데이터 세트 및/또는 고차원 피처 공간의 경우 계산 비용이 많이 들 수 있습니다.
  • 대용량의 샘플에는 적합하지 않습니다: 이 분류기는 계산 복잡성으로 인해 대규모 데이터 세트에 대한 학습에는 효율적이지 않을 수 있습니다.
  • 해석의 복잡성: 가우스 프로세스는 특히 베이지안 통계에 대한 경험이 없는 사용자에게는 해석과 이해가 어려울 수 있습니다.

GaussianProcessClassifier는 확률적 클래스 추정이 중요하고 계산 비용을 처리할 수 있는 작업에서 유용합니다. 그렇지 않으면 대규모 데이터 세트나 단순한 데이터 구조의 분류 작업의 경우 다른 분류 알고리즘이 더 적합할 수 있습니다.

2.29.2.1. GaussianProcessClassifier 모델 생성 코드

# Iris_GaussianProcessClassifier.py
# 이 코드는 Iris 데이터 세트에 대해 Iris_GaussianProcess 분류기 모델을 학습하고 이를 ONNX 형식으로 내보내고 ONNX 모델을 사용하여 예측하는 과정을 보여줍니다.
# 또한 원래 모델과 ONNX 모델 모두의 정확도를 평가합니다.
# Copyright 2023, MetaQuotes Ltd.
# https://www.mql5.com

# 필요한 라이브러리 가져오기
sklearn에서 데이터 세트 import
sklearn.gaussian_process 에서 GaussianProcessClassifier import
sklearn.gaussian_process.kernels 에서 RBF import
sklearn.metrics에서 accuracy_score, classification_report import
skl2onnx에서 convert_sklearn import
skl2onnx.common.data_types에서 FloatTensorType import
import onnxruntime as ort
import numpy as np
sys에서argv import

# 모델 저장 경로를 정의합니다.
data_path = argv[0]
last_index = data_path.rfind("\\")+ 1
data_path = data_path[0:last_index]

# 붓꽃 데이터 세트 로드
iris = datasets.load_iris()
X = iris.data
y = iris.target

# RBF 커널을 사용하여 GaussianProcessClassifier 모델을 생성합니다.
kernel = 1.0 * RBF(1.0)
gpc_model = GaussianProcessClassifier(kernel=kernel)

# 전체 데이터 세트에 대해 모델 훈련하기
gpc_model.fit(X, y)

# 전체 데이터 집합에 대한 클래스 예측
y_pred = gpc_model.predict(X)

# 모델의 정확도 평가
accuracy = accuracy_score(y, y_pred)
print("Accuracy of GaussianProcessClassifier model:", accuracy)

# 분류 보고서 표시
print("\nClassification Report:\n", classification_report(y, y_pred))

# 입력 데이터 유형 정의
initial_type = [(('float_input', FloatTensorType([None, X.shape[1]]))]

# float 데이터 유형을 사용하여 모델을 ONNX 형식으로 내보내기
onnx_model = convert_sklearn(gpc_model, initial_types=initial_type, target_opset=12)

# 파일에 모델 저장
onnx_filename = data_path + "gpc_iris.onnx"
with open(onnx_filename, "wb") as f:
    f.write(onnx_model.SerializeToString())

# 모델 경로를 인쇄합니다.
print(f"Model saved to {onnx_filename}")

# ONNX 모델을 로드하고 예측을 수행합니다.
onnx_session = ort.InferenceSession(onnx_filename)
input_name = onnx_session.get_inputs()[0].name
output_name = onnx_session.get_outputs()[0].name

# 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}")

# ONNX에서 출력 텐서에 대한 정보를 표시합니다.
print("\nInformation about output tensors in ONNX:")
for i, enumerate(onnx_session.get_outputs())의 output_tensor:
    print(f"{i + 1}. Name: {output_tensor.name}, Data Type: {output_tensor.type}, Shape: {output_tensor.shape}")

# 데이터를 부동 소수점 형식(float32)으로 변환합니다.
X_float32 = X.astype(np.float32)

# ONNX를 사용하여 전체 데이터 세트의 클래스 예측하기
y_pred_onnx = onnx_session.run([output_name], {input_name: X_float32})[0]

# ONNX 모델의 정확도를 평가합니다.
accuracy_onnx = accuracy_score(y, y_pred_onnx)
print("\nAccuracy of GaussianProcessClassifier model in ONNX format:", accuracy_onnx)

출력:

Python GaussianProcessClassifier 모델의 정확도: 0.9866666666666667
파이썬    
파이썬 분류 보고서:
파이썬 정밀 리콜 f1-점수 지원
파이썬    
파이썬 0 1.00 1.00 1.00 50
파이썬 1 0.98 0.98 0.98 50
파이썬 2 0.98 0.98 0.98 50
파이썬    
파이썬 정확도 0.99 150
파이썬 매크로 평균 0.99 0.99 0.99 150
파이썬 가중 평균 0.99 0.99 0.99 150
파이썬   

'오류' 탭에는 ONNX 형식으로의 변환 오류와 관련된 메시지가 표시됩니다:

    onnx_model = convert_sklearn(gpc_model, initial_types=initial_type, target_opset=12)    Iris_GaussianProcessClassifier.py    46    1
    onnx_model = convert_topology( convert.py 208 1
    topology.convert_operators(container=container, verbose=verbose)    _topology.py    1532    1
    self.call_converter(operator, container, verbose=verbose)    _topology.py    1349    1
    conv(self.scopes[0], operator, container)    _topology.py    1132    1
    return self._fct(*args) _registration.py 27 1
    raise NotImplementedError("Only binary classification is iplemented.")    gaussian_process.py    247    1
NotImplementedError: 바이너리 분류만 구현됩니다. gaussian_process.py    247    1
4004ms 초만에 완료된 Iris_GaussianProcessClassifier.py 9 1

따라서 가우스프로세스분류기 모델을 ONNX로 변환할 수 없었습니다.


2.29.3. LabelPropagation 분류기

LabelPropagation는 분류 작업에 사용되는 반감독 학습 메서드입니다. 이 메서드의 기본 아이디어는 그래프 기반 데이터 구조에서 레이블이 지정된 인스턴스에서 레이블이 지정되지 않은 인스턴스로 레이블(클래스)을 전파하는 것입니다.

LabelPropagation 프로세스:

  1. LabelPropagation는 노드가 데이터 인스턴스를 나타내고 노드 간의 에지가 인스턴스 간의 유사성 또는 근접성을 반영하는 그래프를 구성하는 것으로 시작합니다.
  2. 초기 레이블 할당: 레이블이 지정된 인스턴스에는 레이블이 지정되고 레이블이 지정되지 않은 인스턴스는 정의되지 않은 레이블로 시작됩니다.
  3. 그래프의 레이블 전파: 레이블이 지정된 인스턴스의 레이블은 인스턴스 간의 유사성에 따라 레이블이 지정되지 않은 인스턴스로 전파됩니다. 이 유사성은 그래프에서 가장 가까운 이웃을 사용하는 등 다양한 방법으로 결정될 수 있습니다.
  4. 반복적인 프로세스: 레이블은 여러 번의 반복을 통해 변경될 수 있으며 각 반복은 현재 레이블과 인스턴스 유사성을 기반으로 레이블이 없는 인스턴스의 레이블을 업데이트합니다.
  5. 안정화: 이 프로세스는 레이블이 안정화되거나 특정 중지 기준이 충족될 때까지 계속됩니다.

LabelPropagation의 장점:

  • 레이블이 지정되지 않은 데이터로부터의 정보를 활용합니다: 레이블 전파를 사용하면 레이블이 지정되지 않은 인스턴스의 정보를 사용하여 분류 품질을 개선할 수 있습니다. 이는 레이블링 된 데이터가 부족할 때 특히 유용합니다.
  • 노이즈에 대한 견고함: 이 메서드는 인스턴스 유사성을 고려하고 레이블에만 의존하지 않기 때문에 노이즈가 있는 데이터를 효과적으로 처리할 수 있습니다.

LabelPropagation의 한계:

  • 그래프 선택에 대한 의존성: LabelPropagation 분류의 품질은 그래프의 선택과 인스턴스 유사성을 결정하는 메서드에 따라 크게 달라질 수 있습니다. 매개변수를 잘못 선택하면 결과가 좋지 않을 수 있습니다.
  • 계산의 복잡성: 데이터의 크기와 복잡성, 메서드 매개변수에 따라 LabelPropagation에는 상당한 계산 리소스가 필요할 수 있습니다.
  • 잠재력 과잉: 그래프에 노이즈가 많은 가장자리나 잘못된 레이블이 너무 많으면 메서드가 과도하게 적합할 수 있습니다.
  • 컨버전스는 보장되지 않습니다: 드물지만 LabelPropagation이 안정적인 레이블로 수렴되지 않을 수 있으므로 반복 횟수를 제한하거나 다른 설정을 조정해야 할 수도 있습니다.

레이블 전파는 강력한 메서드이지만 좋은 결과를 얻으려면 신중한 매개변수 조정과 데이터의 그래프 구조 분석이 필요합니다.

2.29.3.1. LabelPropagationClassifier 모델 생성 코드

# Iris_LabelPropagationClassifier.py

# 이 코드는 붓꽃 데이터 세트에서 LabelPropagation Classifier 모델을 학습하고 이를 ONNX 형식으로 내보내고 ONNX 모델을 사용하여 예측하는 과정을 보여줍니다.
# 또한 원래 모델과 ONNX 모델 모두의 정확도를 평가합니다.
# Copyright 2023, MetaQuotes Ltd.
# https://www.mql5.com

# 필요한 라이브러리 가져오기
sklearn에서 데이터 세트 import
sklearn.semi_supervised에서 LabelPropagation import
sklearn.metrics에서 accuracy_score, classification_report import
skl2onnx에서 convert_sklearn import
skl2onnx.common.data_types에서 FloatTensorType import
import onnxruntime as ort
import numpy as np
sys에서 argv 가져오기

# 모델 저장 경로를 정의합니다.
data_path = argv[0]
last_index = data_path.rfind("\\")+ 1
data_path = data_path[0:last_index]

# 붓꽃 데이터 세트 로드
iris = datasets.load_iris()
X = iris.data
y = iris.target

# LabelPropagation 모델 생성
lp_model = LabelPropagation()

# 전체 데이터 세트에 대해 모델 훈련하기
lp_model.fit(X, y)

# 전체 데이터 집합에 대한 클래스 예측
y_pred = lp_model.predict(X)

# 모델의 정확도 평가
accuracy = accuracy_score(y, y_pred)
print("Accuracy of LabelPropagation model:", accuracy)

# 분류 보고서 표시
print("\nClassification Report:\n", classification_report(y, y_pred))

# 입력 데이터 유형 정의
initial_type = [(('float_input', FloatTensorType([None, X.shape[1]]))]

# float 데이터 유형을 사용하여 모델을 ONNX 형식으로 내보내기
onnx_model = convert_sklearn(lp_model, initial_types=initial_type, target_opset=12)

# 파일에 모델 저장
onnx_filename = data_path + "lp_iris.onnx"
with open(onnx_filename, "wb") as f:
    f.write(onnx_model.SerializeToString())

# 모델 경로를 인쇄합니다.
print(f"Model saved to {onnx_filename}")

# ONNX 모델을 로드하고 예측을 수행합니다s
onnx_session = ort.InferenceSession(onnx_filename)
input_name = onnx_session.get_inputs()[0].name
output_name = onnx_session.get_outputs()[0].name

# 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}")

# ONNX에서 출력 텐서에 대한 정보를 표시합니다.
print("\nInformation about output tensors in ONNX:")
for i, enumerate(onnx_session.get_outputs())의 output_tensor:
    print(f"{i + 1}. Name: {output_tensor.name}, Data Type: {output_tensor.type}, Shape: {output_tensor.shape}")

# 데이터를 부동 소수점 형식(float32)으로 변환합니다.
X_float32 = X.astype(np.float32)

# ONNX를 사용하여 전체 데이터 세트의 클래스 예측하기
y_pred_onnx = onnx_session.run([output_name], {input_name: X_float32})[0]

# ONNX 모델의 정확도를 평가합니다.
accuracy_onnx = accuracy_score(y, y_pred_onnx)
print("\nAccuracy of LabelPropagation model in ONNX format:", accuracy_onnx)

출력:

Python LabelPropagation 모델의 정확도: 1.0
파이썬    
파이썬 분류 보고서:
파이썬 정밀 리콜 f1-점수 지원
파이썬    
파이썬 0 1.00 1.00 1.00 50
파이썬 1 1.00 1.00 1.00 50
파이썬 2 1.00 1.00 1.00 50
파이썬    
파이썬 정확도 1.00 150
파이썬 매크로 평균 1.00 1.00 1.00 150
파이썬 가중 평균 1.00 1.00 1.00 150
파이썬   

모델이 구축되었지만 ONNX 형식으로 변환하는 동안 오류가 발생했습니다.

'오류' 탭에는 ONNX 형식으로의 변환 오류와 관련된 메시지가 표시됩니다:

    onnx_model = convert_sklearn(lp_model, initial_types=initial_type, target_opset=12) Iris_LabelPropagation.py 44 1
    onnx_model = convert_topology( convert.py 208 1
    topology.convert_operators(container=container, verbose=verbose)    _topology.py    1532    1
    self.call_shape_calculator(operator)    _topology.py    1348    1
    operator.infer_types() _topology.py 1163 1
    raise MissingShapeCalculator( _topology.py 629 1
skl2onnx.common.exceptions.MissingShapeCalculator: Unable to find a shape calculator for type '<class 'sklearn.semi_supervised._label_propagation.LabelPropagation'>'.    _topology.py    629    1
이는 일반적으로 변환 중인 파이프라인이 _topology.py를 포함하고 있다는 것을 의미합니다 629 1
변환기 또는 해당 변환기가 없는 예측자 _topology.py 629 1
sklearn-onnx에서 구현 만약 변환이 구현된 경우 _topology.py 629 1
여러분은 다른 라이브러리에 _topology.py를 등록해야 합니다. 629 1
sklearn-onnx (function_topology.py에서 사용될 수 있도록 변환됩니다(함수 _topology.py 629 1
update_registered_converter). 모델이 아직 다루지 않은 경우 _topology.py 629 1
여러분은 sklearn-onnx로 _topology.py에 문제를 제기할 수 있습니다. 629 1
https://github.com/onnx/sklearn-onnx/issues _topology.py 629 1
변환기를 구현하거나 _topology.py에 기여할 수 있습니다. 629 1
프로젝트. 모델이 사용자 지정 모델인 경우 새 변환기는 _topology.py이어야 합니다. 629 1
구현해야 합니다. 예제는 _topology.py 629 1 갤러리에서 찾을 수 있습니다.
2064ms 초만에 Iris_LabelPropagation.py 완료 19 1
따라서 LabelSpreading 분류기 모델을 ONNX로 변환할 수 없습니다.


2.29.4. LabelSpreading 분류기

LabelSpreading은 분류 작업에 사용되는 세미 지도 학습 메서드입니다. 이는 LabelPropagation과 유사하게 그래프 기반 데이터 구조에서 레이블이 지정된 인스턴스에서 레이블이 지정되지 않은 인스턴스로 레이블(클래스)을 전파한다는 아이디어를 기반으로 합니다. 그러나 LabelSpreading에는 라벨 전파 프로세스의 추가 안정화 및 정규화 기능이 포함되어 있습니다.

LabelSpreading 프로세스:

  1. LabelPropagation는 노드가 데이터 인스턴스를 나타내고 노드 간의 에지가 인스턴스 간의 유사성 또는 근접성을 반영하는 그래프를 구성하는 것으로 시작합니다.
  2. 초기 레이블 할당: 레이블이 지정된 인스턴스에는 레이블이 지정되고 레이블이 지정되지 않은 인스턴스는 정의되지 않은 레이블로 시작됩니다.
  3. 그래프의 레이블 전파: 레이블이 지정된 인스턴스의 레이블은 인스턴스 간의 유사성에 따라 레이블이 지정되지 않은 인스턴스로 전파됩니다.
  4. 정규화 및 안정화: LabelSpreading에는 레이블 전파 프로세스를 안정화하고 과적합을 줄이는 데 도움이 되는 정규화가 포함되어 있습니다. 이는 인스턴스 간의 유사성뿐만 아니라 인접 인스턴스의 레이블 간의 차이점도 고려함으로써 달성됩니다.
  5. 반복적인 프로세스: 레이블은 여러 번의 반복을 통해 변경될 수 있습니다. 각 반복은 현재 레이블과 정규화에 따라 레이블이 없는 인스턴스의 레이블을 업데이트합니다.
  6. 안정화: 이 프로세스는 레이블이 안정화되거나 특정 중지 기준이 충족될 때까지 계속됩니다.

LabelSpreading의 장점:

  • 레이블이 지정되지 않은 데이터로부터의 정보를 활용합니다: LabelSpreading을 사용하면 레이블이 지정되지 않은 인스턴스의 정보를 사용하여 분류 품질을 개선할 수 있습니다.
  • 정규화: LabelSpreading에 정규화 기능이 있으면 과적합을 줄이고 레이블 전파 프로세스를 더욱 안정적으로 만들 수 있습니다.

LabelSpreading의 한계:

  • 그래프 선택에 대한 의존성: LabelPropagation과 마찬가지로 LabelSpreading 분류의 품질은 그래프와 메서드 매개변수의 선택에 따라 크게 달라질 수 있습니다.
  • 계산의 복잡성: 데이터의 크기와 복잡성, 메서드 매개변수에 따라 LabelSpreading에 상당한 계산 리소스가 필요할 수 있습니다.
  • 항상 수렴하는 것은 아닙니다: 드물지만 LabelSpreading이 안정적인 레이블로 수렴되지 않을 수 있으므로 반복 횟수를 제한하거나 다른 설정을 조정해야 할 수 있습니다.

레이블 확산은 세심한 조정이 필요한 메서드이며 분류 작업에서 레이블이 없는 데이터를 사용하기 위한 강력한 도구가 될 수 있습니다.

2.29.4.1. LabelSpreadingClassifier 모델 생성 코드

# Iris_LabelSpreadingClassifier.py
# 이 코드는 붓꽃 데이터 세트에서 LabelSpreading Classifier 모델을 학습하고 이를 ONNX 형식으로 내보내고 ONNX 모델을 사용하여 예측하는 과정을 보여 줍니다.
# 또한 원래 모델과 ONNX 모델 모두의 정확도를 평가합니다.
# 저작권 2023, MetaQuotes Ltd.


# 필요한 라이브러리 가져오기
sklearn에서 데이터 세트 import
sklearn.semi_supervised에서 LabelSpreading import
sklearn.metrics에서 accuracy_score, classification_report import
skl2onnx에서 convert_sklearn import
skl2onnx.common.data_types에서 FloatTensorType import
import onnxruntime as ort
import numpy as np
import sys

# 스크립트 경로 가져오기
script_path = sys.argv[0]
last_index = script_path.rfind("\\")+ 1
data_path = script_path[0:last_index]

# 붓꽃 데이터 세트 로드
iris = datasets.load_iris()
X = iris.data
y = iris.target

# LabelSpreading 모델 생성
ls_model = LabelSpreading()

# 전체 데이터 세트에서 모델 훈련하기
ls_model.fit(X, y)

# 전체 데이터 집합에 대한 클래스 예측
y_pred = ls_model.predict(X)

# 모델의 정확도 평가
accuracy = accuracy_score(y, y_pred)
print("Accuracy of LabelSpreading model:", accuracy)

# 분류 보고서 표시
print("\nClassification Report:\n", classification_report(y, y_pred))

# 입력 데이터 유형 정의
initial_type = [(('float_input', FloatTensorType([None, X.shape[1]]))]

# float 데이터 유형을 사용하여 모델을 ONNX 형식으로 내보내기
onnx_model = convert_sklearn(ls_model, initial_types=initial_type, target_opset=12)

# 파일에 모델 저장
onnx_filename = data_path + "ls_iris.onnx"
with open(onnx_filename, "wb") as f:
    f.write(onnx_model.SerializeToString())

# 모델 경로를 인쇄합니다.
print(f"Model saved to {onnx_filename}")

# ONNX 모델을 로드하고 예측을 수행합니다.
onnx_session = ort.InferenceSession(onnx_filename)
input_name = onnx_session.get_inputs()[0].name
output_name = onnx_session.get_outputs()[0].name

# 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}")

# ONNX에서 출력 텐서에 대한 정보를 표시합니다.
print("\nInformation about output tensors in ONNX:")
for i, enumerate(onnx_session.get_outputs())의 output_tensor:
    print(f"{i + 1}. Name: {output_tensor.name}, Data Type: {output_tensor.type}, Shape: {output_tensor.shape}")

# 데이터를 부동 소수점 형식(float32)으로 변환합니다.
X_float32 = X.astype(np.float32)

# ONNX를 사용하여 전체 데이터 세트의 클래스 예측하기
y_pred_onnx = onnx_session.run([output_name], {input_name: X_float32})[0]

# ONNX 모델의 정확도를 평가합니다.
accuracy_onnx = accuracy_score(y, y_pred_onnx)
print("\nAccuracy of LabelSpreading model in ONNX format:", accuracy_onnx)

출력:

Python LabelSpreading 모델의 정확도: 1.0
파이썬    
파이썬 분류 보고서:
파이썬 정밀 리콜 f1-점수 지원
파이썬    
파이썬 0 1.00 1.00 1.00 50
파이썬 1 1.00 1.00 1.00 50
파이썬 2 1.00 1.00 1.00 50
파이썬    
파이썬 정확도 1.00 150
파이썬 매크로 평균 1.00 1.00 1.00 150
파이썬 가중 평균 1.00 1.00 1.00 150
파이썬   

'오류' 탭에는 ONNX 형식으로의 변환 오류와 관련된 메시지가 표시됩니다:

    onnx_model = convert_sklearn(ls_model, initial_types=initial_type, target_opset=12) Iris_LabelSpreading.py 45 1
    onnx_model = convert_topology( convert.py 208 1
    topology.convert_operators(container=container, verbose=verbose)    _topology.py    1532    1
    self.call_shape_calculator(operator)    _topology.py    1348    1
    operator.infer_types() _topology.py 1163 1
    raise MissingShapeCalculator( _topology.py 629 1
skl2onnx.common.exceptions.MissingShapeCalculator: '<class 'sklearn.semi_supervised._label_propagation.LabelSpreading'>' 유형에 대한 도형 계산기를 찾을 수 없습니다. _topology.py 629 1
이는 일반적으로 변환 중인 파이프라인이 _topology.py를 포함하고 있다는 것을 의미합니다 629 1
변환기 또는 해당 변환기가 없는 예측자 _topology.py 629 1
sklearn-onnx에서 구현 만약 변환이 구현된 경우 _topology.py 629 1
여러분은 다른 라이브러리에 _topology.py를 등록해야 합니다. 629 1
sklearn-onnx (function_topology.py에서 사용될 수 있도록 변환됩니다(함수 _topology.py 629 1
update_registered_converter). 모델이 아직 다루지 않은 경우 _topology.py 629 1
여러분은 sklearn-onnx로 _topology.py에 문제를 제기할 수 있습니다. 629 1
https://github.com/onnx/sklearn-onnx/issues _topology.py 629 1
변환기를 구현하거나 _topology.py에 기여할 수 있습니다. 629 1
프로젝트. 모델이 사용자 지정 모델인 경우 새 변환기는 _topology.py이어야 합니다. 629 1
구현해야 합니다. 예제는 _topology.py 629 1 갤러리에서 찾을 수 있습니다.
Iris_LabelSpreading.py finished in 2032 ms        19    1

LabelPropagation 분류기 모델을 ONNX로 변환할 수 없습니다.


2.29.5. NearestCentroid 분류기

NearestCentroid는 각 클래스의 중심을 결정하고 가장 가까운 중심을 기준으로 객체를 분류하는 아이디어에 기반한 분류 메서드입니다. 이 메서드는다중 클래스 문제에 적합하며 선형적으로 분리 가능한 클래스가 있는 데이터 집합에서 잘 작동합니다.

NearestCentroid 프로세스:

  1. 각 클래스에 대해 중심값이 계산되며 이 중심값은 해당 클래스에 속한 모든 객체의 피처의 평균값을 나타냅니다. 이는 해당 클래스의 객체에 대한 각 피처의 평균값을 계산하여 수행할 수 있습니다.
  2. 새로운 객체를 분류할 때 모든 클래스의 구심점 중에서 가장 가까운 구심점이 계산됩니다.
  3. 새 객체는 미터법 공간에서 중심이 가장 가까운 클래스에 할당됩니다.

NearestCentroid의 장점:

  • 간편함과 속도: NearestCentroid는 계산적으로 간단한 메서드이며 대규모 데이터 세트에서 빠르게 작동하며
  • 선형적으로 분리 가능한 클래스에 적합합니다: 이 메서드는 클래스를 선형적으로 분리할 수 있거나 선형적으로 분리할 수 있는 것에 근사한 작업에서 잘 수행되며
  • 멀티 클래스 문제에 효과적입니다: NearestCentroid는 다중 클래스 문제에 적합하며 앙상블에서 기본 분류기로 사용될 수 있습니다.

NearestCentroid의 한계:

  • 이상값에 대한 민감도: 이상값이 있으면 중심값이 크게 왜곡될 수 있으므로 NearestCentroid 메서드는 데이터의 이상값에 민감합니다.
  • 공간 편향: 데이터의 클래스가 분산과 모양이 다른 경우 NearestCentroid 메서드가 덜 효율적으로 작동할 수 있습니다.
  • 동등한 수단을 가정합니다: 이 메서드은 실제 데이터에서는 항상 일치하지 않을 수 있는 클래스의 피처 수단이 거의 동일하다고 가정하며
  • 비선형 작업에는 적합하지 않습니다: NearestCentroid는 클래스 간 비선형 경계가 있는 작업에는 적합하지 않습니다.

NearestCentroid는 간단하고 해석 가능한 분류 메서드으로 특히 클래스가 선형적으로 분리 가능하고 데이터에 이상값이 없을 때 특정 시나리오에서 유용할 수 있습니다.


2.29.5.1. NearestCentroid 모델 생성 코드

# Iris_NearestCentroidClassifier.py
# 이 코드는 Iris 데이터 세트에서 NearestCentroid 분류기 모델을 학습하고 이를 ONNX 형식으로 내보내고 ONNX 모델을 사용하여 예측하는 과정을 보여줍니다.
# 또한 원래 모델과 ONNX 모델 모두의 정확도를 평가합니다.
# Copyright 2023, MetaQuotes Ltd.
# https://www.mql5.com

# 필요한 라이브러리 가져오기
sklearn에서 데이터 세트 import
sklearn.neighbors에서 NearestCentroid import
sklearn.metrics에서 accuracy_score, classification_report import
skl2onnx에서 convert_sklearn import
skl2onnx.common.data_types에서 FloatTensorType import
import onnxruntime as ort
import numpy as np
import sys

# 스크립트 경로 가져오기
script_path = sys.argv[0]
last_index = script_path.rfind("\\")+ 1
data_path = script_path[0:last_index]

# 붓꽃 데이터 세트 로드
iris = datasets.load_iris()
X = iris.data
y = iris.target

# NearestCentroid 모델 생성
nc_model = NearestCentroid()

# 전체 데이터 세트에서 모델 훈련하기
nc_model.fit(X, y)

# 전체 데이터 집합에 대한 클래스 예측
y_pred = nc_model.predict(X)

# 모델의 정확도 평가
accuracy = accuracy_score(y, y_pred)
print("Accuracy of NearestCentroid model:", accuracy)

# 분류 보고서 표시
print("\nClassification Report:\n", classification_report(y, y_pred))

# 입력 데이터 유형 정의
initial_type = [(('float_input', FloatTensorType([None, X.shape[1]]))]

# float 데이터 유형을 사용하여 모델을 ONNX 형식으로 내보내기
onnx_model = convert_sklearn(nc_model, initial_types=initial_type, target_opset=12)

# 파일에 모델 저장
onnx_filename = data_path + "nc_iris.onnx"
with open(onnx_filename, "wb") as f:
    f.write(onnx_model.SerializeToString())

# 모델 경로를 인쇄합니다.
print(f"Model saved to {onnx_filename}")

# ONNX 모델을 로드하고 예측을 수행합니다.
onnx_session = ort.InferenceSession(onnx_filename)
input_name = onnx_session.get_inputs()[0].name
output_name = onnx_session.get_outputs()[0].name

# 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}")

# ONNX에서 출력 텐서에 대한 정보를 표시합니다.
print("\nInformation about output tensors in ONNX:")
for i, enumerate(onnx_session.get_outputs())의 output_tensor:
    print(f"{i + 1}. Name: {output_tensor.name}, Data Type: {output_tensor.type}, Shape: {output_tensor.shape}")

# 데이터를 부동 소수점 형식(float32)으로 변환합니다.
X_float32 = X.astype(np.float32)

# ONNX를 사용하여 전체 데이터 세트의 클래스 예측하기
y_pred_onnx = onnx_session.run([output_name], {input_name: X_float32})[0]

# ONNX 모델의 정확도를 평가합니다.
accuracy_onnx = accuracy_score(y, y_pred_onnx)
print("\nAccuracy of NearestCentroid model in ONNX format:", accuracy_onnx)

출력:

Python NearestCentroid 모델의 정확도: 0.9266666666666666
파이썬    
파이썬 분류 보고서:
파이썬 정밀 리콜 f1-점수 지원
파이썬    
파이썬 0 1.00 1.00 1.00 50
파이썬 1 0.87 0.92 0.89 50
파이썬 2 0.91 0.86 0.89 50
파이썬    
파이썬 정확도 0.93 150
파이썬 매크로 평균 0.93 0.93 0.93 150
파이썬 가중 평균 0.93 0.93 0.93 150
파이썬   

'오류' 탭에는 ONNX 형식으로의 변환 오류와 관련된 메시지가 표시됩니다:

    onnx_model = convert_sklearn(nc_model, initial_types=initial_type, target_opset=12) Iris_NearestCentroid.py 45 1
    onnx_model = convert_topology( convert.py 208 1
    topology.convert_operators(container=container, verbose=verbose)    _topology.py    1532    1
    self.call_shape_calculator(operator)    _topology.py    1348    1
    operator.infer_types() _topology.py 1163 1
    raise MissingShapeCalculator( _topology.py 629 1
skl2onnx.common.exceptions.MissingShapeCalculator: '<class 'sklearn.neighors._nearest_centroid.NearestCentroid'>' 유형에 대한 도형 계산기를 찾을 수 없습니다. _topology.py 629 1
이는 일반적으로 변환 중인 파이프라인이 _topology.py를 포함하고 있다는 것을 의미합니다 629 1
변환기 또는 해당 변환기가 없는 예측자 _topology.py 629 1
sklearn-onnx에서 구현 만약 변환이 구현된 경우 _topology.py 629 1
여러분은 다른 라이브러리에 _topology.py를 등록해야 합니다. 629 1
sklearn-onnx (function_topology.py에서 사용될 수 있도록 변환됩니다(함수 _topology.py 629 1
update_registered_converter). 모델이 아직 다루지 않은 경우 _topology.py 629 1
여러분은 sklearn-onnx로 _topology.py에 문제를 제기할 수 있습니다. 629 1
https://github.com/onnx/sklearn-onnx/issues _topology.py 629 1
변환기를 구현하거나 _topology.py에 기여할 수 있습니다. 629 1
프로젝트. 모델이 사용자 지정 모델인 경우 새 변환기는 _topology.py이어야 합니다. 629 1
구현해야 합니다. 예제는 _topology.py 629 1 갤러리에서 찾을 수 있습니다.
2131ms 초만에 Iris_NearestCentroid.py 완료 19 1
NearestCentroid 분류기 모델을 ONNX로 변환할 수 없습니다.


2.29.6. Quadratic Discriminant Analysis 분류기

이차 판별 분석(Quadratic Discriminant Analysis; QDA)은 확률적 모델을 사용하여 데이터를 등급으로 구분하는 분류 메서드입니다. 이는 선형 판별 분석(Linear Discriminant Analysis; LDA)을 일반화한 것으로 각 클래스 내에서 피처 공분산을 고려할 수 있게 합니다. QDA의 주요 원리는 각 클래스에 대한 피처 분포를 모델링한 다음 이 분포를 사용하여 새로운 객체를 분류하는 것입니다.

QDA 프로세스:

  1. 분포의 매개변수는 피처의 평균 및 공분산 행렬과 같은 각 클래스에 대해 계산됩니다. 이러한 매개변수는 각 클래스에 대한 학습 데이터를 기반으로 추정됩니다.
  2. 얻은 파라미터를 사용하여 다변량 정규 분포(또는 이차 분포 함수)를 사용하여 각 클래스에 대한 확률 밀도가 계산될 수 있습니다.
  3. 새로운 객체를 분류할 때 각 클래스에 대해 확률 밀도 값을 계산하여 확률이 가장 높은 클래스에 객체를 할당합니다.

QDA의 장점:

  • 피처 공변량을 고려합니다: QDA는 클래스별로 서로 다른 공분산 행렬을 허용하고 이를 통해 다양한 데이터 구조에 더 잘 적응할 수 있기 때문에 LDA보다 유연합니다.
  • 비선형 경계에 적합합니다: QDA는 클래스 간의 복잡하고 비선형적인 경계를 모델링할 수 있습니다.
  • 불균형한 데이터에 강합니다: QDA는 클래스가 불균형한 작업에서 우수한 성능을 발휘할 수 있습니다.

QDA의 한계:

  • 계산의 복잡성: QDA는 공분산 행렬을 포함하여 각 클래스에 대한 파라미터를 추정해야 하므로 대규모 데이터 세트에서는 계산 비용이 많이 들 수 있습니다.
  • 제한된 데이터: 데이터가 제한되어 있고 매개변수 추정의 정확도가 떨어지면 QDA의 효율성이 떨어질 수 있습니다.
  • 정규 분포의 가정: QDA는 데이터가 정규 분포를 따른다고 가정하며 이는 일부 유형의 데이터는 해당되지 않을 수 있습니다.
  • 과적합 위험: 학습 데이터가 충분하지 않거나 피처 공분산이 강한 경우 QDA는 과적합 문제에 직면할 수 있습니다.

QDA는 다양한 데이터 유형에 적합하고 클래스 내 피처 공분산을 고려할 수 있는 강력한 분류 메서드입니다. 하지만 사용 시 고려해야 할 제한 사항도 있습니다.

2.29.6.1. QDA 모델을 만들기 위한 코드

# Iris_QuadraticDiscriminantAnalysisClassifier.py
# 이 코드는 Iris 데이터 세트에서 이차 판별 분석 분류기 모델을 학습하고 이를 ONNX 형식으로 내보내고 ONNX 모델을 사용하여 예측하는 과정을 보여줍니다.
# 또한 원래 모델과 ONNX 모델 모두의 정확도를 평가합니다.
# Copyright 2023, MetaQuotes Ltd.
# https://www.mql5.com

# 필요한 라이브러리 가져오기
sklearn에서 datasets import
sklearn.discriminant_analysis에서 QuadraticDiscriminantAnalysis import
sklearn.metrics에서 accuracy_score, classification_report import
skl2onnx에서 convert_sklearn import
skl2onnx.common.data_types에서 FloatTensorType import
import onnxruntime as ort
import numpy as np
import sys

# 스크립트 경로 가져오기
script_path = sys.argv[0]
last_index = script_path.rfind("\\")+ 1
data_path = script_path[0:last_index]

# 붓꽃 데이터 세트 로드
iris = datasets.load_iris()
X = iris.data
y = iris.target

# QuadraticDiscriminantAnalysis 모델 만들기
qda_model = QuadraticDiscriminantAnalysis()

# 전체 데이터 세트에서 모델 훈련하기
qda_model.fit(X, y)

# 전체 데이터 집합에 대한 클래스 예측
y_pred = qda_model.predict(X)

# 모델의 정확도 평가
accuracy = accuracy_score(y, y_pred)
print("Accuracy of Quadratic Discriminant Analysis model:", accuracy)

# 분류 보고서 표시
print("\nClassification Report:\n", classification_report(y, y_pred))

# 입력 데이터 유형 정의
initial_type = [(('float_input', FloatTensorType([None, X.shape[1]]))]

# float 데이터 유형을 사용하여 모델을 ONNX 형식으로 내보내기
onnx_model = convert_sklearn(qda_model, initial_types=initial_type, target_opset=12)

# 파일에 모델 저장
onnx_filename = data_path + "qda_iris.onnx"
with open(onnx_filename, "wb") as f:
    f.write(onnx_model.SerializeToString())

# 모델 경로를 인쇄합니다.
print(f"Model saved to {onnx_filename}")

# ONNX 모델을 로드하고 예측을 수행합니다.
onnx_session = ort.InferenceSession(onnx_filename)
input_name = onnx_session.get_inputs()[0].name
output_name = onnx_session.get_outputs()[0].name

# 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}")

# ONNX에서 출력 텐서에 대한 정보를 표시합니다.
print("\nInformation about output tensors in ONNX:")
for i, enumerate(onnx_session.get_outputs())의 output_tensor:
    print(f"{i + 1}. Name: {output_tensor.name}, Data Type: {output_tensor.type}, Shape: {output_tensor.shape}")

# 데이터를 부동 소수점 형식(float32)으로 변환합니다.
X_float32 = X.astype(np.float32)

# ONNX를 사용하여 전체 데이터 세트의 클래스 예측하기
y_pred_onnx = onnx_session.run([output_name], {input_name: X_float32})[0]

# ONNX 모델의 정확도를 평가합니다.
accuracy_onnx = accuracy_score(y, y_pred_onnx)
print("\nAccuracy of Quadratic Discriminant Analysis model in ONNX format:", accuracy_onnx)

출력:

Python QDA 모델의 정확도: 0.98
파이썬    
파이썬 분류 보고서:
파이썬 정밀 리콜 f1-점수 지원
파이썬    
파이썬 0 1.00 1.00 1.00 50
파이썬 1 0.98 0.96 0.97 50
파이썬 2 0.96 0.98 0.97 50
파이썬    
파이썬 정확도 0.98 150
파이썬 매크로 평균 0.98 0.98 0.98 150
파이썬 가중 평균 0.98 0.98 0.98 150
파이썬    
파이썬 모델은 C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\qda_iris.onnx에 저장됩니다.

이번에는 모델이 ONNX 형식으로 성공적으로 저장되었습니다. 그러나 실행할 때 오류 탭에 오류가 표시됩니다:

    onnx_session = ort.InferenceSession(onnx_filename)    Iris_QuadraticDiscriminantAnalysisClassifier.py    55    1
    self._create_inference_session(providers, provider_options, disabled_optimizers) onnxruntime_inference_collection.py 383 1
    sess = C.InferenceSession(session_options, self._model_path, True, self._read_config_from_model) onnxruntime_inference_collection.py 424 1
onnxruntime.capi.onnxruntime_pybind11_state.InvalidGraph: [ONNXRuntimeError] : 10 : INVALID_GRAPH : C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\qda_iris.onnx failed: 이것은 잘못된 모드입니다 onnxruntime_inference_collection.py 424 1
2063ms 초만에 Iris_QuadraticDiscriminantAnalysisClassifier.py 완료 5 1

QDA 분류기 모델을 ONNX로 변환하는 동안 오류가 발생했습니다.


결론

이 연구에서는 Scikit-learn 라이브러리 버전 1.2.2를 활용하여 Iris 데이터 세트를 사용하여 33개의 분류 모델에 대한 연구를 수행했습니다.

1. 이 세트에서 6개의 모델이 ONNX 형식으로 변환할 때 어려움을 겪었습니다:

  1. DummyClassifier: 더미 분류기;
  2. GaussianProcessClassifier:  가우시안 프로세스 분류기;
  3. LabelPropagation : 레이블 전파 분류기 ;
  4. LabelSpreading : 레이블 확산 분류기;
  5. NearestCentroid: 가장 가까운 중심 분류기;
  6. QuadraticDiscriminantAnalysis: 이차 판별 분석 분류기.

이러한 모델은 구조 및 로직 측면에서 더 복잡한 것으로 보이며 ONNX 형식에 맞게 조정하려면 추가적인 노력이 필요할 수 있으며 ONNX 형식에 완전히 지원되지 않거나 적합하지 않은 특정 데이터 구조 또는 알고리즘을 사용할 수도 있습니다.

2. 나머지 27개 모델은 ONNX 포맷으로 성공적으로 변환되었으며 정확도가 유지되는 것으로 입증되었습니다. 이는 머신러닝 모델을 저장하고 복원하는 도구로서 ONNX의 효율성과 성능을 유지하면서 서로 다른 환경과 애플리케이션 간에 모델을 쉽게 전송할 수 있다는 것을 보여주는 것입니다.

ONNX 형식으로 성공적으로 변환된 전체 모델 목록은 다음과 같습니다:

  1. SVC: 지원 벡터 분류기;
  2. LinearSVC: 선형 지원 벡터 분류기;
  3. NuSVC: Nu 지원 벡터 분류기;
  4. AdaBoostClassifier: 적응형 부스팅 분류기;
  5. BaggingClassifier: 부트스트랩 집계 분류기;
  6. BernoulliNB: 베르누이 나이브 베이즈 분류기;
  7. CategoricalNB: 범주형 나이브 베이즈 분류기;
  8. ComplementNB: 보간 나이브 베이즈 분류기;
  9. DecisionTreeClassifier: 의사 결정 트리 분류기;
  10. ExtraTreeClassifier: Extra Tree Classifier;
  11. ExtraTreesClassifier: Extra Trees Classifier;
  12. GaussianNB: 가우시안 나이브 베이즈 분류기;
  13. GradientBoostingClassifier: 경사 부스팅 분류기;
  14. HistGradientBoostingClassifier: 히스토그램 기반 경사 부스팅 분류기;
  15. KNeighborsClassifier: K-최근접 이웃 분류기;
  16. LinearDiscriminantAnalysis: 선형 판별 분석 분류기;
  17. LogisticRegression: 로지스틱 회귀 분류기;
  18. LogisticRegressionCV: 교차 검증 기능이 있는 로지스틱 회귀 분류기;
  19. MLPClassifier: 멀티 레이어 퍼셉트론 분류기;
  20. MultinomialNB: 다항식 나이브 베이즈 분류기;
  21. PassiveAggressiveClassifier: 패시브-어그레시브 분류기;
  22. Perceptron: 퍼셉트론 분류기;
  23. RadiusNeighborsClassifier: 반경 이웃 분류기;
  24. RandomForestClassifier: 랜덤 숲 분류기;
  25. RidgeClassifier: 릿지 분류기;
  26. RidgeClassifierCV: 교차 검증 기능이 있는 릿지 분류기;
  27. SGDClassifier: 스토캐스틱 경사 하강 분류기.

3. 또한 연구 과정에서 붓꽃 데이터 세트에서 뛰어난 분류 성능을 보인 모델도 확인했습니다. Random Forest Classifier, Gradient Boosting Classifier, Bagging Classifier, Decision Tree Classifier, Extra Tree Classifier, Extra Trees Classifier, and Hist Gradient Boosting Classifier 등의 분류 모델은 완벽한 예측 정확도를 달성했습니다. 이는 이들이 각 붓꽃 샘플이 속한 클래스를 정확하게 결정할 수 있다는 것을 의미합니다.

이러한 결과는 특정 분류 작업에 가장 적합한 모델을 선택할 때 특히 유용할 수 있습니다. 붓꽃 데이터에서 완벽한 정확도를 달성한 모델은 유사한 데이터의 분석 또는 분류와 관련된 작업시 탁월한 선택이 될 수 있습니다.

이와 같이 이 연구는 특정 작업에 적합한 모델을 선택하는 것의 중요성과 분류 작업에 머신러닝 모델을 보존하고 적용하는 데 ONNX를 사용할 때의 이점을 강조합니다.


결론

이 글에서 우리는 Scikit-learn 버전 1.2.2로 Iris 데이터 세트를 사용하여 33개의 분류 모델을 분석했습니다.

저희가 조사한 모든 모델 중 6개 모델은 ONNX 형식으로 변환하기 어려운 것으로 나타났습니다. 이러한 모델에는 Dummy Classifier, Gaussian Process Classifier, Label Propagation Classifier, Label Spreading Classifier, Nearest Centroid Classifier, and Quadratic Discriminant Analysis Classifier가 포함됩니다. 복잡한 구조나 로직의 경우 ONNX 형식으로 성공적으로 변환하기 위해 추가적인 조정이 필요할 수 있습니다.

나머지 27개 모델은 ONNX 포맷으로 성공적으로 변환되었으며 정확도가 유지되는 것으로 입증되었습니다. 이를 통해 우리는 머신 러닝 모델을 보존 및 복원하고 모델 성능을 유지하면서 이식성을 보장하는 ONNX의 효율성을 재확인할 수 있습니다.

특히 Random Forest Classifier, Gradient Boosting Classifier, Bagging Classifier, Decision Tree Classifier, Extra Tree Classifier, Extra Trees Classifier, and Hist Gradient Boosting Classifier와 같은 모델은 붓꽃 데이터를 분류하는 데 완벽한 정확도를 달성했습니다. 이러한 모델은 높은 정확도가 중요한 작업에 특히 유용할 수 있습니다.

이 연구는 특정한 작업에 대해 적합한 모델을 선택하는 것의 중요성을 강조하고 분류 작업에서 머신러닝 모델을 보존하고 적용하는 데 ONNX를 사용하면 얻을 수 있는 이점을 보여줍니다.

이 문서의 모든 스크립트는 퍼블릭 프로젝트 "MQL5\Shared Projects\Scikit.Classification.ONNX."에서도 확인할 수 있습니다.

MetaQuotes 소프트웨어 사를 통해 러시아어가 번역됨.
원본 기고글: https://www.mql5.com/ru/articles/13451

새로운 기능: MQL5의 커스텀 인디케이터 새로운 기능: MQL5의 커스텀 인디케이터
MetaTrader5와 MQL5의 새로운 기능 전체를 나열하지는 않겠습니다. 종류도 많은 데다가, 별도의 설명이 필요한 기능들도 있거든요. 객체 지향 프로그래밍을 이용한 코드 작성법 또한 다음에 알아보도록 하겠습니다. 다른 기능들과 함께 설명하기에는 조금 어려운 이야기일 수 있으니까요. 이 글에서는 인디케이터와 인디케이터의 구조, 드로잉 타입과 프로그래밍 디테일을 MQL4와 비교해 볼게요. 초보자 분들께 많은 도움이 되면 좋겠고 기존에 사용하시던 개발자 분들도 뭔가 새로운 걸 얻어 가실 수 있길 바랍니다.
MQL5의 ALGLIB 수치 해석 라이브러리 MQL5의 ALGLIB 수치 해석 라이브러리
이 글에서는 금융 데이터 분석의 효율성을 향상시킬 수 있는 ALGLIB 3.19 수치 분석 라이브러리와 그 응용 프로그램 및 새로운 알고리즘에 대해 간략히 살펴봅니다.
새 MetaTrader 와 MQL5를 소개해드립니다 새 MetaTrader 와 MQL5를 소개해드립니다
본 문서는 MetaTrader5의 간략 리뷰입니다. 짧은 시간 내에 시스템의 모든 세부 사항을 안내해드리기는 어렵습니다 - 테스트는 2009.09.09에 시작되었습니다. 이는 상징적인 일자로, 전 이것이 행운의 숫자가 될거라 믿어 의심치않습니다. 제가 새 MetaTrader 5 터미널과 MQL5 베타버전을 받은지 며칠이 지났습니다. 아직 모든 기능을 사용해본 것은 아니지만, 벌써부터 감명깊네요.
MQL4 및 MQL5 개발 프레임워크 내 OpenAI의 ChatGPT 기능 MQL4 및 MQL5 개발 프레임워크 내 OpenAI의 ChatGPT 기능
이 글에서는 Expert Advisors, 지표 및 스크립트를 개발하는 데 드는 시간과 노동 강도를 줄이는 측면에서 OpenAI의 ChatGPT를 살펴보고 그 기능에 대해 알아보겠습니다. 이제부터 ChatGPT를 MQL4 및 MQL5에서 프로그래밍에 올바르게 사용하는 방법을 보여 드리겠습니다.