English Русский Español Deutsch 日本語
preview
Modelos de classificação da biblioteca Scikit-learn e sua exportação para o formato ONNX

Modelos de classificação da biblioteca Scikit-learn e sua exportação para o formato ONNX

MetaTrader 5Exemplos | 21 março 2024, 12:47
108 0
MetaQuotes
MetaQuotes

    O desenvolvimento da tecnologia levou ao surgimento de uma abordagem fundamentalmente nova para construir algoritmos de processamento de dados. Anteriormente, para resolver cada problema específico, era necessária uma formulação e um desenvolvimento claros dos algoritmos correspondentes.

    No aprendizado de máquina, o computador aprende por si mesmo a encontrar as melhores maneiras de processar dados. Os modelos de aprendizado de máquina podem resolver com êxito os problemas de classificação (há um conjunto fixo de classes, encontrar as probabilidades de pertencimento de um determinado conjunto de características a cada classe) e regressão (estimar o valor numérico da variável-alvo com base em um determinado conjunto de características). Com base nestes componentes fundamentais, podem ser construídos modelos mais complexos de processamento de dados.

    Na biblioteca Scikit-learn, estão disponíveis muitas ferramentas tanto para classificação quanto para regressão. A escolha de métodos e modelos específicos depende das características dos dados, pois diferentes métodos podem ter eficiências variadas e fornecer resultados diferentes, dependendo do problema.

    No comunicado de imprensa "ONNX Runtime is now open source", afirma-se que o ONNX Runtime também suporta o perfil ONNX-ML:

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

    O perfil ONNX-ML é uma parte do ONNX, criada especificamente para modelos de aprendizado de máquina (ML). Ele é destinado à descrição e representação de vários tipos de modelos de ML, tais como modelos de classificação, regressão, agrupamento e outros, em uma forma conveniente que pode ser usada em diferentes plataformas e ambientes que suportam ONNX. O perfil ONNX-ML simplifica a transferência, implantação e execução de modelos de aprendizado de máquina, tornando-os mais acessíveis e portáveis.

    Neste artigo, exploraremos o uso de todos os modelos de classificação do pacote Scikit-learn para resolver o problema de classificação dos íris de Fisher, tentaremos convertê-los para o formato ONNX e usaremos os modelos resultantes em programas MQL5.

    Também compararemos a precisão dos modelos originais e suas versões ONNX no Iris dataset completo.


    Conteúdo



    1. Íris de Fisher

    O conjunto de dados Iris é um dos conjuntos de dados mais famosos e amplamente usados no mundo do aprendizado de máquina. Foi apresentado pela primeira vez em 1936 pelo estatístico e biólogo R.A. Fisher e desde então se tornou um conjunto de dados clássico para tarefas de classificação.

    O conjunto de dados Iris consiste em medições das sépalas e pétalas de três espécies de íris: Iris setosa, Iris virginica e Iris versicolor.

    Fig.1. Iris setosa

    Fig.1. Iris setosa


    Fig.2. Iris virginica

    Fig.2. Iris virginica


    Fig.3. Iris versicolor

    Fig.3. Iris versicolor


    O conjunto de dados Iris consiste em 150 instâncias de íris, com 50 instâncias de cada uma das três espécies. Cada instância tem quatro características numéricas (em centímetros):

    1. Comprimento da sépala (sepal length)
    2. Largura da sépala (sepal width)
    3. Comprimento da pétala (petal length)
    4. Largura da pétala (petal width)

    Cada instância também tem uma classe que indica a espécie de íris (Iris setosa, Iris virginica ou Iris versicolor). Este atributo de classificação torna o conjunto de dados Iris ideal para tarefas de aprendizado de máquina, como classificação e agrupamento.

    O MetaEditor permite trabalhar com scripts em Python. Para criar um script Python, é necessário selecionar "Novo" no menu "Arquivo" do MetaEditor. Aparece a janela de diálogo para selecionar o objeto a ser criado (Fig. 4).

    Criando um script Python no Assistente MQL5 - Etapa 1

    Fig.4. Criando um script Python no Assistente MQL5 - Etapa 1

    Em seguida, é necessário nomear o script, por exemplo, IRIS.py

    Criando um script Python no Assistente MQL5 - Etapa 2 - Nome do script

    Fig.5. Criando um script Python no Assistente MQL5 - Etapa 2 - Nome do script

    Após isso, pode-se indicar quais bibliotecas serão utilizadas, no nosso caso deixaremos esses campos em branco.

    Criando um script Python no Assistente MQL5 - Etapa 3

    Fig.6. Criando um script Python no Assistente MQL5 - Etapa 3


    Uma das maneiras de começar a análise do Iris dataset é visualizar os dados. A representação gráfica nos permite entender melhor a estrutura dos dados e as relações entre as características.

    Por exemplo, pode-se criar um gráfico de dispersão (scatter plot) para ver como as diferentes espécies de íris estão distribuídas no espaço de características.

    Código do script 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')

    Para executar este script, é necessário copiá-lo no MetaEditor (Fig.7) e pressionar "Compilar".

    Script IRIS.py no MetaEditor

    Fig.7. Script IRIS.py no MetaEditor


    Depois disso, os gráficos serão exibidos na tela:

    Script IRIS.py no MetaEditor com o gráfico Sepal Length/Sepal Width

    Fig.8. Script IRIS.py no MetaEditor com o gráfico Sepal Length/Sepal Width


    Fig.9. Script IRIS.py no MetaEditor com o gráfico Petal Length/Petal Width

    Fig.9. Script IRIS.py no MetaEditor com o gráfico Petal Length/Petal Width


    Vamos examiná-los em detalhes.

    Fig. 10. Diagrama de dispersão para o comprimento e largura da sépala (Scatter Plot Sepal Length vs Sepal Width)

    Fig. 10. Diagrama de dispersão para o comprimento e largura da sépala (Scatter Plot Sepal Length vs Sepal Width)



    Neste diagrama, vemos como as diferentes espécies de íris estão distribuídas pelo comprimento e largura da sépala. Podemos ver que o Iris setosa geralmente tem sépalas mais curtas e largas do que as outras duas espécies.

    Fig.11. Diagrama de dispersão para o comprimento e largura da pétala (Scatter Plot Petal Length vs Petal Width)

    Fig.11. Diagrama de dispersão para o comprimento e largura da pétala (Scatter Plot Petal Length vs Petal Width)



    Neste diagrama, vemos como as diferentes espécies de íris estão distribuídas pelo comprimento e largura da pétala. Podemos notar que o Iris setosa tem as pétalas mais curtas e estreitas, o Iris virginica tem as pétalas mais longas e largas, e o Iris versicolor está entre eles.

    O Iris dataset é um conjunto de dados ideal para treinamento e teste de modelos de aprendizado de máquina. Vamos usá-lo para analisar o desempenho dos modelos de aprendizado de máquina para a tarefa de classificação.



    2. Modelos para classificação

    A tarefa de classificação é uma das principais tarefas do aprendizado de máquina, e seu objetivo é dividir os dados em diferentes categorias ou classes com base em algumas características.

    Vamos considerar os principais modelos de aprendizado de máquina do pacote scikit-learn.


    Lista de classificadores do pacote Scikit-learn

    Para exibir a lista de classificadores disponíveis do Scikit-learn, pode-se usar o script:

    # 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}")
    

    Resultado:

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

    Por praticidade, nesta lista de classificadores, eles são destacados em diferentes cores. Modelos que requerem classificadores base são destacados em amarelo, enquanto os demais modelos podem ser usados independentemente.

    Adiantando-nos, notamos que os modelos marcados em verde são os que foram exportados com sucesso para o formato ONNX, enquanto os modelos marcados em vermelho são aqueles que, na versão atual do scikit-learn 1.2.2, apresentam erros ao serem convertidos.


    Diferentes representações de dados de saída existentes nos modelos

    Deve-se notar que diferentes modelos representam as informações de saída de maneiras variadas, portanto, ao trabalhar com modelos convertidos para ONNX, é preciso ter cuidado.

    Para a tarefa de classificação dos íris de Fisher, os tensores de entrada têm a mesma forma para todos esses modelos:

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

    Os tensores de saída dos modelos ONNX variam.

    1. Modelos que não requerem pós-processamento

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

    Eles retornam o resultado (número da classe) de forma explícita no primeiro tensor de saída inteiro label tensor(int64), não requerendo pós-processamento.

    2. Modelos cujos resultados requerem pós-processamento:

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

    Na saída, retornam uma lista de classes e as probabilidades de pertencer a cada classe.

    Para obter o resultado nestes casos, é necessário um pós-processamento do tipo seq(map(int64,tensor(float) (encontrando o elemento com a maior probabilidade).

    Bem, é preciso ter cuidado e considerar esses pontos ao trabalhar com modelos ONNX. Um exemplo de tratamento diferenciado dos resultados é apresentado no script em 2.28.2.1.


    iris.mqh

    Para testar os modelos no conjunto completo de dados iris no MQL5, será necessário formar os dados do conjunto, para isso será usada a função PrepareIrisDataset().

    É conveniente separar essas funções no arquivo 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);
      }
    //+------------------------------------------------------------------+
    


    Nota sobre os métodos de classificação: SVC, LinearSVC e NuSVC

    Vamos comparar três métodos populares de classificação: classificação de vetores de suporte (SVC), classificação de vetores de suporte linear (LinearSVC) e classificação de vetores de suporte Nu (NuSVC).

    Princípios de funcionamento:

    1. SVC (Support Vector Classification)
      Como funciona: O SVC é um método de classificação baseado na maximização da margem entre as classes. Ele busca a hiperplano separador ótimo, que maximiza a separação entre as classes e suporta os vetores de suporte - pontos mais próximos ao hiperplano.
      Funções de kernel:  O SVC pode usar diferentes funções de kernel, tais como linear, função de base radial (RBF), polinomial, e outras. A função de kernel determina como os dados são transformados para encontrar o hiperplano ótimo.

    2. LinearSVC (Linear Support Vector Classification)
      Como funciona: LinearSVC é uma variante do SVC que se especializa em classificação linear. Ele busca o hiperplano separador linear ótimo, sem usar funções de kernel. Isso o torna mais rápido e mais eficiente para lidar com grandes volumes de dados.

    3. NuSVC (Nu Support Vector Classification)
      Como funciona: O NuSVC também é baseado no método de vetores de suporte, mas introduz o parâmetro Nu (nu), que controla a complexidade do modelo e a fração dos vetores de suporte. O valor de Nu está no intervalo de 0 a 1 e determina qual fração dos dados pode ser usada para vetores de suporte e erros.

    Vantagens:

    1. SVC
      Algoritmo poderoso: O SVC pode lidar com tarefas complexas de classificação e trabalhar com dados não lineares graças ao uso de funções de kernel.
      Resistência a valores atípicos: O SVC é resistente a valores atípicos nos dados, pois usa vetores de suporte para construir o hiperplano separador.

    2. LinearSVC
      Alta eficiência: O LinearSVC é mais rápido e eficiente ao lidar com grandes volumes de dados, especialmente quando há muitos dados e o hiperplano separador linear é adequado para a tarefa.
      Classificação linear: Se a tarefa for bem separável linearmente, o LinearSVC pode oferecer bons resultados sem a necessidade de usar funções de kernel complexas.

    3. NuSVC
      Controle da complexidade do modelo: O parâmetro Nu no NuSVC permite controlar a complexidade do modelo e o equilíbrio entre o ajuste dos dados e a capacidade de generalização.
      Resistência a valores atípicos: Assim como o SVC, o NuSVC é resistente a valores atípicos, o que o torna útil para tarefas com dados imprecisos.

    Limitações:

    1. SVC
      Complexidade computacional: O SVC pode ser lento em grandes volumes de dados e/ou ao usar funções de kernel complexas.
      Sensibilidade à escolha do kernel: Escolher a função de kernel correta pode ser uma tarefa difícil e pode afetar significativamente o desempenho do modelo.

    2. LinearSVC
      Limitação para linearidade: O LinearSVC está limitado à separação linear dos dados e pode apresentar resultados pobres em caso de relações não lineares entre as características e a variável alvo.

    3. NuSVC
      Ajuste do parâmetro Nu: Ajustar o parâmetro Nu pode exigir tempo e experimentação para alcançar os melhores resultados.

    Dependendo das características da tarefa e do volume de dados, cada um desses métodos pode ser a melhor escolha. É importante realizar experimentos e escolher o método que melhor atenda aos requisitos específicos da tarefa de classificação.


    2.1. SVC Classifier

    O método de classificação Support Vector Classification (SVC) é um poderoso algoritmo de aprendizado de máquina amplamente utilizado para resolver tarefas de classificação.

    Princípios de funcionamento:

    1. Busca do hiperplano separador ótimo
      Como funciona: A ideia principal do SVC é encontrar o hiperplano separador ótimo no espaço de características. Esse hiperplano deve maximizar a separação entre os objetos de classes diferentes e sustentar os vetores de suporte - pontos de dados mais próximos ao hiperplano.
      Maximização da margem: O SVC visa maximizar a margem entre as classes, ou seja, a distância dos vetores de suporte ao hiperplano. Isso permite que o método seja resistente a valores atípicos e generalize bem para novos dados.

    2. Uso de funções de kernel
      Funções de kernel: O SVC pode usar diferentes funções de kernel, tais como linear, função de base radial (RBF), polinomial, e outras. A função de kernel permite projetar os dados em um espaço de maior dimensão, onde a tarefa se torna linear, mesmo se não houver separabilidade linear no espaço original dos dados.
      Escolha do kernel: A escolha da função de kernel correta pode afetar significativamente o desempenho do modelo SVC. Não sempre uma hiperplano linear é a solução ótima.

    Vantagens:

    • Algoritmo poderoso. Tratamento de tarefas complexas: O SVC é capaz de resolver tarefas de classificação complexas, incluindo aquelas com dependências não lineares entre características e a variável alvo.
    • Resistência a valores atípicos: O uso de vetores de suporte torna o método resistente a valores atípicos nos dados. Ele não depende de toda a amostra, mas apenas dos vetores de suporte.
    • Flexibilidade na escolha do kernel. Adaptabilidade aos dados: A capacidade de usar diferentes funções de kernel permite adaptar o método SVC aos dados específicos e procurar dependências não lineares.
    • Boa capacidade de generalização. Generalização para novos dados: O modelo SVC é capaz de generalizar para novos dados, o que o torna útil para tarefas de previsão.

    Limitações:

    • Complexidade dos cálculos. Tempo de treinamento: O SVC pode ser lento para treinar, especialmente quando usado com grandes volumes de dados ou funções de kernel complexas.
    • Escolha do kernel. Escolha da função de kernel ótima: A escolha da função de kernel correta pode exigir experimentação e depende das características dos dados.
    • Sensibilidade à escala das características. Normalização dos dados: O SVC é sensível à escala das características, portanto, recomenda-se realizar a normalização ou padronização dos dados antes do treinamento.
    • Interpretação do modelo. Dificuldade de interpretação: Os modelos SVC podem ser difíceis de interpretar devido ao uso de kernels não lineares e ao grande número de vetores de suporte.

    Dependendo da tarefa específica e do volume de dados, o método SVC pode ser uma ferramenta poderosa para resolver problemas de classificação. No entanto, é importante considerar suas limitações e ajustar os parâmetros para alcançar os melhores resultados.

    2.1.1. Código de criação do modelo SVC Classifier

    Este código demonstra o processo de treinamento do modelo SVC Classifier no conjunto de dados Iris, sua exportação para o formato ONNX e a realização de classificação usando o modelo ONNX. Também avalia a precisão tanto do modelo original quanto do modelo 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)
    

    Após executar o script no MetaEditor usando o botão "Compilar" na aba Journal, é possível ver os resultados de seu trabalho.

    Fig.12. Resultados do trabalho do script Iris_SVMClassifier.py no MetaEditor

    Fig.12. Resultados do trabalho do script Iris_SVMClassifier.py no MetaEditor

    Resultados do trabalho do script Iris_SVCClassifier.py:

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

    Aqui é possível encontrar informações sobre o caminho onde o modelo ONNX foi salvo, os tipos de parâmetros de entrada e saída do modelo ONNX, bem como a precisão da descrição do Iris dataset.

    A precisão da descrição do conjunto de dados usando o SVM Classifier é de 99%, precisão similar é mostrada pelo modelo exportado no formato ONNX.

    Agora, vamos verificar esses resultados do MQL5, executando o modelo construído para cada uma das 150 amostras de dados. Além disso, o script contém um exemplo de processamento em lote de dados.


    2.1.2. Código no MQL5 para trabalhar com o modelo SVC Classifier

    //+------------------------------------------------------------------+
    //|                                           Iris_SVCClassifier.mq5 |
    //|                                  Copyright 2023, MetaQuotes Ltd. |
    //|                                             https://www.mql5.com |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2023, MetaQuotes Ltd."
    #property link      "https://www.mql5.com"
    #property version   "1.00"
    
    #include "iris.mqh"
    #resource "svc_iris.onnx" as const uchar ExtModel[];
    
    //+------------------------------------------------------------------+
    //| Test IRIS dataset samples                                        |
    //+------------------------------------------------------------------+
    bool TestSamples(long model,float &input_data[][4], int &model_classes_id[])
      {
    //--- check number of input samples
       ulong batch_size=input_data.Range(0);
       if(batch_size==0)
          return(false);
    //--- prepare output array
       ArrayResize(model_classes_id,(int)batch_size);
    //---
       ulong input_shape[]= { batch_size, input_data.Range(1)};
       OnnxSetInputShape(model,0,input_shape);
    //---
       int output1[];
       float output2[][3];
    //---
       ArrayResize(output1,(int)batch_size);
       ArrayResize(output2,(int)batch_size);
    //---
       ulong output_shape[]= {batch_size};
       OnnxSetOutputShape(model,0,output_shape);
    //---
       ulong output_shape2[]= {batch_size,3};
       OnnxSetOutputShape(model,1,output_shape2);
    //---
       bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output1,output2);
    //--- classes are ready in output1[k];
       if(res)
         {
          for(int k=0; k<(int)batch_size; k++)
             model_classes_id[k]=output1[k];
         }
    //---
       return(res);
      }
    //+------------------------------------------------------------------+
    //| Test all samples from IRIS dataset (150)                         |
    //| Here we test all samples with batch=1, sample by sample          |
    //+------------------------------------------------------------------+
    bool TestAllIrisDataset(const long model,const string model_name,double &model_accuracy)
      {
       sIRISsample iris_samples[];
    //--- load dataset from file
       PrepareIrisDataset(iris_samples);
    //--- test
       int total_samples=ArraySize(iris_samples);
       if(total_samples==0)
         {
          Print("iris dataset not prepared");
          return(false);
         }
    //--- show dataset
       for(int k=0; k<total_samples; k++)
         {
          //PrintFormat("%d (%.2f,%.2f,%.2f,%.2f) class %d (%s)",iris_samples[k].sample_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3],iris_samples[k].class_id,iris_samples[k].class_name);
         }
    //--- array for output classes
       int model_output_classes_id[];
    //--- check all Iris dataset samples
       int correct_results=0;
       for(int k=0; k<total_samples; k++)
         {
          //--- input array
          float iris_sample_input_data[1][4];
          //--- prepare input data from kth iris sample dataset
          iris_sample_input_data[0][0]=(float)iris_samples[k].features[0];
          iris_sample_input_data[0][1]=(float)iris_samples[k].features[1];
          iris_sample_input_data[0][2]=(float)iris_samples[k].features[2];
          iris_sample_input_data[0][3]=(float)iris_samples[k].features[3];
          //--- run model
          bool res=TestSamples(model,iris_sample_input_data,model_output_classes_id);
          //--- check result
          if(res)
            {
             if(model_output_classes_id[0]==iris_samples[k].class_id)
               {
                correct_results++;
               }
             else
               {
                PrintFormat("model:%s  sample=%d FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f]",model_name,iris_samples[k].sample_id,model_output_classes_id[0],iris_samples[k].class_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3]);
               }
            }
         }
       model_accuracy=1.0*correct_results/total_samples;
    //---
       PrintFormat("model:%s   correct results: %.2f%%",model_name,100*model_accuracy);
    //---
       return(true);
      }
    
    //+------------------------------------------------------------------+
    //| Here we test batch execution of the model                        |
    //+------------------------------------------------------------------+
    bool TestBatchExecution(const long model,const string model_name,double &model_accuracy)
      {
       model_accuracy=0;
    //--- array for output classes
       int model_output_classes_id[];
       int correct_results=0;
       int total_results=0;
       bool res=false;
    
    //--- run batch with 3 samples
       float input_data_batch3[3][4]=
         {
            {5.1f,3.5f,1.4f,0.2f}, // iris dataset sample id=1, Iris-setosa
            {6.3f,2.5f,4.9f,1.5f}, // iris dataset sample id=73, Iris-versicolor
            {6.3f,2.7f,4.9f,1.8f}  // iris dataset sample id=124, Iris-virginica
         };
       int correct_classes_batch3[3]= {0,1,2};
    //--- run model
       res=TestSamples(model,input_data_batch3,model_output_classes_id);
       if(res)
         {
          //--- check result
          for(int j=0; j<ArraySize(model_output_classes_id); j++)
            {
             //--- check result
             if(model_output_classes_id[j]==correct_classes_batch3[j])
                correct_results++;
             else
               {
                PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch3[j],input_data_batch3[j][0],input_data_batch3[j][1],input_data_batch3[j][2],input_data_batch3[j][3]);
               }
             total_results++;
            }
         }
       else
          return(false);
    
    //--- run batch with 10 samples
       float input_data_batch10[10][4]=
         {
            {5.5f,3.5f,1.3f,0.2f}, // iris dataset sample id=37 (Iris-setosa)
            {4.9f,3.1f,1.5f,0.1f}, // iris dataset sample id=38 (Iris-setosa)
            {4.4f,3.0f,1.3f,0.2f}, // iris dataset sample id=39 (Iris-setosa)
            {5.0f,3.3f,1.4f,0.2f}, // iris dataset sample id=50 (Iris-setosa)
            {7.0f,3.2f,4.7f,1.4f}, // iris dataset sample id=51 (Iris-versicolor)
            {6.4f,3.2f,4.5f,1.5f}, // iris dataset sample id=52 (Iris-versicolor)
            {6.3f,3.3f,6.0f,2.5f}, // iris dataset sample id=101 (Iris-virginica)
            {5.8f,2.7f,5.1f,1.9f}, // iris dataset sample id=102 (Iris-virginica)
            {7.1f,3.0f,5.9f,2.1f}, // iris dataset sample id=103 (Iris-virginica)
            {6.3f,2.9f,5.6f,1.8f}  // iris dataset sample id=104 (Iris-virginica)
         };
    //--- correct classes for all 10 samples in the batch
       int correct_classes_batch10[10]= {0,0,0,0,1,1,2,2,2,2};
    
    //--- run model
       res=TestSamples(model,input_data_batch10,model_output_classes_id);
    //--- check result
       if(res)
         {
          for(int j=0; j<ArraySize(model_output_classes_id); j++)
            {
             if(model_output_classes_id[j]==correct_classes_batch10[j])
                correct_results++;
             else
               {
                double f1=input_data_batch10[j][0];
                double f2=input_data_batch10[j][1];
                double f3=input_data_batch10[j][2];
                double f4=input_data_batch10[j][3];
                PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch10[j],input_data_batch10[j][0],input_data_batch10[j][1],input_data_batch10[j][2],input_data_batch10[j][3]);
               }
             total_results++;
            }
         }
       else
          return(false);
    
    //--- calculate accuracy
       model_accuracy=correct_results/total_results;
    //---
       return(res);
      }
    //+------------------------------------------------------------------+
    //| Script program start function                                    |
    //+------------------------------------------------------------------+
    int OnStart(void)
      {
       string model_name="SVCClassifier";
    //---
       long model=OnnxCreateFromBuffer(ExtModel,ONNX_DEFAULT);
       if(model==INVALID_HANDLE)
         {
          PrintFormat("model_name=%s OnnxCreate error %d for",model_name,GetLastError());
         }
       else
         {
          //--- test all dataset
          double model_accuracy=0;
          //-- test sample by sample execution for all Iris dataset
          if(TestAllIrisDataset(model,model_name,model_accuracy))
             PrintFormat("model=%s all samples accuracy=%f",model_name,model_accuracy);
          else
             PrintFormat("error in testing model=%s ",model_name);
          //--- test batch execution for several samples
          if(TestBatchExecution(model,model_name,model_accuracy))
             PrintFormat("model=%s batch test accuracy=%f",model_name,model_accuracy);
          else
             PrintFormat("error in testing model=%s ",model_name);
          //--- release model
          OnnxRelease(model);
         }
       return(0);
      }
    //+------------------------------------------------------------------+
    

    Os resultados do trabalho do script são exibidos na aba "Experts" do terminal MetaTrader 5.

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

    O modelo SVC conseguiu distinguir corretamente 149 amostras de 150, o que é um resultado muito bom. O modelo cometeu apenas um erro de classificação no conjunto de dados Iris, prevendo a classe 2 (versicolor) em vez da classe 1 (virginica) para a amostra №84.

    Note-se que a precisão do modelo ONNX exportado no Iris dataset completo é de 99.33%, o que corresponde à precisão do original.


    2.1.3. Representação ONNX do modelo SVC Classifier

    O modelo ONNX construído pode ser visualizado no MetaEditor:


    Fig.13. Modelo ONNX svc_iris.onnx no MetaEditor

    Fig.13. Modelo ONNX svc_iris.onnx no MetaEditor


    Para obter mais informações sobre a arquitetura do modelo, pode-se usar o Netron; para isso, na descrição do modelo no MetaEditor, é necessário pressionar o botão "Open in Netron".


    Fig.14. Modelo ONNX svc_iris.onnx no Netron

    Fig.14. Modelo ONNX svc_iris.onnx no Netron


    Além disso, ao passar o mouse sobre os operadores ONNX presentes no modelo, pode-se obter informações sobre os parâmetros desses operadores (SVMClassifier na Fig.15).


    Fig.15. Modelo ONNX svc_iris.onnx no Netron (parâmetros do operador ONNX SVMClassifier)

    Fig.15. Modelo ONNX svc_iris.onnx no Netron (parâmetros do operador ONNX SVMClassifier)



    2.2. LinearSVC Classifier

    LinearSVC (Classificação de Vetores de Suporte Linear) é um poderoso algoritmo de aprendizado de máquina usado para tarefas de classificação binária e multiclasse. Ele é baseado na ideia de encontrar a hiperplano que melhor separa os dados.

    Princípios de funcionamento do LinearSVC:

    1. Busca do hiperplano ótimo: A ideia principal do LinearSVC é encontrar o hiperplano ótimo que maximiza a separação entre duas classes de dados. Um hiperplano é uma superfície multidimensional definida como uma equação linear.
    2. Minimização das margens: O LinearSVC visa minimizar as margens (distâncias entre os pontos de dados e o hiperplano). Quanto maiores as margens, mais confiável o hiperplano separa as classes.
    3. Trabalho com dados linearmente inseparáveis: O LinearSVC pode lidar com dados que não podem ser particionados linearmente no espaço original, por meio do uso de funções de kernel (kernel trick) que permitem que os dados sejam projetados em um espaço de dimensão mais alta, onde podem ser particionados linearmente.

    Vantagens do LinearSVC:

    • Boa capacidade de generalização: O LinearSVC tem uma boa capacidade de generalização e pode fornecer bons resultados em novos dados, não vistos anteriormente.
    • Eficiência: O LinearSVC é rápido em grandes conjuntos de dados e requer relativamente poucos recursos computacionais.
    • Trabalho com dados linearmente inseparáveis: Graças ao uso de funções de kernel, o LinearSVC pode resolver tarefas de classificação com dados linearmente inseparáveis.
    • Escalabilidade: O LinearSVC pode ser eficientemente utilizado em tarefas com um grande número de características e grandes volumes de dados.

    Limitações do LinearSVC:

    • Apenas hiperplanos separadores lineares: O LinearSVC constrói apenas hiperplanos separadores lineares, o que pode ser insuficiente para tarefas de classificação complexas com dependências não lineares.
    • Escolha de parâmetros: A escolha dos parâmetros corretos (por exemplo, o parâmetro de regularização) pode requerer conhecimento especializado ou validação cruzada.
    • Sensibilidade a valores atípicos: O LinearSVC pode ser sensível a valores atípicos nos dados, o que pode afetar a qualidade da classificação.
    • Interpretação do modelo: Modelos criados usando o LinearSVC podem ser menos interpretáveis do que alguns outros métodos.

    O LinearSVC é um algoritmo de classificação poderoso, que possui uma boa capacidade de generalização, alta eficiência e suporte para trabalhar com dados linearmente inseparáveis. Ele é aplicável em diversas tarefas de classificação, especialmente nos casos em que os dados podem ser separados por um hiperplano linear. No entanto, é importante considerar que para tarefas complexas que requerem modelagem de dependências não lineares, o LinearSVC pode ser uma escolha menos adequada, e nesses casos, deve-se considerar o uso de métodos com superfícies separadoras mais complexas.


    2.2.1. Código de criação do modelo LinearSVC Classifier

    Este código demonstra o processo de treinamento do modelo LinearSVC Classifier no conjunto de dados Iris, sua exportação para o formato ONNX e a realização de classificação usando o modelo ONNX. Também avalia a precisão tanto do modelo original quanto do modelo 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)
    

    Resultado:

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


    2.2.2. Código no MQL5 para trabalhar com o modelo LinearSVC Classifier

    //+------------------------------------------------------------------+
    //|                                               Iris_LinearSVC.mq5 |
    //|                                  Copyright 2023, MetaQuotes Ltd. |
    //|                                             https://www.mql5.com |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2023, MetaQuotes Ltd."
    #property link      "https://www.mql5.com"
    #property version   "1.00"
    
    #include "iris.mqh"
    #resource "linear_svc_iris.onnx" as const uchar ExtModel[];
    
    //+------------------------------------------------------------------+
    //| Test IRIS dataset samples                                        |
    //+------------------------------------------------------------------+
    bool TestSamples(long model,float &input_data[][4], int &model_classes_id[])
      {
    //--- check number of input samples
       ulong batch_size=input_data.Range(0);
       if(batch_size==0)
          return(false);
    //--- prepare output array
       ArrayResize(model_classes_id,(int)batch_size);
    //---
       ulong input_shape[]= { batch_size, input_data.Range(1)};
       OnnxSetInputShape(model,0,input_shape);
    //---
       int output1[];
       float output2[][3];
    //---
       ArrayResize(output1,(int)batch_size);
       ArrayResize(output2,(int)batch_size);
    //---
       ulong output_shape[]= {batch_size};
       OnnxSetOutputShape(model,0,output_shape);
    //---
       ulong output_shape2[]= {batch_size,3};
       OnnxSetOutputShape(model,1,output_shape2);
    //---
       bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output1,output2);
    //--- classes are ready in output1[k];
       if(res)
         {
          for(int k=0; k<(int)batch_size; k++)
             model_classes_id[k]=output1[k];
         }
    //---
       return(res);
      }
    //+------------------------------------------------------------------+
    //| Test all samples from IRIS dataset (150)                         |
    //| Here we test all samples with batch=1, sample by sample          |
    //+------------------------------------------------------------------+
    bool TestAllIrisDataset(const long model,const string model_name,double &model_accuracy)
      {
       sIRISsample iris_samples[];
    //--- load dataset from file
       PrepareIrisDataset(iris_samples);
    //--- test
       int total_samples=ArraySize(iris_samples);
       if(total_samples==0)
         {
          Print("iris dataset not prepared");
          return(false);
         }
    //--- show dataset
       for(int k=0; k<total_samples; k++)
         {
          //PrintFormat("%d (%.2f,%.2f,%.2f,%.2f) class %d (%s)",iris_samples[k].sample_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3],iris_samples[k].class_id,iris_samples[k].class_name);
         }
    //--- array for output classes
       int model_output_classes_id[];
    //--- check all Iris dataset samples
       int correct_results=0;
       for(int k=0; k<total_samples; k++)
         {
          //--- input array
          float iris_sample_input_data[1][4];
          //--- prepare input data from kth iris sample dataset
          iris_sample_input_data[0][0]=(float)iris_samples[k].features[0];
          iris_sample_input_data[0][1]=(float)iris_samples[k].features[1];
          iris_sample_input_data[0][2]=(float)iris_samples[k].features[2];
          iris_sample_input_data[0][3]=(float)iris_samples[k].features[3];
          //--- run model
          bool res=TestSamples(model,iris_sample_input_data,model_output_classes_id);
          //--- check result
          if(res)
            {
             if(model_output_classes_id[0]==iris_samples[k].class_id)
               {
                correct_results++;
               }
             else
               {
                PrintFormat("model:%s  sample=%d FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f]",model_name,iris_samples[k].sample_id,model_output_classes_id[0],iris_samples[k].class_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3]);
               }
            }
         }
       model_accuracy=1.0*correct_results/total_samples;
    //---
       PrintFormat("model:%s   correct results: %.2f%%",model_name,100*model_accuracy);
    //---
       return(true);
      }
    
    //+------------------------------------------------------------------+
    //| Here we test batch execution of the model                        |
    //+------------------------------------------------------------------+
    bool TestBatchExecution(const long model,const string model_name,double &model_accuracy)
      {
       model_accuracy=0;
    //--- array for output classes
       int model_output_classes_id[];
       int correct_results=0;
       int total_results=0;
       bool res=false;
    
    //--- run batch with 3 samples
       float input_data_batch3[3][4]=
         {
            {5.1f,3.5f,1.4f,0.2f}, // iris dataset sample id=1, Iris-setosa
            {6.3f,2.5f,4.9f,1.5f}, // iris dataset sample id=73, Iris-versicolor
            {6.3f,2.7f,4.9f,1.8f}  // iris dataset sample id=124, Iris-virginica
         };
       int correct_classes_batch3[3]= {0,1,2};
    //--- run model
       res=TestSamples(model,input_data_batch3,model_output_classes_id);
       if(res)
         {
          //--- check result
          for(int j=0; j<ArraySize(model_output_classes_id); j++)
            {
             //--- check result
             if(model_output_classes_id[j]==correct_classes_batch3[j])
                correct_results++;
             else
               {
                PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch3[j],input_data_batch3[j][0],input_data_batch3[j][1],input_data_batch3[j][2],input_data_batch3[j][3]);
               }
             total_results++;
            }
         }
       else
          return(false);
    
    //--- run batch with 10 samples
       float input_data_batch10[10][4]=
         {
            {5.5f,3.5f,1.3f,0.2f}, // iris dataset sample id=37 (Iris-setosa)
            {4.9f,3.1f,1.5f,0.1f}, // iris dataset sample id=38 (Iris-setosa)
            {4.4f,3.0f,1.3f,0.2f}, // iris dataset sample id=39 (Iris-setosa)
            {5.0f,3.3f,1.4f,0.2f}, // iris dataset sample id=50 (Iris-setosa)
            {7.0f,3.2f,4.7f,1.4f}, // iris dataset sample id=51 (Iris-versicolor)
            {6.4f,3.2f,4.5f,1.5f}, // iris dataset sample id=52 (Iris-versicolor)
            {6.3f,3.3f,6.0f,2.5f}, // iris dataset sample id=101 (Iris-virginica)
            {5.8f,2.7f,5.1f,1.9f}, // iris dataset sample id=102 (Iris-virginica)
            {7.1f,3.0f,5.9f,2.1f}, // iris dataset sample id=103 (Iris-virginica)
            {6.3f,2.9f,5.6f,1.8f}  // iris dataset sample id=104 (Iris-virginica)
         };
    //--- correct classes for all 10 samples in the batch
       int correct_classes_batch10[10]= {0,0,0,0,1,1,2,2,2,2};
    
    //--- run model
       res=TestSamples(model,input_data_batch10,model_output_classes_id);
    //--- check result
       if(res)
         {
          for(int j=0; j<ArraySize(model_output_classes_id); j++)
            {
             if(model_output_classes_id[j]==correct_classes_batch10[j])
                correct_results++;
             else
               {
                double f1=input_data_batch10[j][0];
                double f2=input_data_batch10[j][1];
                double f3=input_data_batch10[j][2];
                double f4=input_data_batch10[j][3];
                PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch10[j],input_data_batch10[j][0],input_data_batch10[j][1],input_data_batch10[j][2],input_data_batch10[j][3]);
               }
             total_results++;
            }
         }
       else
          return(false);
    
    //--- calculate accuracy
       model_accuracy=correct_results/total_results;
    //---
       return(res);
      }
    //+------------------------------------------------------------------+
    //| Script program start function                                    |
    //+------------------------------------------------------------------+
    int OnStart(void)
      {
       string model_name="LinearSVC";
    //---
       long model=OnnxCreateFromBuffer(ExtModel,ONNX_DEFAULT);
       if(model==INVALID_HANDLE)
         {
          PrintFormat("model_name=%s OnnxCreate error %d for",model_name,GetLastError());
         }
       else
         {
          //--- test all dataset
          double model_accuracy=0;
          //-- test sample by sample execution for all Iris dataset
          if(TestAllIrisDataset(model,model_name,model_accuracy))
             PrintFormat("model=%s all samples accuracy=%f",model_name,model_accuracy);
          else
             PrintFormat("error in testing model=%s ",model_name);
          //--- test batch execution for several samples
          if(TestBatchExecution(model,model_name,model_accuracy))
             PrintFormat("model=%s batch test accuracy=%f",model_name,model_accuracy);
          else
             PrintFormat("error in testing model=%s ",model_name);
          //--- release model
          OnnxRelease(model);
         }
       return(0);
      }
    //+------------------------------------------------------------------+
    

    Resultado:

    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
    

    A precisão do modelo ONNX exportado no Iris dataset completo é de 96.67%, o que corresponde à precisão do original.


    2.2.3. Representação ONNX do modelo LinearSVC Classifier

    Fig.16. Representação ONNX do modelo LinearSVC Classifier no Netron

    Fig.16. Representação ONNX do modelo LinearSVC Classifier no Netron


    2.3. NuSVC Classifier

    O método de classificação Nu-Support Vector Classification (NuSVC) é um poderoso algoritmo de aprendizado de máquina, baseado no método de vetores de suporte (Support Vector Machine, SVM).

    Princípios de funcionamento do NuSVC:

    1. Método de vetores de suporte (SVM): O NuSVC é uma variante do SVM, utilizado para resolver tarefas de classificação binária e multiclasse. O principal princípio do SVM é encontrar o hiperplano separador ótimo que maximiza a separação entre as classes e possui a maior margem entre elas.
    2. Parâmetro Nu: Um dos parâmetros chave do NuSVC é o parâmetro Nu (nu), que controla a complexidade do modelo e determina a fração da amostra que pode ser usada como vetores de suporte e erros. O valor de Nu está no intervalo de 0 a 1, onde 0.5 significa que aproximadamente metade da amostra será utilizada como vetores de suporte e erros.
    3. Ajuste de parâmetros: Determinar os valores ótimos do parâmetro Nu e outros hiperparâmetros pode exigir validação cruzada e a busca pelos melhores valores nos dados de treinamento.
    4. Funções de kernel: O NuSVC pode usar várias funções de kernel, como linear, função de base radial (RBF), polinomial e outras. A função de kernel determina a maneira de transformar o espaço de características para encontrar o hiperplano separador.

    Vantagens do NuSVC:

    • Eficiência em espaços de alta dimensão: O NuSVC pode operar eficientemente em espaços de alta dimensão, o que o torna adequado para tarefas com um grande número de características.
    • Resistência a valores atípicos: O SVM e o NuSVC, em particular, são resistentes a valores atípicos nos dados graças ao uso de vetores de suporte.
    • Controle da complexidade do modelo: O parâmetro Nu permite controlar a complexidade do modelo e gerenciar o equilíbrio entre o ajuste dos dados e a capacidade de generalização.
    • Boa capacidade de generalização: O SVM e o NuSVC, em particular, têm uma boa capacidade de generalização, o que permite obter bons resultados em novos dados, anteriormente não vistos.

    Limitações do NuSVC:

    • Ineficiência com grandes volumes de dados: O NuSVC pode ser ineficiente ao treinar em grandes volumes de dados devido à complexidade dos cálculos.
    • Necessidade de ajuste de parâmetros: Ajustar os parâmetros Nu e a função de kernel pode exigir tempo e recursos computacionais.
    • Linearidade da função de kernel: A eficácia do NuSVC pode depender fortemente da escolha da função de kernel, e para algumas tarefas, pode ser necessário experimentar com diferentes funções.
    • Dificuldade de interpretação: O SVM e o NuSVC oferecem bons resultados, mas seus modelos podem ser difíceis de interpretar, especialmente ao usar kernels não lineares.

    O Nu-Support Vector Classification (NuSVC) é um poderoso método de classificação baseado em SVM, que possui várias vantagens, incluindo resistência a valores atípicos e boa capacidade de generalização. No entanto, sua eficácia pode depender da escolha de parâmetros e da função de kernel, além de poder ser ineficiente ao treinar em grandes volumes de dados. É importante ajustar cuidadosamente os parâmetros do método e adaptá-lo às tarefas de classificação específicas.


    2.3.1. Código de criação do modelo NuSVC Classifier

    Este código demonstra o processo de treinamento do modelo NuSVC Classifier no conjunto de dados Iris, sua exportação para o formato ONNX e a realização de classificação usando o modelo ONNX. Também avalia a precisão tanto do modelo original quanto do modelo 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)
    

    Resultado:

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


    2.3.2. Código no MQL5 para trabalhar com o modelo NuSVC Classifier

    //+------------------------------------------------------------------+
    //|                                                   Iris_NuSVC.mq5 |
    //|                                  Copyright 2023, MetaQuotes Ltd. |
    //|                                             https://www.mql5.com |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2023, MetaQuotes Ltd."
    #property link      "https://www.mql5.com"
    #property version   "1.00"
    
    #include "iris.mqh"
    #resource "nusvc_iris.onnx" as const uchar ExtModel[];
    
    //+------------------------------------------------------------------+
    //| Test IRIS dataset samples                                        |
    //+------------------------------------------------------------------+
    bool TestSamples(long model,float &input_data[][4], int &model_classes_id[])
      {
    //--- check number of input samples
       ulong batch_size=input_data.Range(0);
       if(batch_size==0)
          return(false);
    //--- prepare output array
       ArrayResize(model_classes_id,(int)batch_size);
    //---
       ulong input_shape[]= { batch_size, input_data.Range(1)};
       OnnxSetInputShape(model,0,input_shape);
    //---
       int output1[];
       float output2[][3];
    //---
       ArrayResize(output1,(int)batch_size);
       ArrayResize(output2,(int)batch_size);
    //---
       ulong output_shape[]= {batch_size};
       OnnxSetOutputShape(model,0,output_shape);
    //---
       ulong output_shape2[]= {batch_size,3};
       OnnxSetOutputShape(model,1,output_shape2);
    //---
       bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output1,output2);
    //--- classes are ready in output1[k];
       if(res)
         {
          for(int k=0; k<(int)batch_size; k++)
             model_classes_id[k]=output1[k];
         }
    //---
       return(res);
      }
    //+------------------------------------------------------------------+
    //| Test all samples from IRIS dataset (150)                         |
    //| Here we test all samples with batch=1, sample by sample          |
    //+------------------------------------------------------------------+
    bool TestAllIrisDataset(const long model,const string model_name,double &model_accuracy)
      {
       sIRISsample iris_samples[];
    //--- load dataset from file
       PrepareIrisDataset(iris_samples);
    //--- test
       int total_samples=ArraySize(iris_samples);
       if(total_samples==0)
         {
          Print("iris dataset not prepared");
          return(false);
         }
    //--- show dataset
       for(int k=0; k<total_samples; k++)
         {
          //PrintFormat("%d (%.2f,%.2f,%.2f,%.2f) class %d (%s)",iris_samples[k].sample_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3],iris_samples[k].class_id,iris_samples[k].class_name);
         }
    //--- array for output classes
       int model_output_classes_id[];
    //--- check all Iris dataset samples
       int correct_results=0;
       for(int k=0; k<total_samples; k++)
         {
          //--- input array
          float iris_sample_input_data[1][4];
          //--- prepare input data from kth iris sample dataset
          iris_sample_input_data[0][0]=(float)iris_samples[k].features[0];
          iris_sample_input_data[0][1]=(float)iris_samples[k].features[1];
          iris_sample_input_data[0][2]=(float)iris_samples[k].features[2];
          iris_sample_input_data[0][3]=(float)iris_samples[k].features[3];
          //--- run model
          bool res=TestSamples(model,iris_sample_input_data,model_output_classes_id);
          //--- check result
          if(res)
            {
             if(model_output_classes_id[0]==iris_samples[k].class_id)
               {
                correct_results++;
               }
             else
               {
                PrintFormat("model:%s  sample=%d FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f]",model_name,iris_samples[k].sample_id,model_output_classes_id[0],iris_samples[k].class_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3]);
               }
            }
         }
       model_accuracy=1.0*correct_results/total_samples;
    //---
       PrintFormat("model:%s   correct results: %.2f%%",model_name,100*model_accuracy);
    //---
       return(true);
      }
    
    //+------------------------------------------------------------------+
    //| Here we test batch execution of the model                        |
    //+------------------------------------------------------------------+
    bool TestBatchExecution(const long model,const string model_name,double &model_accuracy)
      {
       model_accuracy=0;
    //--- array for output classes
       int model_output_classes_id[];
       int correct_results=0;
       int total_results=0;
       bool res=false;
    
    //--- run batch with 3 samples
       float input_data_batch3[3][4]=
         {
            {5.1f,3.5f,1.4f,0.2f}, // iris dataset sample id=1, Iris-setosa
            {6.3f,2.5f,4.9f,1.5f}, // iris dataset sample id=73, Iris-versicolor
            {6.3f,2.7f,4.9f,1.8f}  // iris dataset sample id=124, Iris-virginica
         };
       int correct_classes_batch3[3]= {0,1,2};
    //--- run model
       res=TestSamples(model,input_data_batch3,model_output_classes_id);
       if(res)
         {
          //--- check result
          for(int j=0; j<ArraySize(model_output_classes_id); j++)
            {
             //--- check result
             if(model_output_classes_id[j]==correct_classes_batch3[j])
                correct_results++;
             else
               {
                PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch3[j],input_data_batch3[j][0],input_data_batch3[j][1],input_data_batch3[j][2],input_data_batch3[j][3]);
               }
             total_results++;
            }
         }
       else
          return(false);
    
    //--- run batch with 10 samples
       float input_data_batch10[10][4]=
         {
            {5.5f,3.5f,1.3f,0.2f}, // iris dataset sample id=37 (Iris-setosa)
            {4.9f,3.1f,1.5f,0.1f}, // iris dataset sample id=38 (Iris-setosa)
            {4.4f,3.0f,1.3f,0.2f}, // iris dataset sample id=39 (Iris-setosa)
            {5.0f,3.3f,1.4f,0.2f}, // iris dataset sample id=50 (Iris-setosa)
            {7.0f,3.2f,4.7f,1.4f}, // iris dataset sample id=51 (Iris-versicolor)
            {6.4f,3.2f,4.5f,1.5f}, // iris dataset sample id=52 (Iris-versicolor)
            {6.3f,3.3f,6.0f,2.5f}, // iris dataset sample id=101 (Iris-virginica)
            {5.8f,2.7f,5.1f,1.9f}, // iris dataset sample id=102 (Iris-virginica)
            {7.1f,3.0f,5.9f,2.1f}, // iris dataset sample id=103 (Iris-virginica)
            {6.3f,2.9f,5.6f,1.8f}  // iris dataset sample id=104 (Iris-virginica)
         };
    //--- correct classes for all 10 samples in the batch
       int correct_classes_batch10[10]= {0,0,0,0,1,1,2,2,2,2};
    
    //--- run model
       res=TestSamples(model,input_data_batch10,model_output_classes_id);
    //--- check result
       if(res)
         {
          for(int j=0; j<ArraySize(model_output_classes_id); j++)
            {
             if(model_output_classes_id[j]==correct_classes_batch10[j])
                correct_results++;
             else
               {
                double f1=input_data_batch10[j][0];
                double f2=input_data_batch10[j][1];
                double f3=input_data_batch10[j][2];
                double f4=input_data_batch10[j][3];
                PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch10[j],input_data_batch10[j][0],input_data_batch10[j][1],input_data_batch10[j][2],input_data_batch10[j][3]);
               }
             total_results++;
            }
         }
       else
          return(false);
    
    //--- calculate accuracy
       model_accuracy=correct_results/total_results;
    //---
       return(res);
      }
    //+------------------------------------------------------------------+
    //| Script program start function                                    |
    //+------------------------------------------------------------------+
    int OnStart(void)
      {
       string model_name="NuSVC";
    //---
       long model=OnnxCreateFromBuffer(ExtModel,ONNX_DEFAULT);
       if(model==INVALID_HANDLE)
         {
          PrintFormat("model_name=%s OnnxCreate error %d for",model_name,GetLastError());
         }
       else
         {
          //--- test all dataset
          double model_accuracy=0;
          //-- test sample by sample execution for all Iris dataset
          if(TestAllIrisDataset(model,model_name,model_accuracy))
             PrintFormat("model=%s all samples accuracy=%f",model_name,model_accuracy);
          else
             PrintFormat("error in testing model=%s ",model_name);
          //--- test batch execution for several samples
          if(TestBatchExecution(model,model_name,model_accuracy))
             PrintFormat("model=%s batch test accuracy=%f",model_name,model_accuracy);
          else
             PrintFormat("error in testing model=%s ",model_name);
          //--- release model
          OnnxRelease(model);
         }
       return(0);
      }
    //+------------------------------------------------------------------+
    

    Resultado:

    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
    

    A precisão do modelo ONNX exportado no iris dataset completo é de 97.33%, o que corresponde à precisão do original.


    2.3.3. Representação ONNX do modelo NuSVC Classifier

    Fig.17. Representação ONNX do modelo NuSVC Classifier no Netron

    Fig.17. Representação ONNX do modelo NuSVC Classifier no Netron


    2.4. Radius Neighbors Classifier

    O Radius Neighbors Classifier é um método de aprendizado de máquina que também é utilizado para tarefas de classificação e baseia-se no princípio de proximidade dos objetos. Diferentemente do classificador clássico K-Nearest Neighbors (K-NN), em que um número fixo de vizinhos mais próximos (K) é selecionado, no Radius Neighbors Classifier, os objetos são classificados com base na distância até os vizinhos mais próximos dentro de um determinado raio.

    Princípios de funcionamento do Radius Neighbors Classifier:
    1. Definição do raio: O principal parâmetro do Radius Neighbors Classifier é o raio, que define a distância máxima entre um objeto e seus vizinhos para que o objeto seja considerado próximo a uma classe de vizinhos.
    2. Busca dos vizinhos mais próximos: Para cada objeto, calcula-se a distância para todos os outros objetos do conjunto de treinamento. Aqueles objetos que estão dentro do raio definido são considerados vizinhos do objeto em questão.
    3. Votação: O Radius Neighbors Classifier utiliza a votação da maioria entre os vizinhos para determinar a classe de um objeto. Por exemplo, se a maioria dos vizinhos pertence à classe A, então o objeto também será classificado como pertencente à classe A.
    Vantagens do Radius Neighbors Classifier:
    • Adaptabilidade à densidade de dados: O Radius Neighbors Classifier é adequado para tarefas nas quais a densidade dos dados em diferentes áreas do espaço de características pode variar.
    • Capacidade de trabalhar com diferentes formas de classes: Este método lida bem com tarefas onde as classes têm formas complexas e não lineares.
    • Adequado para dados com valores atípicos: O Radius Neighbors Classifier é mais resistente a valores atípicos do que o K-NN, pois ignora os vizinhos que estão fora do raio definido.
    Limitações do Radius Neighbors Classifier:
    • Sensibilidade à escolha do raio: Escolher o valor ótimo do raio pode ser uma tarefa não trivial e requer ajustes.
    • Ineficiência em grandes conjuntos de dados: Em grandes volumes de dados, calcular distâncias para todos os objetos pode ser computacionalmente custoso.
    • Dependência da densidade de dados: Este método pode ser menos eficaz se os dados tiverem uma densidade desigual no espaço de características.

    O Radius Neighbors Classifier é um método útil de aprendizado de máquina em situações onde a proximidade dos objetos e a forma das classes podem ser complexas. Pode ser usado em várias áreas, incluindo análise de imagens, processamento de linguagem natural, entre outras.


    2.4.1. Código para criar o modelo Radius Neighbors Classifier

    Este código demonstra o processo de treinamento do modelo Radius Neighbors Classifier no conjunto de dados Iris, sua exportação para o formato ONNX e a realização de classificação usando o modelo ONNX. Também avalia a precisão tanto do modelo original quanto do modelo 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)
    

    Resultados do script Iris_RadiusNeighbors.py:

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

    A precisão do modelo original e do modelo exportado para o formato ONNX coincidem.


    2.4.2. Código no MQL5 para trabalhar com o modelo Radius Neighbors Classifier

    //+------------------------------------------------------------------+
    //|                               Iris_RadiusNeighborsClassifier.mq5 |
    //|                                  Copyright 2023, MetaQuotes Ltd. |
    //|                                             https://www.mql5.com |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2023, MetaQuotes Ltd."
    #property link      "https://www.mql5.com"
    #property version   "1.00"
    
    #include "iris.mqh"
    #resource "radius_neighbors_iris.onnx" as const uchar ExtModel[];
    
    //+------------------------------------------------------------------+
    //| Test IRIS dataset samples                                        |
    //+------------------------------------------------------------------+
    bool TestSamples(long model,float &input_data[][4], int &model_classes_id[])
      {
    //--- check number of input samples
       ulong batch_size=input_data.Range(0);
       if(batch_size==0)
          return(false);
    //--- prepare output array
       ArrayResize(model_classes_id,(int)batch_size);
    //---
       ulong input_shape[]= { batch_size, input_data.Range(1)};
       OnnxSetInputShape(model,0,input_shape);
    //---
       int output1[];
       float output2[][3];
    //---
       ArrayResize(output1,(int)batch_size);
       ArrayResize(output2,(int)batch_size);
    //---
       ulong output_shape[]= {batch_size};
       OnnxSetOutputShape(model,0,output_shape);
    //---
       ulong output_shape2[]= {batch_size,3};
       OnnxSetOutputShape(model,1,output_shape2);
    //---
       bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output1,output2);
    //--- classes are ready in output1[k];
       if(res)
         {
          for(int k=0; k<(int)batch_size; k++)
             model_classes_id[k]=output1[k];
         }
    //---
       return(res);
      }
    //+------------------------------------------------------------------+
    //| Test all samples from IRIS dataset (150)                         |
    //| Here we test all samples with batch=1, sample by sample          |
    //+------------------------------------------------------------------+
    bool TestAllIrisDataset(const long model,const string model_name,double &model_accuracy)
      {
       sIRISsample iris_samples[];
    //--- load dataset from file
       PrepareIrisDataset(iris_samples);
    //--- test
       int total_samples=ArraySize(iris_samples);
       if(total_samples==0)
         {
          Print("iris dataset not prepared");
          return(false);
         }
    //--- show dataset
       for(int k=0; k<total_samples; k++)
         {
          //PrintFormat("%d (%.2f,%.2f,%.2f,%.2f) class %d (%s)",iris_samples[k].sample_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3],iris_samples[k].class_id,iris_samples[k].class_name);
         }
    //--- array for output classes
       int model_output_classes_id[];
    //--- check all Iris dataset samples
       int correct_results=0;
       for(int k=0; k<total_samples; k++)
         {
          //--- input array
          float iris_sample_input_data[1][4];
          //--- prepare input data from kth iris sample dataset
          iris_sample_input_data[0][0]=(float)iris_samples[k].features[0];
          iris_sample_input_data[0][1]=(float)iris_samples[k].features[1];
          iris_sample_input_data[0][2]=(float)iris_samples[k].features[2];
          iris_sample_input_data[0][3]=(float)iris_samples[k].features[3];
          //--- run model
          bool res=TestSamples(model,iris_sample_input_data,model_output_classes_id);
          //--- check result
          if(res)
            {
             if(model_output_classes_id[0]==iris_samples[k].class_id)
               {
                correct_results++;
               }
             else
               {
                PrintFormat("model:%s  sample=%d FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f]",model_name,iris_samples[k].sample_id,model_output_classes_id[0],iris_samples[k].class_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3]);
               }
            }
         }
       model_accuracy=1.0*correct_results/total_samples;
    //---
       PrintFormat("model:%s   correct results: %.2f%%",model_name,100*model_accuracy);
    //---
       return(true);
      }
    
    //+------------------------------------------------------------------+
    //| Here we test batch execution of the model                        |
    //+------------------------------------------------------------------+
    bool TestBatchExecution(const long model,const string model_name,double &model_accuracy)
      {
       model_accuracy=0;
    //--- array for output classes
       int model_output_classes_id[];
       int correct_results=0;
       int total_results=0;
       bool res=false;
    
    //--- run batch with 3 samples
       float input_data_batch3[3][4]=
         {
            {5.1f,3.5f,1.4f,0.2f}, // iris dataset sample id=1, Iris-setosa
            {6.3f,2.5f,4.9f,1.5f}, // iris dataset sample id=73, Iris-versicolor
            {6.3f,2.7f,4.9f,1.8f}  // iris dataset sample id=124, Iris-virginica
         };
       int correct_classes_batch3[3]= {0,1,2};
    //--- run model
       res=TestSamples(model,input_data_batch3,model_output_classes_id);
       if(res)
         {
          //--- check result
          for(int j=0; j<ArraySize(model_output_classes_id); j++)
            {
             //--- check result
             if(model_output_classes_id[j]==correct_classes_batch3[j])
                correct_results++;
             else
               {
                PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch3[j],input_data_batch3[j][0],input_data_batch3[j][1],input_data_batch3[j][2],input_data_batch3[j][3]);
               }
             total_results++;
            }
         }
       else
          return(false);
    
    //--- run batch with 10 samples
       float input_data_batch10[10][4]=
         {
            {5.5f,3.5f,1.3f,0.2f}, // iris dataset sample id=37 (Iris-setosa)
            {4.9f,3.1f,1.5f,0.1f}, // iris dataset sample id=38 (Iris-setosa)
            {4.4f,3.0f,1.3f,0.2f}, // iris dataset sample id=39 (Iris-setosa)
            {5.0f,3.3f,1.4f,0.2f}, // iris dataset sample id=50 (Iris-setosa)
            {7.0f,3.2f,4.7f,1.4f}, // iris dataset sample id=51 (Iris-versicolor)
            {6.4f,3.2f,4.5f,1.5f}, // iris dataset sample id=52 (Iris-versicolor)
            {6.3f,3.3f,6.0f,2.5f}, // iris dataset sample id=101 (Iris-virginica)
            {5.8f,2.7f,5.1f,1.9f}, // iris dataset sample id=102 (Iris-virginica)
            {7.1f,3.0f,5.9f,2.1f}, // iris dataset sample id=103 (Iris-virginica)
            {6.3f,2.9f,5.6f,1.8f}  // iris dataset sample id=104 (Iris-virginica)
         };
    //--- correct classes for all 10 samples in the batch
       int correct_classes_batch10[10]= {0,0,0,0,1,1,2,2,2,2};
    
    //--- run model
       res=TestSamples(model,input_data_batch10,model_output_classes_id);
    //--- check result
       if(res)
         {
          for(int j=0; j<ArraySize(model_output_classes_id); j++)
            {
             if(model_output_classes_id[j]==correct_classes_batch10[j])
                correct_results++;
             else
               {
                double f1=input_data_batch10[j][0];
                double f2=input_data_batch10[j][1];
                double f3=input_data_batch10[j][2];
                double f4=input_data_batch10[j][3];
                PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch10[j],input_data_batch10[j][0],input_data_batch10[j][1],input_data_batch10[j][2],input_data_batch10[j][3]);
               }
             total_results++;
            }
         }
       else
          return(false);
    
    //--- calculate accuracy
       model_accuracy=correct_results/total_results;
    //---
       return(res);
      }
    //+------------------------------------------------------------------+
    //| Script program start function                                    |
    //+------------------------------------------------------------------+
    int OnStart(void)
      {
       string model_name="RadiusNeighborsClassifier";
    //---
       long model=OnnxCreateFromBuffer(ExtModel,ONNX_DEFAULT);
       if(model==INVALID_HANDLE)
         {
          PrintFormat("model_name=%s OnnxCreate error %d for",model_name,GetLastError());
         }
       else
         {
          //--- test all dataset
          double model_accuracy=0;
          //-- test sample by sample execution for all Iris dataset
          if(TestAllIrisDataset(model,model_name,model_accuracy))
             PrintFormat("model=%s all samples accuracy=%f",model_name,model_accuracy);
          else
             PrintFormat("error in testing model=%s ",model_name);
          //--- test batch execution for several samples
          if(TestBatchExecution(model,model_name,model_accuracy))
             PrintFormat("model=%s batch test accuracy=%f",model_name,model_accuracy);
          else
             PrintFormat("error in testing model=%s ",model_name);
          //--- release model
          OnnxRelease(model);
         }
       return(0);
      }
    //+------------------------------------------------------------------+
    

    Resultado:

    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
    

    O modelo Radius Neighbor Classifier mostrou uma precisão de 97% com 4 erros de classificação (amostras 78, 107, 127 e 139).

    A precisão do modelo ONNX exportado no iris dataset completo é de 97.33%, o que corresponde à precisão do original.


    2.4.3. Representação ONNX do modelo Radius Neighbors Classifier

    Fig.18. Representação ONNX do modelo Radius Neighbors Classifier no Netron

    Fig.18. Representação ONNX do modelo Radius Neighbors Classifier no Netron


    Nota sobre os métodos RidgeClassifier e RidgeClassifierCV

    RidgeClassifier e RidgeClassifierCV são dois métodos de classificação baseados na regressão Ridge, mas diferem na forma de ajuste dos parâmetros e na seleção automática dos hiperparâmetros:

        RidgeClassifier:

    • RidgeClassifier é um método de classificação baseado na regressão linear Ridge, que é usado para tarefas de classificação binária e multiclasse.
    • Para classificação multiclasse, o RidgeClassifier transforma a tarefa em várias tarefas binárias (um contra todos) e constrói um modelo para cada uma delas.
    • O parâmetro de regularização alpha deve ser ajustado manualmente pelo usuário. Isso significa que você precisa escolher o valor ótimo de alpha por meio de experimentos ou análise dos resultados em dados de validação.

        RidgeClassifierCV:

    • RidgeClassifierCV é uma extensão do RidgeClassifier que oferece suporte integrado para validação cruzada e seleção automática do valor ótimo do parâmetro de regularização alpha.
    • Em vez de ajustar manualmente o alpha, você pode fornecer ao RidgeClassifierCV uma lista de valores de alpha para explorar e especificar o método de validação cruzada (por exemplo, através do parâmetro cv).
    • O RidgeClassifierCV selecionará automaticamente o valor de alpha que oferece o melhor desempenho na validação cruzada.

    Então, a principal diferença entre eles está no nível de automação na seleção do valor ótimo do parâmetro de regularização alpha. O RidgeClassifier requer ajuste manual de alpha, enquanto o RidgeClassifierCV permite a seleção automática do valor ótimo de alpha usando validação cruzada. A escolha entre eles depende de suas necessidades e do desejo de automação no processo de ajuste do modelo.


    2.5. Ridge Classifier

    O Ridge Classifier é uma variante da regressão logística que inclui regularização L2 (regularização de Tikhonov) no modelo. A regularização L2 adiciona uma penalidade aos grandes valores dos coeficientes do modelo, o que ajuda a reduzir o sobreajuste e melhorar a capacidade de generalização do modelo.

    Princípios de funcionamento do Ridge Classifier:
    • Previsão de probabilidade: Assim como na regressão logística, o Ridge Classifier modela a probabilidade de um objeto pertencer a uma determinada classe usando a função logística (sigmoidal).
    • Regularização L2: O Ridge Classifier inclui um termo de regularização L2, que penaliza os grandes valores dos coeficientes do modelo. Isso é feito para controlar a complexidade do modelo e reduzir o sobreajuste.
    • Treinamento dos parâmetros: O modelo Ridge Classifier é treinado no conjunto de dados de treinamento com o objetivo de ajustar os pesos (coeficientes) das características e o parâmetro de regularização.
    Vantagens do Ridge Classifier:
    • Redução de sobreajuste: A regularização L2 ajuda a diminuir a tendência do modelo ao sobreajuste, o que é especialmente útil quando há escassez de dados.
    • Resistência à multicolinearidade: O Ridge Classifier lida bem com o problema da multicolinearidade nos dados, quando as características são fortemente correlacionadas entre si.
    Limitações do Ridge Classifier:
    • Sensibilidade à escolha do parâmetro de regularização: Como em outros métodos com regularização, a escolha do valor correto do parâmetro de regularização (alpha) requer ajuste e avaliação.
    • Limitação para classificação multiclasse: O Ridge Classifier é originalmente projetado para classificação binária, mas pode ser adaptado para classificação multiclasse usando abordagens como One-vs-All.

    O Ridge Classifier é um método de aprendizado de máquina poderoso que combina as vantagens da regressão logística com a regularização para combater o sobreajuste e melhorar a capacidade de generalização do modelo. Ele é aplicado em várias áreas onde a classificação com consideração de probabilidades e controle da complexidade do modelo são importantes.


    2.5.1. Código para criar o modelo Ridge Classifier

    Este código demonstra o processo de treinamento do modelo Ridge Classifier no conjunto de dados Iris, sua exportação para o formato ONNX e a realização de classificação usando o modelo ONNX. Também avalia a precisão tanto do modelo original quanto do modelo 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)
    

    Resultado:

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


    2.5.2. Código no MQL5 para trabalhar com o modelo Ridge Classifier

    //+------------------------------------------------------------------+
    //|                                         Iris_RidgeClassifier.mq5 |
    //|                                  Copyright 2023, MetaQuotes Ltd. |
    //|                                             https://www.mql5.com |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2023, MetaQuotes Ltd."
    #property link      "https://www.mql5.com"
    #property version   "1.00"
    
    #include "iris.mqh"
    #resource "ridge_classifier_iris.onnx" as const uchar ExtModel[];
    
    //+------------------------------------------------------------------+
    //| Test IRIS dataset samples                                        |
    //+------------------------------------------------------------------+
    bool TestSamples(long model,float &input_data[][4], int &model_classes_id[])
      {
    //--- check number of input samples
       ulong batch_size=input_data.Range(0);
       if(batch_size==0)
          return(false);
    //--- prepare output array
       ArrayResize(model_classes_id,(int)batch_size);
    //---
       ulong input_shape[]= { batch_size, input_data.Range(1)};
       OnnxSetInputShape(model,0,input_shape);
    //---
       int output1[];
       float output2[][3];
    //---
       ArrayResize(output1,(int)batch_size);
       ArrayResize(output2,(int)batch_size);
    //---
       ulong output_shape[]= {batch_size};
       OnnxSetOutputShape(model,0,output_shape);
    //---
       ulong output_shape2[]= {batch_size,3};
       OnnxSetOutputShape(model,1,output_shape2);
    //---
       bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output1,output2);
    //--- classes are ready in output1[k];
       if(res)
         {
          for(int k=0; k<(int)batch_size; k++)
             model_classes_id[k]=output1[k];
         }
    //---
       return(res);
      }
    //+------------------------------------------------------------------+
    //| Test all samples from IRIS dataset (150)                         |
    //| Here we test all samples with batch=1, sample by sample          |
    //+------------------------------------------------------------------+
    bool TestAllIrisDataset(const long model,const string model_name,double &model_accuracy)
      {
       sIRISsample iris_samples[];
    //--- load dataset from file
       PrepareIrisDataset(iris_samples);
    //--- test
       int total_samples=ArraySize(iris_samples);
       if(total_samples==0)
         {
          Print("iris dataset not prepared");
          return(false);
         }
    //--- show dataset
       for(int k=0; k<total_samples; k++)
         {
          //PrintFormat("%d (%.2f,%.2f,%.2f,%.2f) class %d (%s)",iris_samples[k].sample_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3],iris_samples[k].class_id,iris_samples[k].class_name);
         }
    //--- array for output classes
       int model_output_classes_id[];
    //--- check all Iris dataset samples
       int correct_results=0;
       for(int k=0; k<total_samples; k++)
         {
          //--- input array
          float iris_sample_input_data[1][4];
          //--- prepare input data from kth iris sample dataset
          iris_sample_input_data[0][0]=(float)iris_samples[k].features[0];
          iris_sample_input_data[0][1]=(float)iris_samples[k].features[1];
          iris_sample_input_data[0][2]=(float)iris_samples[k].features[2];
          iris_sample_input_data[0][3]=(float)iris_samples[k].features[3];
          //--- run model
          bool res=TestSamples(model,iris_sample_input_data,model_output_classes_id);
          //--- check result
          if(res)
            {
             if(model_output_classes_id[0]==iris_samples[k].class_id)
               {
                correct_results++;
               }
             else
               {
                PrintFormat("model:%s  sample=%d FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f]",model_name,iris_samples[k].sample_id,model_output_classes_id[0],iris_samples[k].class_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3]);
               }
            }
         }
       model_accuracy=1.0*correct_results/total_samples;
    //---
       PrintFormat("model:%s   correct results: %.2f%%",model_name,100*model_accuracy);
    //---
       return(true);
      }
    
    //+------------------------------------------------------------------+
    //| Here we test batch execution of the model                        |
    //+------------------------------------------------------------------+
    bool TestBatchExecution(const long model,const string model_name,double &model_accuracy)
      {
       model_accuracy=0;
    //--- array for output classes
       int model_output_classes_id[];
       int correct_results=0;
       int total_results=0;
       bool res=false;
    
    //--- run batch with 3 samples
       float input_data_batch3[3][4]=
         {
            {5.1f,3.5f,1.4f,0.2f}, // iris dataset sample id=1, Iris-setosa
            {6.3f,2.5f,4.9f,1.5f}, // iris dataset sample id=73, Iris-versicolor
            {6.3f,2.7f,4.9f,1.8f}  // iris dataset sample id=124, Iris-virginica
         };
       int correct_classes_batch3[3]= {0,1,2};
    //--- run model
       res=TestSamples(model,input_data_batch3,model_output_classes_id);
       if(res)
         {
          //--- check result
          for(int j=0; j<ArraySize(model_output_classes_id); j++)
            {
             //--- check result
             if(model_output_classes_id[j]==correct_classes_batch3[j])
                correct_results++;
             else
               {
                PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch3[j],input_data_batch3[j][0],input_data_batch3[j][1],input_data_batch3[j][2],input_data_batch3[j][3]);
               }
             total_results++;
            }
         }
       else
          return(false);
    
    //--- run batch with 10 samples
       float input_data_batch10[10][4]=
         {
            {5.5f,3.5f,1.3f,0.2f}, // iris dataset sample id=37 (Iris-setosa)
            {4.9f,3.1f,1.5f,0.1f}, // iris dataset sample id=38 (Iris-setosa)
            {4.4f,3.0f,1.3f,0.2f}, // iris dataset sample id=39 (Iris-setosa)
            {5.0f,3.3f,1.4f,0.2f}, // iris dataset sample id=50 (Iris-setosa)
            {7.0f,3.2f,4.7f,1.4f}, // iris dataset sample id=51 (Iris-versicolor)
            {6.4f,3.2f,4.5f,1.5f}, // iris dataset sample id=52 (Iris-versicolor)
            {6.3f,3.3f,6.0f,2.5f}, // iris dataset sample id=101 (Iris-virginica)
            {5.8f,2.7f,5.1f,1.9f}, // iris dataset sample id=102 (Iris-virginica)
            {7.1f,3.0f,5.9f,2.1f}, // iris dataset sample id=103 (Iris-virginica)
            {6.3f,2.9f,5.6f,1.8f}  // iris dataset sample id=104 (Iris-virginica)
         };
    //--- correct classes for all 10 samples in the batch
       int correct_classes_batch10[10]= {0,0,0,0,1,1,2,2,2,2};
    
    //--- run model
       res=TestSamples(model,input_data_batch10,model_output_classes_id);
    //--- check result
       if(res)
         {
          for(int j=0; j<ArraySize(model_output_classes_id); j++)
            {
             if(model_output_classes_id[j]==correct_classes_batch10[j])
                correct_results++;
             else
               {
                double f1=input_data_batch10[j][0];
                double f2=input_data_batch10[j][1];
                double f3=input_data_batch10[j][2];
                double f4=input_data_batch10[j][3];
                PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch10[j],input_data_batch10[j][0],input_data_batch10[j][1],input_data_batch10[j][2],input_data_batch10[j][3]);
               }
             total_results++;
            }
         }
       else
          return(false);
    
    //--- calculate accuracy
       model_accuracy=correct_results/total_results;
    //---
       return(res);
      }
    //+------------------------------------------------------------------+
    //| Script program start function                                    |
    //+------------------------------------------------------------------+
    int OnStart(void)
      {
       string model_name="RidgeClassifier";
    //---
       long model=OnnxCreateFromBuffer(ExtModel,ONNX_DEFAULT);
       if(model==INVALID_HANDLE)
         {
          PrintFormat("model_name=%s OnnxCreate error %d for",model_name,GetLastError());
         }
       else
         {
          //--- test all dataset
          double model_accuracy=0;
          //-- test sample by sample execution for all Iris dataset
          if(TestAllIrisDataset(model,model_name,model_accuracy))
             PrintFormat("model=%s all samples accuracy=%f",model_name,model_accuracy);
          else
             PrintFormat("error in testing model=%s ",model_name);
          //--- test batch execution for several samples
          if(TestBatchExecution(model,model_name,model_accuracy))
             PrintFormat("model=%s batch test accuracy=%f",model_name,model_accuracy);
          else
             PrintFormat("error in testing model=%s ",model_name);
          //--- release model
          OnnxRelease(model);
         }
       return(0);
      }
    //+------------------------------------------------------------------+
    

    Resultado:

    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
    

    No conjunto completo de dados íris, o modelo mostrou uma eficácia de 85.33%, o que corresponde à precisão do original.


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

    Fig.19. Representação ONNX do modelo Ridge Classifier no Netron

    Fig.19. Representação ONNX do modelo Ridge Classifier no Netron


    2.6. RidgeClassifierCV

    O método de classificação RidgeClassifierCV é um algoritmo poderoso de classificação binária e multiclasse, baseado na regressão Ridge.

    Princípios de funcionamento do RidgeClassifierCV:

    1. Regressão linear Ridge: O RidgeClassifierCV é baseado na regressão linear Ridge. Este método representa uma modificação da regressão linear, onde a regularização L2 é adicionada. A regularização ajuda a controlar o sobreajuste, reduzindo a amplitude dos coeficientes de peso das características .
    2. Classificação binária e multiclasse: O RidgeClassifierCV pode ser usado tanto para classificação binária (quando há apenas duas classes) quanto para classificação multiclasse (quando há mais de duas classes). Para a classificação multiclasse, o método converte o problema em várias tarefas binárias (um contra todos) e constrói um modelo para cada uma delas.
    3. Seleção automática do parâmetro de regularização: Um dos principais benefícios do RidgeClassifierCV é o suporte embutido para validação cruzada e seleção automática do valor ótimo do parâmetro de regularização alpha. Em vez de ajustar manualmente o alpha, o método itera sobre diferentes valores de alpha e seleciona o melhor com base na validação cruzada.
    4. Resistência à multicolinearidade: A regressão Ridge lida bem com o problema da multicolinearidade, quando as características são altamente correlacionadas entre si. A regularização permite controlar a contribuição de cada recurso, tornando o modelo resistente a dados correlacionados.

    Vantagens do RidgeClassifierCV:

    • Seleção automática de hiperparâmetros: Uma das importantes vantagens do RidgeClassifierCV é a capacidade de selecionar automaticamente o valor ótimo de alpha usando validação cruzada. Isso elimina a necessidade de experimentar com diferentes valores de alpha e aumenta a chance de obter bons resultados.
    • Resistência ao sobreajuste: A regularização L2 fornecida pelo RidgeClassifierCV ajuda a controlar a complexidade do modelo e reduz o risco de sobreajuste. Isso é especialmente importante em tarefas com dados limitados.
    • Transparência e interpretabilidade: O RidgeClassifierCV fornece coeficientes de peso interpretáveis para cada característica, o que permite analisar a contribuição de cada característica na previsão e tirar conclusões sobre a importância das características .
    • Eficiência: O método é altamente eficaz e pode ser aplicado a grandes volumes de dados.

    Limitações do RidgeClassifierCV:

    • Linearidade: O RidgeClassifierCV pressupõe dependências lineares entre as características e a variável alvo. Se os dados tiverem fortes dependências não lineares, o método pode não ser suficientemente preciso.
    • Sensibilidade à escala das características : O método é sensível à escala das características . É recomendado realizar a padronização ou normalização das características antes de aplicar o RidgeClassifierCV.
    • Seleção do número ótimo de características: O RidgeClassifierCV não realiza a seleção automática de características, por isso, é necessário decidir manualmente quais características incluir no modelo.

    O método de classificação RidgeClassifierCV é uma ferramenta poderosa para classificação binária e multiclasse com seleção automática do parâmetro de regularização ótimo. Sua resistência ao sobreajuste, interpretabilidade e eficácia o tornam uma escolha popular para uma série de tarefas de classificação. No entanto, é importante lembrar de suas limitações, especialmente a suposição de dependências lineares entre as características e a variável alvo.


    2.6.1. Código para criação do modelo RidgeClassifierCV

    Este código demonstra o processo de treinamento do modelo RidgeClassifierCV no conjunto de dados Iris, exportação para o formato ONNX e realização de classificação usando o modelo ONNX. Também avalia a precisão tanto do modelo original quanto do modelo 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)
    

    Resultado:

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


    2.6.2. Código no MQL5 para trabalhar com o modelo RidgeClassifierCV

    //+------------------------------------------------------------------+
    //|                                       Iris_RidgeClassifierCV.mq5 |
    //|                                  Copyright 2023, MetaQuotes Ltd. |
    //|                                             https://www.mql5.com |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2023, MetaQuotes Ltd."
    #property link      "https://www.mql5.com"
    #property version   "1.00"
    
    #include "iris.mqh"
    #resource "ridge_classifier_cv_iris.onnx" as const uchar ExtModel[];
    
    //+------------------------------------------------------------------+
    //| Test IRIS dataset samples                                        |
    //+------------------------------------------------------------------+
    bool TestSamples(long model,float &input_data[][4], int &model_classes_id[])
      {
    //--- check number of input samples
       ulong batch_size=input_data.Range(0);
       if(batch_size==0)
          return(false);
    //--- prepare output array
       ArrayResize(model_classes_id,(int)batch_size);
    //---
       ulong input_shape[]= { batch_size, input_data.Range(1)};
       OnnxSetInputShape(model,0,input_shape);
    //---
       int output1[];
       float output2[][3];
    //---
       ArrayResize(output1,(int)batch_size);
       ArrayResize(output2,(int)batch_size);
    //---
       ulong output_shape[]= {batch_size};
       OnnxSetOutputShape(model,0,output_shape);
    //---
       ulong output_shape2[]= {batch_size,3};
       OnnxSetOutputShape(model,1,output_shape2);
    //---
       bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output1,output2);
    //--- classes are ready in output1[k];
       if(res)
         {
          for(int k=0; k<(int)batch_size; k++)
             model_classes_id[k]=output1[k];
         }
    //---
       return(res);
      }
    //+------------------------------------------------------------------+
    //| Test all samples from IRIS dataset (150)                         |
    //| Here we test all samples with batch=1, sample by sample          |
    //+------------------------------------------------------------------+
    bool TestAllIrisDataset(const long model,const string model_name,double &model_accuracy)
      {
       sIRISsample iris_samples[];
    //--- load dataset from file
       PrepareIrisDataset(iris_samples);
    //--- test
       int total_samples=ArraySize(iris_samples);
       if(total_samples==0)
         {
          Print("iris dataset not prepared");
          return(false);
         }
    //--- show dataset
       for(int k=0; k<total_samples; k++)
         {
          //PrintFormat("%d (%.2f,%.2f,%.2f,%.2f) class %d (%s)",iris_samples[k].sample_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3],iris_samples[k].class_id,iris_samples[k].class_name);
         }
    //--- array for output classes
       int model_output_classes_id[];
    //--- check all Iris dataset samples
       int correct_results=0;
       for(int k=0; k<total_samples; k++)
         {
          //--- input array
          float iris_sample_input_data[1][4];
          //--- prepare input data from kth iris sample dataset
          iris_sample_input_data[0][0]=(float)iris_samples[k].features[0];
          iris_sample_input_data[0][1]=(float)iris_samples[k].features[1];
          iris_sample_input_data[0][2]=(float)iris_samples[k].features[2];
          iris_sample_input_data[0][3]=(float)iris_samples[k].features[3];
          //--- run model
          bool res=TestSamples(model,iris_sample_input_data,model_output_classes_id);
          //--- check result
          if(res)
            {
             if(model_output_classes_id[0]==iris_samples[k].class_id)
               {
                correct_results++;
               }
             else
               {
                PrintFormat("model:%s  sample=%d FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f]",model_name,iris_samples[k].sample_id,model_output_classes_id[0],iris_samples[k].class_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3]);
               }
            }
         }
       model_accuracy=1.0*correct_results/total_samples;
    //---
       PrintFormat("model:%s   correct results: %.2f%%",model_name,100*model_accuracy);
    //---
       return(true);
      }
    
    //+------------------------------------------------------------------+
    //| Here we test batch execution of the model                        |
    //+------------------------------------------------------------------+
    bool TestBatchExecution(const long model,const string model_name,double &model_accuracy)
      {
       model_accuracy=0;
    //--- array for output classes
       int model_output_classes_id[];
       int correct_results=0;
       int total_results=0;
       bool res=false;
    
    //--- run batch with 3 samples
       float input_data_batch3[3][4]=
         {
            {5.1f,3.5f,1.4f,0.2f}, // iris dataset sample id=1, Iris-setosa
            {6.3f,2.5f,4.9f,1.5f}, // iris dataset sample id=73, Iris-versicolor
            {6.3f,2.7f,4.9f,1.8f}  // iris dataset sample id=124, Iris-virginica
         };
       int correct_classes_batch3[3]= {0,1,2};
    //--- run model
       res=TestSamples(model,input_data_batch3,model_output_classes_id);
       if(res)
         {
          //--- check result
          for(int j=0; j<ArraySize(model_output_classes_id); j++)
            {
             //--- check result
             if(model_output_classes_id[j]==correct_classes_batch3[j])
                correct_results++;
             else
               {
                PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch3[j],input_data_batch3[j][0],input_data_batch3[j][1],input_data_batch3[j][2],input_data_batch3[j][3]);
               }
             total_results++;
            }
         }
       else
          return(false);
    
    //--- run batch with 10 samples
       float input_data_batch10[10][4]=
         {
            {5.5f,3.5f,1.3f,0.2f}, // iris dataset sample id=37 (Iris-setosa)
            {4.9f,3.1f,1.5f,0.1f}, // iris dataset sample id=38 (Iris-setosa)
            {4.4f,3.0f,1.3f,0.2f}, // iris dataset sample id=39 (Iris-setosa)
            {5.0f,3.3f,1.4f,0.2f}, // iris dataset sample id=50 (Iris-setosa)
            {7.0f,3.2f,4.7f,1.4f}, // iris dataset sample id=51 (Iris-versicolor)
            {6.4f,3.2f,4.5f,1.5f}, // iris dataset sample id=52 (Iris-versicolor)
            {6.3f,3.3f,6.0f,2.5f}, // iris dataset sample id=101 (Iris-virginica)
            {5.8f,2.7f,5.1f,1.9f}, // iris dataset sample id=102 (Iris-virginica)
            {7.1f,3.0f,5.9f,2.1f}, // iris dataset sample id=103 (Iris-virginica)
            {6.3f,2.9f,5.6f,1.8f}  // iris dataset sample id=104 (Iris-virginica)
         };
    //--- correct classes for all 10 samples in the batch
       int correct_classes_batch10[10]= {0,0,0,0,1,1,2,2,2,2};
    
    //--- run model
       res=TestSamples(model,input_data_batch10,model_output_classes_id);
    //--- check result
       if(res)
         {
          for(int j=0; j<ArraySize(model_output_classes_id); j++)
            {
             if(model_output_classes_id[j]==correct_classes_batch10[j])
                correct_results++;
             else
               {
                double f1=input_data_batch10[j][0];
                double f2=input_data_batch10[j][1];
                double f3=input_data_batch10[j][2];
                double f4=input_data_batch10[j][3];
                PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch10[j],input_data_batch10[j][0],input_data_batch10[j][1],input_data_batch10[j][2],input_data_batch10[j][3]);
               }
             total_results++;
            }
         }
       else
          return(false);
    
    //--- calculate accuracy
       model_accuracy=correct_results/total_results;
    //---
       return(res);
      }
    //+------------------------------------------------------------------+
    //| Script program start function                                    |
    //+------------------------------------------------------------------+
    int OnStart(void)
      {
       string model_name="RidgeClassifierCV";
    //---
       long model=OnnxCreateFromBuffer(ExtModel,ONNX_DEFAULT);
       if(model==INVALID_HANDLE)
         {
          PrintFormat("model_name=%s OnnxCreate error %d for",model_name,GetLastError());
         }
       else
         {
          //--- test all dataset
          double model_accuracy=0;
          //-- test sample by sample execution for all Iris dataset
          if(TestAllIrisDataset(model,model_name,model_accuracy))
             PrintFormat("model=%s all samples accuracy=%f",model_name,model_accuracy);
          else
             PrintFormat("error in testing model=%s ",model_name);
          //--- test batch execution for several samples
          if(TestBatchExecution(model,model_name,model_accuracy))
             PrintFormat("model=%s batch test accuracy=%f",model_name,model_accuracy);
          else
             PrintFormat("error in testing model=%s ",model_name);
          //--- release model
          OnnxRelease(model);
         }
       return(0);
      }
    //+------------------------------------------------------------------+
    

    Resultado:

    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
    

    A eficácia do modelo ONNX também corresponde completamente à eficácia do modelo original do pacote Scikit-learn (85.33%).


    2.6.3. Representação ONNX do modelo RidgeClassifierCV

    Fig.20. Representação ONNX do modelo RidgeClassifierCV em Netron

    Fig.20. Representação ONNX do modelo RidgeClassifierCV em Netron



    2.7. Random Forest Classifier

    Random Forest Classifier é um método de ensemble de aprendizado de máquina baseado na construção de várias árvores de decisão e na combinação de seus resultados para melhorar a qualidade da classificação. Este método é extremamente popular devido à sua eficácia e capacidade de trabalhar com dados diversos.

    Princípios do Random Forest Classifier:
    1. Bagging (Bootstrap Aggregating): O Random Forest utiliza o método de bagging, que envolve a criação de várias subamostras (bootstrap samples) dos dados de treinamento com reposição. Para cada subamostra, é construída uma árvore de decisão separada.
    2. Seleção aleatória de características: Na construção de cada árvore, um subconjunto de características é selecionado aleatoriamente a partir do conjunto total de características. Isso promove a diversidade das árvores e reduz a correlação entre elas.
    3. Votação: Na classificação de um objeto, cada árvore faz sua própria previsão, e a classe que recebe a maioria dos votos entre todas as árvores é escolhida como a previsão final do modelo.
    Vantagens do Random Forest Classifier:
    • Alta precisão: O Random Forest geralmente fornece alta precisão de classificação ao calcular a média dos resultados de várias árvores.
    • Capacidade de trabalhar com dados diversos: Ele funciona bem tanto com características numéricas quanto categóricas, bem como com dados de estruturas variadas.
    • Resistência ao sobreajuste: O Random Forest possui regularização embutida, o que o torna resistente ao sobreajuste.
    • Importância das características : O Random Forest pode avaliar a importância das características , o que ajuda pesquisadores e engenheiros de características a entender melhor os dados.
    Limitações do Random Forest Classifier:
    • Complexidade computacional: O treinamento do Random Forest pode ser demorado, especialmente com um grande número de árvores e características.
    • Dificuldade de interpretação: Devido ao grande número de árvores e à seleção aleatória de características, a interpretação do modelo pode ser desafiadora.
    • Não garante resistência a valores atípicos: O Random Forest nem sempre possui boa resistência a valores atípicos nos dados.

    O Random Forest Classifier é um poderoso algoritmo de aprendizado de máquina amplamente utilizado em várias áreas, incluindo pesquisas biomédicas, análise financeira e análise de dados textuais. Ele é altamente adequado para resolver tarefas de classificação e regressão e possui uma grande capacidade de generalização.


    2.7.1. Código para criação do modelo Random Forest Classifier

    Este código demonstra o processo de treinamento do modelo Random Forest Classifier no conjunto de dados Iris, exportação para o formato ONNX e realização de classificação usando o modelo ONNX. Também avalia a precisão tanto do modelo original quanto do modelo 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)
    

    Resultado:

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

    O modelo Random Forest Classifier (e sua versão ONNX) resolve a tarefa de classificação dos íris de Fisher com 100% de precisão.


    2.7.2. Código no MQL5 para trabalhar com o modelo Random Forest Classifier

    //+------------------------------------------------------------------+
    //|                                  Iris_RandomForestClassifier.mq5 |
    //|                                  Copyright 2023, MetaQuotes Ltd. |
    //|                                             https://www.mql5.com |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2023, MetaQuotes Ltd."
    #property link      "https://www.mql5.com"
    #property version   "1.00"
    
    #include "iris.mqh"
    #resource "rf_iris.onnx" as const uchar ExtModel[];
    
    //+------------------------------------------------------------------+
    //| Test IRIS dataset samples                                        |
    //+------------------------------------------------------------------+
    bool TestSamples(long model,float &input_data[][4], int &model_classes_id[])
      {
    //--- check number of input samples
       ulong batch_size=input_data.Range(0);
       if(batch_size==0)
          return(false);
    //--- prepare output array
       ArrayResize(model_classes_id,(int)batch_size);
    //---
       float output_data[];
    //---
       struct Map
         {
          ulong          key[];
          float          value[];
         } output_data_map[];
    //--- check consistency
       bool res=ArrayResize(output_data,(int)batch_size)==batch_size;
    //---
       if(res)
         {
          //--- set input shape
          ulong input_shape[]= {batch_size,input_data.Range(1)};
          OnnxSetInputShape(model,0,input_shape);
          //--- set output shapeы
          ulong output_shape1[]= {batch_size};
          ulong output_shape2[]= {batch_size};
          OnnxSetOutputShape(model,0,output_shape1);
          OnnxSetOutputShape(model,1,output_shape2);
          //--- run the model
          res=OnnxRun(model,0,input_data,output_data,output_data_map);
          //--- postprocessing
          if(res)
            {
             //--- postprocessing of sequence map data
             //--- find class with maximum probability
             ulong output_keys[];
             float output_values[];
             //---
             for(uint n=0; n<output_data_map.Size(); n++)
               {
                int model_class_id=-1;
                int max_idx=-1;
                float max_value=-1;
                //--- copy to arrays
                ArrayCopy(output_keys,output_data_map[n].key);
                ArrayCopy(output_values,output_data_map[n].value);
                //ArrayPrint(output_keys);
                //ArrayPrint(output_values);
                //--- find the key with maximum probability
                for(int k=0; k<ArraySize(output_values); k++)
                  {
                   if(k==0)
                     {
                      max_idx=0;
                      max_value=output_values[max_idx];
                      model_class_id=(int)output_keys[max_idx];
                     }
                   else
                     {
                      if(output_values[k]>max_value)
                        {
                         max_idx=k;
                         max_value=output_values[max_idx];
                         model_class_id=(int)output_keys[max_idx];
                        }
                     }
                  }
                //--- store the result to the output array
                model_classes_id[n]=model_class_id;
                //Print("model_class_id=",model_class_id);
               }
            }
         }
    //---
       return(res);
      }
    
    //+------------------------------------------------------------------+
    //| Test all samples from IRIS dataset (150)                         |
    //| Here we test all samples with batch=1, sample by sample          |
    //+------------------------------------------------------------------+
    bool TestAllIrisDataset(const long model,const string model_name,double &model_accuracy)
      {
       sIRISsample iris_samples[];
    //--- load dataset from file
       PrepareIrisDataset(iris_samples);
    //--- test
       int total_samples=ArraySize(iris_samples);
       if(total_samples==0)
         {
          Print("iris dataset not prepared");
          return(false);
         }
    //--- show dataset
       for(int k=0; k<total_samples; k++)
         {
          //PrintFormat("%d (%.2f,%.2f,%.2f,%.2f) class %d (%s)",iris_samples[k].sample_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3],iris_samples[k].class_id,iris_samples[k].class_name);
         }
    //--- array for output classes
       int model_output_classes_id[];
    //--- check all Iris dataset samples
       int correct_results=0;
       for(int k=0; k<total_samples; k++)
         {
          //--- input array
          float iris_sample_input_data[1][4];
          //--- prepare input data from kth iris sample dataset
          iris_sample_input_data[0][0]=(float)iris_samples[k].features[0];
          iris_sample_input_data[0][1]=(float)iris_samples[k].features[1];
          iris_sample_input_data[0][2]=(float)iris_samples[k].features[2];
          iris_sample_input_data[0][3]=(float)iris_samples[k].features[3];
          //--- run model
          bool res=TestSamples(model,iris_sample_input_data,model_output_classes_id);
          //--- check result
          if(res)
            {
             if(model_output_classes_id[0]==iris_samples[k].class_id)
               {
                correct_results++;
               }
             else
               {
                PrintFormat("model:%s  sample=%d FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f]",model_name,iris_samples[k].sample_id,model_output_classes_id[0],iris_samples[k].class_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3]);
               }
            }
         }
       model_accuracy=1.0*correct_results/total_samples;
    //---
       PrintFormat("model:%s   correct results: %.2f%%",model_name,100*model_accuracy);
    //---
       return(true);
      }
    
    //+------------------------------------------------------------------+
    //| Here we test batch execution of the model                        |
    //+------------------------------------------------------------------+
    bool TestBatchExecution(const long model,const string model_name,double &model_accuracy)
      {
       model_accuracy=0;
    //--- array for output classes
       int model_output_classes_id[];
       int correct_results=0;
       int total_results=0;
       bool res=false;
    
    //--- run batch with 3 samples
       float input_data_batch3[3][4]=
         {
            {5.1f,3.5f,1.4f,0.2f}, // iris dataset sample id=1, Iris-setosa
            {6.3f,2.5f,4.9f,1.5f}, // iris dataset sample id=73, Iris-versicolor
            {6.3f,2.7f,4.9f,1.8f}  // iris dataset sample id=124, Iris-virginica
         };
       int correct_classes_batch3[3]= {0,1,2};
    //--- run model
       res=TestSamples(model,input_data_batch3,model_output_classes_id);
       if(res)
         {
          //--- check result
          for(int j=0; j<ArraySize(model_output_classes_id); j++)
            {
             //--- check result
             if(model_output_classes_id[j]==correct_classes_batch3[j])
                correct_results++;
             else
               {
                PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch3[j],input_data_batch3[j][0],input_data_batch3[j][1],input_data_batch3[j][2],input_data_batch3[j][3]);
               }
             total_results++;
            }
         }
       else
          return(false);
    
    //--- run batch with 10 samples
       float input_data_batch10[10][4]=
         {
            {5.5f,3.5f,1.3f,0.2f}, // iris dataset sample id=37 (Iris-setosa)
            {4.9f,3.1f,1.5f,0.1f}, // iris dataset sample id=38 (Iris-setosa)
            {4.4f,3.0f,1.3f,0.2f}, // iris dataset sample id=39 (Iris-setosa)
            {5.0f,3.3f,1.4f,0.2f}, // iris dataset sample id=50 (Iris-setosa)
            {7.0f,3.2f,4.7f,1.4f}, // iris dataset sample id=51 (Iris-versicolor)
            {6.4f,3.2f,4.5f,1.5f}, // iris dataset sample id=52 (Iris-versicolor)
            {6.3f,3.3f,6.0f,2.5f}, // iris dataset sample id=101 (Iris-virginica)
            {5.8f,2.7f,5.1f,1.9f}, // iris dataset sample id=102 (Iris-virginica)
            {7.1f,3.0f,5.9f,2.1f}, // iris dataset sample id=103 (Iris-virginica)
            {6.3f,2.9f,5.6f,1.8f}  // iris dataset sample id=104 (Iris-virginica)
         };
    //--- correct classes for all 10 samples in the batch
       int correct_classes_batch10[10]= {0,0,0,0,1,1,2,2,2,2};
    
    //--- run model
       res=TestSamples(model,input_data_batch10,model_output_classes_id);
    //--- check result
       if(res)
         {
          for(int j=0; j<ArraySize(model_output_classes_id); j++)
            {
             if(model_output_classes_id[j]==correct_classes_batch10[j])
                correct_results++;
             else
               {
                double f1=input_data_batch10[j][0];
                double f2=input_data_batch10[j][1];
                double f3=input_data_batch10[j][2];
                double f4=input_data_batch10[j][3];
                PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch10[j],input_data_batch10[j][0],input_data_batch10[j][1],input_data_batch10[j][2],input_data_batch10[j][3]);
               }
             total_results++;
            }
         }
       else
          return(false);
    
    //--- calculate accuracy
       model_accuracy=correct_results/total_results;
    //---
       return(res);
      }
    //+------------------------------------------------------------------+
    //| Script program start function                                    |
    //+------------------------------------------------------------------+
    int OnStart(void)
      {
       string model_name="RandomForestClassifier";
    //---
       long model=OnnxCreateFromBuffer(ExtModel,ONNX_DEFAULT);
       if(model==INVALID_HANDLE)
         {
          PrintFormat("model_name=%s OnnxCreate error %d for",model_name,GetLastError());
         }
       else
         {
          //--- test all dataset
          double model_accuracy=0;
          //-- test sample by sample execution for all Iris dataset
          if(TestAllIrisDataset(model,model_name,model_accuracy))
             PrintFormat("model=%s all samples accuracy=%f",model_name,model_accuracy);
          else
             PrintFormat("error in testing model=%s ",model_name);
          //--- test batch execution for several samples
          if(TestBatchExecution(model,model_name,model_accuracy))
             PrintFormat("model=%s batch test accuracy=%f",model_name,model_accuracy);
          else
             PrintFormat("error in testing model=%s ",model_name);
          //--- release model
          OnnxRelease(model);
         }
       return(0);
      }
    //+------------------------------------------------------------------+
    

    Resultado:

    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
    

    A precisão do modelo ONNX exportado no conjunto de dados completo de íris é de 100%, o que corresponde à precisão do original.


    2.7.3. Representação ONNX do modelo Random Forest Classifier

    Fig.21. Representação ONNX do modelo Random Forest Classifier em Netron

    Fig.21. Representação ONNX do modelo Random Forest Classifier em Netron


    2.8. Gradient Boosting Classifier

    O Gradient Bousting é uma das mais poderosas técnicas de aprendizado de máquina e encontra aplicações em diversos campos, incluindo análise de dados, visão computacional, linguagem natural e análise financeira, devido à sua capacidade de alta precisão e de lidar com uma variedade de dados.   

    O Gradient Boosting Classifier é um método de ensemble de aprendizado de máquina que constrói uma composição de árvores de decisão para resolver tarefas de classificação. Esse método é popular devido à sua capacidade de obter alta precisão e resistência ao ajuste excessivo.

    Princípios do Gradient Boosting Classifier:

    1. Ensemble de árvores de decisão: O Gradient Boosting Classifier constrói uma composição (ensemble) de árvores de decisão, onde cada árvore tenta melhorar as previsões da árvore anterior.
    2. Descida de gradiente: O Gradient Boosting utiliza a descida de gradiente para otimizar a função de perda. Minimiza o erro de classificação, calculando o gradiente da função de perda e atualizando as previsões de acordo com esse gradiente.
    3. Ponderação das árvores: Cada árvore na composição tem um peso, e as previsões de todas as árvores são combinadas levando em conta seus pesos.

    Vantagens do Gradient Boosting Classifier:

    • Alta precisão: O Gradient Boosting Classifier geralmente oferece alta precisão de classificação e é um dos métodos mais poderosos de aprendizado de máquina.
    • Resistência ao sobreajuste: Graças ao uso de regularização e descida de gradiente, este método é bem resistente ao sobreajuste, especialmente quando os hiperparâmetros são ajustados corretamente.
    • Capacidade de trabalhar com diferentes tipos de dados: O Gradient Boosting Classifier pode trabalhar com diferentes tipos de dados, incluindo características numéricas e categóricas.

    Limitações do Gradient Boosting Classifier:

    • Complexidade computacional: O treinamento do Gradient Boosting Classifier pode ser computacionalmente custoso, especialmente se um grande número de árvores ou árvores profundas for usado.
    • Dificuldade de interpretação: Devido à complexidade da composição de muitas árvores, a interpretação dos resultados pode ser desafiadora.
    • O Gradient Boosting nem sempre é adequado para conjuntos de dados pequenos: O Gradient Boosting geralmente requer uma grande quantidade de dados para funcionar efetivamente, e em conjuntos de dados pequenos, ele pode ser propenso ao sobreajuste.

    O Gradient Boosting Classifier é um método poderoso de aprendizado de máquina, frequentemente utilizado em competições de análise de dados e resolve com sucesso muitas tarefas de classificação. Ele é capaz de encontrar dependências não lineares complexas nos dados e possui boa capacidade de generalização quando os hiperparâmetros são ajustados corretamente.


    2.8.1. Código de criação do modelo Gradient Boosting Classifier

    Este código demonstra o processo de treinamento do modelo Gradient Boosting Classifier no conjunto de dados Iris, exportação para o formato ONNX e realização de classificação usando o modelo ONNX. Também avalia a precisão tanto do modelo original quanto do modelo 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)
    

    Resultado:

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

    O modelo Gradient Boosting Classifier (e sua versão ONNX) resolve a tarefa de classificação dos íris de Fisher com 100% de precisão.


    2.8.2. Código no MQL5 para trabalhar com o modelo Gradient Boosting Classifier

    //+------------------------------------------------------------------+
    //|                              Iris_GradientBoostingClassifier.mq5 |
    //|                                  Copyright 2023, MetaQuotes Ltd. |
    //|                                             https://www.mql5.com |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2023, MetaQuotes Ltd."
    #property link      "https://www.mql5.com"
    #property version   "1.00"
    
    #include "iris.mqh"
    #resource "gb_iris.onnx" as const uchar ExtModel[];
    
    //+------------------------------------------------------------------+
    //| Test IRIS dataset samples                                        |
    //+------------------------------------------------------------------+
    bool TestSamples(long model,float &input_data[][4], int &model_classes_id[])
      {
    //--- check number of input samples
       ulong batch_size=input_data.Range(0);
       if(batch_size==0)
          return(false);
    //--- prepare output array
       ArrayResize(model_classes_id,(int)batch_size);
    //---
       float output_data[];
    //---
       struct Map
         {
          ulong          key[];
          float          value[];
         } output_data_map[];
    //--- check consistency
       bool res=ArrayResize(output_data,(int)batch_size)==batch_size;
    //---
       if(res)
         {
          //--- set input shape
          ulong input_shape[]= {batch_size,input_data.Range(1)};
          OnnxSetInputShape(model,0,input_shape);
          //--- set output shapeы
          ulong output_shape1[]= {batch_size};
          ulong output_shape2[]= {batch_size};
          OnnxSetOutputShape(model,0,output_shape1);
          OnnxSetOutputShape(model,1,output_shape2);
          //--- run the model
          res=OnnxRun(model,0,input_data,output_data,output_data_map);
          //--- postprocessing
          if(res)
            {
             //--- postprocessing of sequence map data
             //--- find class with maximum probability
             ulong output_keys[];
             float output_values[];
             //---
             for(uint n=0; n<output_data_map.Size(); n++)
               {
                int model_class_id=-1;
                int max_idx=-1;
                float max_value=-1;
                //--- copy to arrays
                ArrayCopy(output_keys,output_data_map[n].key);
                ArrayCopy(output_values,output_data_map[n].value);
                //ArrayPrint(output_keys);
                //ArrayPrint(output_values);
                //--- find the key with maximum probability
                for(int k=0; k<ArraySize(output_values); k++)
                  {
                   if(k==0)
                     {
                      max_idx=0;
                      max_value=output_values[max_idx];
                      model_class_id=(int)output_keys[max_idx];
                     }
                   else
                     {
                      if(output_values[k]>max_value)
                        {
                         max_idx=k;
                         max_value=output_values[max_idx];
                         model_class_id=(int)output_keys[max_idx];
                        }
                     }
                  }
                //--- store the result to the output array
                model_classes_id[n]=model_class_id;
                //Print("model_class_id=",model_class_id);
               }
            }
         }
    //---
       return(res);
      }
    
    //+------------------------------------------------------------------+
    //| Test all samples from IRIS dataset (150)                         |
    //| Here we test all samples with batch=1, sample by sample          |
    //+------------------------------------------------------------------+
    bool TestAllIrisDataset(const long model,const string model_name,double &model_accuracy)
      {
       sIRISsample iris_samples[];
    //--- load dataset from file
       PrepareIrisDataset(iris_samples);
    //--- test
       int total_samples=ArraySize(iris_samples);
       if(total_samples==0)
         {
          Print("iris dataset not prepared");
          return(false);
         }
    //--- show dataset
       for(int k=0; k<total_samples; k++)
         {
          //PrintFormat("%d (%.2f,%.2f,%.2f,%.2f) class %d (%s)",iris_samples[k].sample_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3],iris_samples[k].class_id,iris_samples[k].class_name);
         }
    //--- array for output classes
       int model_output_classes_id[];
    //--- check all Iris dataset samples
       int correct_results=0;
       for(int k=0; k<total_samples; k++)
         {
          //--- input array
          float iris_sample_input_data[1][4];
          //--- prepare input data from kth iris sample dataset
          iris_sample_input_data[0][0]=(float)iris_samples[k].features[0];
          iris_sample_input_data[0][1]=(float)iris_samples[k].features[1];
          iris_sample_input_data[0][2]=(float)iris_samples[k].features[2];
          iris_sample_input_data[0][3]=(float)iris_samples[k].features[3];
          //--- run model
          bool res=TestSamples(model,iris_sample_input_data,model_output_classes_id);
          //--- check result
          if(res)
            {
             if(model_output_classes_id[0]==iris_samples[k].class_id)
               {
                correct_results++;
               }
             else
               {
                PrintFormat("model:%s  sample=%d FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f]",model_name,iris_samples[k].sample_id,model_output_classes_id[0],iris_samples[k].class_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3]);
               }
            }
         }
       model_accuracy=1.0*correct_results/total_samples;
    //---
       PrintFormat("model:%s   correct results: %.2f%%",model_name,100*model_accuracy);
    //---
       return(true);
      }
    
    //+------------------------------------------------------------------+
    //| Here we test batch execution of the model                        |
    //+------------------------------------------------------------------+
    bool TestBatchExecution(const long model,const string model_name,double &model_accuracy)
      {
       model_accuracy=0;
    //--- array for output classes
       int model_output_classes_id[];
       int correct_results=0;
       int total_results=0;
       bool res=false;
    
    //--- run batch with 3 samples
       float input_data_batch3[3][4]=
         {
            {5.1f,3.5f,1.4f,0.2f}, // iris dataset sample id=1, Iris-setosa
            {6.3f,2.5f,4.9f,1.5f}, // iris dataset sample id=73, Iris-versicolor
            {6.3f,2.7f,4.9f,1.8f}  // iris dataset sample id=124, Iris-virginica
         };
       int correct_classes_batch3[3]= {0,1,2};
    //--- run model
       res=TestSamples(model,input_data_batch3,model_output_classes_id);
       if(res)
         {
          //--- check result
          for(int j=0; j<ArraySize(model_output_classes_id); j++)
            {
             //--- check result
             if(model_output_classes_id[j]==correct_classes_batch3[j])
                correct_results++;
             else
               {
                PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch3[j],input_data_batch3[j][0],input_data_batch3[j][1],input_data_batch3[j][2],input_data_batch3[j][3]);
               }
             total_results++;
            }
         }
       else
          return(false);
    
    //--- run batch with 10 samples
       float input_data_batch10[10][4]=
         {
            {5.5f,3.5f,1.3f,0.2f}, // iris dataset sample id=37 (Iris-setosa)
            {4.9f,3.1f,1.5f,0.1f}, // iris dataset sample id=38 (Iris-setosa)
            {4.4f,3.0f,1.3f,0.2f}, // iris dataset sample id=39 (Iris-setosa)
            {5.0f,3.3f,1.4f,0.2f}, // iris dataset sample id=50 (Iris-setosa)
            {7.0f,3.2f,4.7f,1.4f}, // iris dataset sample id=51 (Iris-versicolor)
            {6.4f,3.2f,4.5f,1.5f}, // iris dataset sample id=52 (Iris-versicolor)
            {6.3f,3.3f,6.0f,2.5f}, // iris dataset sample id=101 (Iris-virginica)
            {5.8f,2.7f,5.1f,1.9f}, // iris dataset sample id=102 (Iris-virginica)
            {7.1f,3.0f,5.9f,2.1f}, // iris dataset sample id=103 (Iris-virginica)
            {6.3f,2.9f,5.6f,1.8f}  // iris dataset sample id=104 (Iris-virginica)
         };
    //--- correct classes for all 10 samples in the batch
       int correct_classes_batch10[10]= {0,0,0,0,1,1,2,2,2,2};
    
    //--- run model
       res=TestSamples(model,input_data_batch10,model_output_classes_id);
    //--- check result
       if(res)
         {
          for(int j=0; j<ArraySize(model_output_classes_id); j++)
            {
             if(model_output_classes_id[j]==correct_classes_batch10[j])
                correct_results++;
             else
               {
                double f1=input_data_batch10[j][0];
                double f2=input_data_batch10[j][1];
                double f3=input_data_batch10[j][2];
                double f4=input_data_batch10[j][3];
                PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch10[j],input_data_batch10[j][0],input_data_batch10[j][1],input_data_batch10[j][2],input_data_batch10[j][3]);
               }
             total_results++;
            }
         }
       else
          return(false);
    
    //--- calculate accuracy
       model_accuracy=correct_results/total_results;
    //---
       return(res);
      }
    //+------------------------------------------------------------------+
    //| Script program start function                                    |
    //+------------------------------------------------------------------+
    int OnStart(void)
      {
       string model_name="GradientBoostingClassifier";
    //---
       long model=OnnxCreateFromBuffer(ExtModel,ONNX_DEFAULT);
       if(model==INVALID_HANDLE)
         {
          PrintFormat("model_name=%s OnnxCreate error %d for",model_name,GetLastError());
         }
       else
         {
          //--- test all dataset
          double model_accuracy=0;
          //-- test sample by sample execution for all Iris dataset
          if(TestAllIrisDataset(model,model_name,model_accuracy))
             PrintFormat("model=%s all samples accuracy=%f",model_name,model_accuracy);
          else
             PrintFormat("error in testing model=%s ",model_name);
          //--- test batch execution for several samples
          if(TestBatchExecution(model,model_name,model_accuracy))
             PrintFormat("model=%s batch test accuracy=%f",model_name,model_accuracy);
          else
             PrintFormat("error in testing model=%s ",model_name);
          //--- release model
          OnnxRelease(model);
         }
       return(0);
      }
    //+------------------------------------------------------------------+
    

    Resultado:

    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
    

    A precisão do modelo ONNX exportado no conjunto de dados completo de íris é de 100%, o que corresponde à precisão do original.


    2.8.3. Representação ONNX do modelo Gradient Boosting Classifier

    Fig.22. Representação ONNX do modelo Gradient Boosting Classifier em Netron

    Fig.22. Representação ONNX do modelo Gradient Boosting Classifier em Netron


    2.9. Adaptive Boosting Classifier

    O classificador AdaBoost (Adaptive Boosting) é um método de aprendizado de máquina baseado em conjunto usado para melhorar a classificação combinando os resultados de vários classificadores fracos (por exemplo, árvores de decisão) para criar um algoritmo mais forte.

    Princípios do AdaBoost Classifier:
    1. Ensemble de classificadores fracos: O AdaBoost começa com a inicialização de cada amostra no conjunto de treinamento com pesos, atribuindo-lhes valores iniciais iguais.
    2. Treinamento de classificadores fracos: Em seguida, o AdaBoost treina um classificador fraco (por exemplo, uma árvore de decisão) no conjunto de treinamento, considerando os pesos das amostras. Este classificador tenta classificar corretamente as amostras.
    3. Redistribuição de pesos: O AdaBoost ajusta os pesos das amostras, aumentando os pesos das amostras classificadas incorretamente e diminuindo os pesos das amostras classificadas corretamente.
    4. Criação de uma composição: O AdaBoost repete o processo de treinamento de classificadores fracos e redistribuição de pesos várias vezes. Então, os resultados desses classificadores fracos são combinados em uma composição, onde cada classificador contribui com base em sua precisão.
    Vantagens do AdaBoost Classifier:
    • Alta precisão: O AdaBoost geralmente fornece alta precisão de classificação pela combinação de vários classificadores fracos.
    • Resistência ao sobreajuste: O AdaBoost possui regularização embutida, o que o torna resistente ao sobreajuste.
    • Capacidade de trabalhar com diferentes classificadores: O AdaBoost pode usar diferentes classificadores base, o que permite adaptá-lo a tarefas específicas.
    Limitações do AdaBoost Classifier:
    • Sensibilidade a valores atípicos: O AdaBoost pode ser sensível a valores atípicos nos dados, pois eles podem receber um peso grande.
    • Nem sempre é adequado para tarefas complexas: Em algumas tarefas complexas, o AdaBoost pode requerer um grande número de classificadores base para alcançar bons resultados.
    • Dependência da qualidade dos classificadores base: O AdaBoost funciona melhor quando os classificadores base são melhores do que adivinhações aleatórias.

    O AdaBoost Classifier é um algoritmo poderoso de aprendizado de máquina frequentemente utilizado na prática para resolver tarefas de classificação. Ele é bem adequado para tarefas com classes binárias e multiclasse e pode ser adaptado a diferentes classificadores base.


    2.9.1. Código de criação do modelo Adaptive Boosting Classifier

    Este código demonstra o processo de treinamento do modelo Adaptive Boosting Classifier no conjunto de dados Iris, exportação para o formato ONNX e realização de classificação usando o modelo ONNX. Também avalia a precisão tanto do modelo original quanto do modelo 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)
    

    Resultado:

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


    2.9.2. Código no MQL5 para trabalhar com o modelo Adaptive Boosting Classifier

    //+------------------------------------------------------------------+
    //|                                      Iris_AdaBoostClassifier.mq5 |
    //|                                  Copyright 2023, MetaQuotes Ltd. |
    //|                                             https://www.mql5.com |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2023, MetaQuotes Ltd."
    #property link      "https://www.mql5.com"
    #property version   "1.00"
    
    #include "iris.mqh"
    #resource "adaboost_iris.onnx" as const uchar ExtModel[];
    
    //+------------------------------------------------------------------+
    //| Test IRIS dataset samples                                        |
    //+------------------------------------------------------------------+
    bool TestSamples(long model,float &input_data[][4], int &model_classes_id[])
      {
    //--- check number of input samples
       ulong batch_size=input_data.Range(0);
       if(batch_size==0)
          return(false);
    //--- prepare output array
       ArrayResize(model_classes_id,(int)batch_size);
    //---
       float output_data[];
    //---
       struct Map
         {
          ulong          key[];
          float          value[];
         } output_data_map[];
    //--- check consistency
       bool res=ArrayResize(output_data,(int)batch_size)==batch_size;
    //---
       if(res)
         {
          //--- set input shape
          ulong input_shape[]= {batch_size,input_data.Range(1)};
          OnnxSetInputShape(model,0,input_shape);
          //--- set output shapeы
          ulong output_shape1[]= {batch_size};
          ulong output_shape2[]= {batch_size};
          OnnxSetOutputShape(model,0,output_shape1);
          OnnxSetOutputShape(model,1,output_shape2);
          //--- run the model
          res=OnnxRun(model,0,input_data,output_data,output_data_map);
          //--- postprocessing
          if(res)
            {
             //--- postprocessing of sequence map data
             //--- find class with maximum probability
             ulong output_keys[];
             float output_values[];
             //---
             for(uint n=0; n<output_data_map.Size(); n++)
               {
                int model_class_id=-1;
                int max_idx=-1;
                float max_value=-1;
                //--- copy to arrays
                ArrayCopy(output_keys,output_data_map[n].key);
                ArrayCopy(output_values,output_data_map[n].value);
                //ArrayPrint(output_keys);
                //ArrayPrint(output_values);
                //--- find the key with maximum probability
                for(int k=0; k<ArraySize(output_values); k++)
                  {
                   if(k==0)
                     {
                      max_idx=0;
                      max_value=output_values[max_idx];
                      model_class_id=(int)output_keys[max_idx];
                     }
                   else
                     {
                      if(output_values[k]>max_value)
                        {
                         max_idx=k;
                         max_value=output_values[max_idx];
                         model_class_id=(int)output_keys[max_idx];
                        }
                     }
                  }
                //--- store the result to the output array
                model_classes_id[n]=model_class_id;
                //Print("model_class_id=",model_class_id);
               }
            }
         }
    //---
       return(res);
      }
    
    //+------------------------------------------------------------------+
    //| Test all samples from IRIS dataset (150)                         |
    //| Here we test all samples with batch=1, sample by sample          |
    //+------------------------------------------------------------------+
    bool TestAllIrisDataset(const long model,const string model_name,double &model_accuracy)
      {
       sIRISsample iris_samples[];
    //--- load dataset from file
       PrepareIrisDataset(iris_samples);
    //--- test
       int total_samples=ArraySize(iris_samples);
       if(total_samples==0)
         {
          Print("iris dataset not prepared");
          return(false);
         }
    //--- show dataset
       for(int k=0; k<total_samples; k++)
         {
          //PrintFormat("%d (%.2f,%.2f,%.2f,%.2f) class %d (%s)",iris_samples[k].sample_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3],iris_samples[k].class_id,iris_samples[k].class_name);
         }
    //--- array for output classes
       int model_output_classes_id[];
    //--- check all Iris dataset samples
       int correct_results=0;
       for(int k=0; k<total_samples; k++)
         {
          //--- input array
          float iris_sample_input_data[1][4];
          //--- prepare input data from kth iris sample dataset
          iris_sample_input_data[0][0]=(float)iris_samples[k].features[0];
          iris_sample_input_data[0][1]=(float)iris_samples[k].features[1];
          iris_sample_input_data[0][2]=(float)iris_samples[k].features[2];
          iris_sample_input_data[0][3]=(float)iris_samples[k].features[3];
          //--- run model
          bool res=TestSamples(model,iris_sample_input_data,model_output_classes_id);
          //--- check result
          if(res)
            {
             if(model_output_classes_id[0]==iris_samples[k].class_id)
               {
                correct_results++;
               }
             else
               {
                PrintFormat("model:%s  sample=%d FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f]",model_name,iris_samples[k].sample_id,model_output_classes_id[0],iris_samples[k].class_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3]);
               }
            }
         }
       model_accuracy=1.0*correct_results/total_samples;
    //---
       PrintFormat("model:%s   correct results: %.2f%%",model_name,100*model_accuracy);
    //---
       return(true);
      }
    
    //+------------------------------------------------------------------+
    //| Here we test batch execution of the model                        |
    //+------------------------------------------------------------------+
    bool TestBatchExecution(const long model,const string model_name,double &model_accuracy)
      {
       model_accuracy=0;
    //--- array for output classes
       int model_output_classes_id[];
       int correct_results=0;
       int total_results=0;
       bool res=false;
    
    //--- run batch with 3 samples
       float input_data_batch3[3][4]=
         {
            {5.1f,3.5f,1.4f,0.2f}, // iris dataset sample id=1, Iris-setosa
            {6.3f,2.5f,4.9f,1.5f}, // iris dataset sample id=73, Iris-versicolor
            {6.3f,2.7f,4.9f,1.8f}  // iris dataset sample id=124, Iris-virginica
         };
       int correct_classes_batch3[3]= {0,1,2};
    //--- run model
       res=TestSamples(model,input_data_batch3,model_output_classes_id);
       if(res)
         {
          //--- check result
          for(int j=0; j<ArraySize(model_output_classes_id); j++)
            {
             //--- check result
             if(model_output_classes_id[j]==correct_classes_batch3[j])
                correct_results++;
             else
               {
                PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch3[j],input_data_batch3[j][0],input_data_batch3[j][1],input_data_batch3[j][2],input_data_batch3[j][3]);
               }
             total_results++;
            }
         }
       else
          return(false);
    
    //--- run batch with 10 samples
       float input_data_batch10[10][4]=
         {
            {5.5f,3.5f,1.3f,0.2f}, // iris dataset sample id=37 (Iris-setosa)
            {4.9f,3.1f,1.5f,0.1f}, // iris dataset sample id=38 (Iris-setosa)
            {4.4f,3.0f,1.3f,0.2f}, // iris dataset sample id=39 (Iris-setosa)
            {5.0f,3.3f,1.4f,0.2f}, // iris dataset sample id=50 (Iris-setosa)
            {7.0f,3.2f,4.7f,1.4f}, // iris dataset sample id=51 (Iris-versicolor)
            {6.4f,3.2f,4.5f,1.5f}, // iris dataset sample id=52 (Iris-versicolor)
            {6.3f,3.3f,6.0f,2.5f}, // iris dataset sample id=101 (Iris-virginica)
            {5.8f,2.7f,5.1f,1.9f}, // iris dataset sample id=102 (Iris-virginica)
            {7.1f,3.0f,5.9f,2.1f}, // iris dataset sample id=103 (Iris-virginica)
            {6.3f,2.9f,5.6f,1.8f}  // iris dataset sample id=104 (Iris-virginica)
         };
    //--- correct classes for all 10 samples in the batch
       int correct_classes_batch10[10]= {0,0,0,0,1,1,2,2,2,2};
    
    //--- run model
       res=TestSamples(model,input_data_batch10,model_output_classes_id);
    //--- check result
       if(res)
         {
          for(int j=0; j<ArraySize(model_output_classes_id); j++)
            {
             if(model_output_classes_id[j]==correct_classes_batch10[j])
                correct_results++;
             else
               {
                double f1=input_data_batch10[j][0];
                double f2=input_data_batch10[j][1];
                double f3=input_data_batch10[j][2];
                double f4=input_data_batch10[j][3];
                PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch10[j],input_data_batch10[j][0],input_data_batch10[j][1],input_data_batch10[j][2],input_data_batch10[j][3]);
               }
             total_results++;
            }
         }
       else
          return(false);
    
    //--- calculate accuracy
       model_accuracy=correct_results/total_results;
    //---
       return(res);
      }
    //+------------------------------------------------------------------+
    //| Script program start function                                    |
    //+------------------------------------------------------------------+
    int OnStart(void)
      {
       string model_name="AdaBoostClassifier";
    //---
       long model=OnnxCreateFromBuffer(ExtModel,ONNX_DEFAULT);
       if(model==INVALID_HANDLE)
         {
          PrintFormat("model_name=%s OnnxCreate error %d for",model_name,GetLastError());
         }
       else
         {
          //--- test all dataset
          double model_accuracy=0;
          //-- test sample by sample execution for all Iris dataset
          if(TestAllIrisDataset(model,model_name,model_accuracy))
             PrintFormat("model=%s all samples accuracy=%f",model_name,model_accuracy);
          else
             PrintFormat("error in testing model=%s ",model_name);
          //--- test batch execution for several samples
          if(TestBatchExecution(model,model_name,model_accuracy))
             PrintFormat("model=%s batch test accuracy=%f",model_name,model_accuracy);
          else
             PrintFormat("error in testing model=%s ",model_name);
          //--- release model
          OnnxRelease(model);
         }
       return(0);
      }
    //+------------------------------------------------------------------+
    

    Resultado:

    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
    

    A precisão do modelo ONNX exportado no iris dataset completo é de 96%, o que corresponde à precisão do original.


    2.9.3. Representação ONNX do modelo Adaptive Boosting Classifier

    Fig.23. Representação ONNX do modelo Adaptive Boosting Classifier em Netron

    Fig.23. Representação ONNX do modelo Adaptive Boosting Classifier em Netron


    2.10. Bootstrap Aggregating Classifier

    O classificador Bagging (Bootstrap Aggregating) é uma técnica de aprendizado de máquina baseada em conjuntos que se baseia na criação de várias subamostras (amostras bootstrap) a partir de dados de treinamento e na construção de modelos separados em cada uma delas e, em seguida, na combinação dos resultados para melhorar a generalização do modelo.

    Princípios do Bagging Classifier:
    1. Criação de subamostras: O Bagging começa com a criação de várias subamostras aleatórias (bootstrap samples) dos dados de treinamento com reposição. Isso significa que as mesmas amostras podem aparecer em várias subamostras, enquanto algumas amostras podem ser omitidas.
    2. Treinamento dos modelos base: Em cada subamostra, um modelo base separado (por exemplo, uma árvore de decisão) é treinado. Cada modelo é treinado independentemente dos outros modelos.
    3. Agregação dos resultados: Após o treinamento de todos os modelos base, os resultados de suas previsões são combinados para obter a previsão final. No caso de classificação binária, isso pode ser feito por votação da maioria.
    Vantagens do Bagging Classifier:
    • Redução da variância: O Bagging reduz a variância do modelo ao média os resultados de vários modelos base, o que pode levar a previsões mais estáveis e confiáveis.
    • Redução de sobreajuste: Como cada modelo base é treinado em subamostras diferentes, o Bagging pode reduzir a tendência do modelo ao sobreajuste.
    • Versatilidade: O Bagging pode usar diferentes modelos base, o que permite adaptá-lo a diferentes tipos de dados e tarefas.
    Limitações do Bagging Classifier:
    • Não melhora o viés: O Bagging tende a reduzir a variância, mas não o viés do modelo. Se os modelos base são propensos ao viés (por exemplo, subajustados), o Bagging não corrigirá esse problema.
    • Nem sempre é adequado para tarefas complexas: Em algumas tarefas complexas, o Bagging pode requerer um grande número de modelos base para alcançar bons resultados.

    O Bagging Classifier é um método eficaz de aprendizado de máquina que pode aumentar a capacidade de generalização do modelo e reduzir o sobreajuste. É frequentemente usado em combinação com diferentes modelos base para resolver uma variedade de tarefas de classificação e regressão.


    2.10.1. Código de criação do modelo Bootstrap Aggregating Classifier

    Este código demonstra o processo de treinamento do modelo Bootstrap Aggregating Classifier no conjunto de dados Iris, exportação para o formato ONNX e realização de classificação usando o modelo ONNX. Também avalia a precisão tanto do modelo original quanto do modelo 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)
    

    Resultado:

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

    O modelo Bootstrap Aggregating Classifier (e sua versão ONNX) alcançou 100% de precisão na classificação do conjunto de dados de íris.


    2.10.2. Código no MQL5 para trabalhar com o modelo Bootstrap Aggregating Classifier

    //+------------------------------------------------------------------+
    //|                          Iris_BootstrapAggregatingClassifier.mq5 |
    //|                                  Copyright 2023, MetaQuotes Ltd. |
    //|                                             https://www.mql5.com |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2023, MetaQuotes Ltd."
    #property link      "https://www.mql5.com"
    #property version   "1.00"
    
    #include "iris.mqh"
    #resource "bagging_iris.onnx" as const uchar ExtModel[];
    
    //+------------------------------------------------------------------+
    //| Test IRIS dataset samples                                        |
    //+------------------------------------------------------------------+
    bool TestSamples(long model,float &input_data[][4], int &model_classes_id[])
      {
    //--- check number of input samples
       ulong batch_size=input_data.Range(0);
       if(batch_size==0)
          return(false);
    //--- prepare output array
       ArrayResize(model_classes_id,(int)batch_size);
    //---
       float output_data[];
    //---
       struct Map
         {
          ulong          key[];
          float          value[];
         } output_data_map[];
    //--- check consistency
       bool res=ArrayResize(output_data,(int)batch_size)==batch_size;
    //---
       if(res)
         {
          //--- set input shape
          ulong input_shape[]= {batch_size,input_data.Range(1)};
          OnnxSetInputShape(model,0,input_shape);
          //--- set output shapeы
          ulong output_shape1[]= {batch_size};
          ulong output_shape2[]= {batch_size};
          OnnxSetOutputShape(model,0,output_shape1);
          OnnxSetOutputShape(model,1,output_shape2);
          //--- run the model
          res=OnnxRun(model,0,input_data,output_data,output_data_map);
          //--- postprocessing
          if(res)
            {
             //--- postprocessing of sequence map data
             //--- find class with maximum probability
             ulong output_keys[];
             float output_values[];
             //---
             for(uint n=0; n<output_data_map.Size(); n++)
               {
                int model_class_id=-1;
                int max_idx=-1;
                float max_value=-1;
                //--- copy to arrays
                ArrayCopy(output_keys,output_data_map[n].key);
                ArrayCopy(output_values,output_data_map[n].value);
                //ArrayPrint(output_keys);
                //ArrayPrint(output_values);
                //--- find the key with maximum probability
                for(int k=0; k<ArraySize(output_values); k++)
                  {
                   if(k==0)
                     {
                      max_idx=0;
                      max_value=output_values[max_idx];
                      model_class_id=(int)output_keys[max_idx];
                     }
                   else
                     {
                      if(output_values[k]>max_value)
                        {
                         max_idx=k;
                         max_value=output_values[max_idx];
                         model_class_id=(int)output_keys[max_idx];
                        }
                     }
                  }
                //--- store the result to the output array
                model_classes_id[n]=model_class_id;
                //Print("model_class_id=",model_class_id);
               }
            }
         }
    //---
       return(res);
      }
    
    //+------------------------------------------------------------------+
    //| Test all samples from IRIS dataset (150)                         |
    //| Here we test all samples with batch=1, sample by sample          |
    //+------------------------------------------------------------------+
    bool TestAllIrisDataset(const long model,const string model_name,double &model_accuracy)
      {
       sIRISsample iris_samples[];
    //--- load dataset from file
       PrepareIrisDataset(iris_samples);
    //--- test
       int total_samples=ArraySize(iris_samples);
       if(total_samples==0)
         {
          Print("iris dataset not prepared");
          return(false);
         }
    //--- show dataset
       for(int k=0; k<total_samples; k++)
         {
          //PrintFormat("%d (%.2f,%.2f,%.2f,%.2f) class %d (%s)",iris_samples[k].sample_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3],iris_samples[k].class_id,iris_samples[k].class_name);
         }
    //--- array for output classes
       int model_output_classes_id[];
    //--- check all Iris dataset samples
       int correct_results=0;
       for(int k=0; k<total_samples; k++)
         {
          //--- input array
          float iris_sample_input_data[1][4];
          //--- prepare input data from kth iris sample dataset
          iris_sample_input_data[0][0]=(float)iris_samples[k].features[0];
          iris_sample_input_data[0][1]=(float)iris_samples[k].features[1];
          iris_sample_input_data[0][2]=(float)iris_samples[k].features[2];
          iris_sample_input_data[0][3]=(float)iris_samples[k].features[3];
          //--- run model
          bool res=TestSamples(model,iris_sample_input_data,model_output_classes_id);
          //--- check result
          if(res)
            {
             if(model_output_classes_id[0]==iris_samples[k].class_id)
               {
                correct_results++;
               }
             else
               {
                PrintFormat("model:%s  sample=%d FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f]",model_name,iris_samples[k].sample_id,model_output_classes_id[0],iris_samples[k].class_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3]);
               }
            }
         }
       model_accuracy=1.0*correct_results/total_samples;
    //---
       PrintFormat("model:%s   correct results: %.2f%%",model_name,100*model_accuracy);
    //---
       return(true);
      }
    
    //+------------------------------------------------------------------+
    //| Here we test batch execution of the model                        |
    //+------------------------------------------------------------------+
    bool TestBatchExecution(const long model,const string model_name,double &model_accuracy)
      {
       model_accuracy=0;
    //--- array for output classes
       int model_output_classes_id[];
       int correct_results=0;
       int total_results=0;
       bool res=false;
    
    //--- run batch with 3 samples
       float input_data_batch3[3][4]=
         {
            {5.1f,3.5f,1.4f,0.2f}, // iris dataset sample id=1, Iris-setosa
            {6.3f,2.5f,4.9f,1.5f}, // iris dataset sample id=73, Iris-versicolor
            {6.3f,2.7f,4.9f,1.8f}  // iris dataset sample id=124, Iris-virginica
         };
       int correct_classes_batch3[3]= {0,1,2};
    //--- run model
       res=TestSamples(model,input_data_batch3,model_output_classes_id);
       if(res)
         {
          //--- check result
          for(int j=0; j<ArraySize(model_output_classes_id); j++)
            {
             //--- check result
             if(model_output_classes_id[j]==correct_classes_batch3[j])
                correct_results++;
             else
               {
                PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch3[j],input_data_batch3[j][0],input_data_batch3[j][1],input_data_batch3[j][2],input_data_batch3[j][3]);
               }
             total_results++;
            }
         }
       else
          return(false);
    
    //--- run batch with 10 samples
       float input_data_batch10[10][4]=
         {
            {5.5f,3.5f,1.3f,0.2f}, // iris dataset sample id=37 (Iris-setosa)
            {4.9f,3.1f,1.5f,0.1f}, // iris dataset sample id=38 (Iris-setosa)
            {4.4f,3.0f,1.3f,0.2f}, // iris dataset sample id=39 (Iris-setosa)
            {5.0f,3.3f,1.4f,0.2f}, // iris dataset sample id=50 (Iris-setosa)
            {7.0f,3.2f,4.7f,1.4f}, // iris dataset sample id=51 (Iris-versicolor)
            {6.4f,3.2f,4.5f,1.5f}, // iris dataset sample id=52 (Iris-versicolor)
            {6.3f,3.3f,6.0f,2.5f}, // iris dataset sample id=101 (Iris-virginica)
            {5.8f,2.7f,5.1f,1.9f}, // iris dataset sample id=102 (Iris-virginica)
            {7.1f,3.0f,5.9f,2.1f}, // iris dataset sample id=103 (Iris-virginica)
            {6.3f,2.9f,5.6f,1.8f}  // iris dataset sample id=104 (Iris-virginica)
         };
    //--- correct classes for all 10 samples in the batch
       int correct_classes_batch10[10]= {0,0,0,0,1,1,2,2,2,2};
    
    //--- run model
       res=TestSamples(model,input_data_batch10,model_output_classes_id);
    //--- check result
       if(res)
         {
          for(int j=0; j<ArraySize(model_output_classes_id); j++)
            {
             if(model_output_classes_id[j]==correct_classes_batch10[j])
                correct_results++;
             else
               {
                double f1=input_data_batch10[j][0];
                double f2=input_data_batch10[j][1];
                double f3=input_data_batch10[j][2];
                double f4=input_data_batch10[j][3];
                PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch10[j],input_data_batch10[j][0],input_data_batch10[j][1],input_data_batch10[j][2],input_data_batch10[j][3]);
               }
             total_results++;
            }
         }
       else
          return(false);
    
    //--- calculate accuracy
       model_accuracy=correct_results/total_results;
    //---
       return(res);
      }
    //+------------------------------------------------------------------+
    //| Script program start function                                    |
    //+------------------------------------------------------------------+
    int OnStart(void)
      {
       string model_name="BootstrapAggregatingClassifier";
    //---
       long model=OnnxCreateFromBuffer(ExtModel,ONNX_DEFAULT);
       if(model==INVALID_HANDLE)
         {
          PrintFormat("model_name=%s OnnxCreate error %d for",model_name,GetLastError());
         }
       else
         {
          //--- test all dataset
          double model_accuracy=0;
          //-- test sample by sample execution for all Iris dataset
          if(TestAllIrisDataset(model,model_name,model_accuracy))
             PrintFormat("model=%s all samples accuracy=%f",model_name,model_accuracy);
          else
             PrintFormat("error in testing model=%s ",model_name);
          //--- test batch execution for several samples
          if(TestBatchExecution(model,model_name,model_accuracy))
             PrintFormat("model=%s batch test accuracy=%f",model_name,model_accuracy);
          else
             PrintFormat("error in testing model=%s ",model_name);
          //--- release model
          OnnxRelease(model);
         }
       return(0);
      }
    //+------------------------------------------------------------------+
    

    Resultado:

    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
    

    A precisão do modelo ONNX exportado no conjunto de dados completo de íris é de 100%, o que corresponde à precisão do original.


    2.10.3. Representação ONNX do modelo Bootstrap Aggregating Classifier

    Fig.24. Representação ONNX do modelo Bootstrap Aggregating Classifier em Netron

    Fig.24. Representação ONNX do modelo Bootstrap Aggregating Classifier no Netron


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

    O K-Nearest Neighbors (K-NN) Classifier é um método de aprendizado de máquina usado para resolver tarefas de classificação e regressão com base na proximidade entre os objetos de dados. Ele se baseia no princípio de que objetos próximos uns dos outros no espaço multidimensional de características tendem a ter características semelhantes e, portanto, podem ter rótulos de classe semelhantes.

    Princípios do K-NN Classifier:

    1. Definição de proximidade: O classificador K-NN determina a proximidade entre o objeto a ser classificado e os outros objetos no conjunto de treinamento. Isso geralmente é feito usando uma métrica de distância, como a distância euclidiana ou a distância de Manhattan.
    2. Escolha do número de vizinhos: O parâmetro K define o número de vizinhos mais próximos que serão usados para classificar o objeto. Normalmente, K é escolhido com base na tarefa e nos dados.
    3. Votação: O K-NN usa votação majoritária entre os K vizinhos mais próximos para determinar a classe do objeto. Por exemplo, se a maioria dos K vizinhos pertence à classe A, o objeto também será classificado como classe A.

    Vantagens do K-NN Classifier:

    • Simplicidade e intuição: O K-NN é um método simples e intuitivamente compreensível, fácil de entender e aplicar.
    • Capacidade de trabalhar com diferentes tipos de dados: O K-NN pode ser usado para diversos tipos de dados, incluindo numéricos, categóricos e textuais.
    • Adaptabilidade a mudanças nos dados: O K-NN pode se adaptar rapidamente a mudanças nos dados, tornando-o adequado para tarefas com dados dinâmicos.

    Limitações do K-NN Classifier:

    • Sensibilidade à escolha de K: Escolher o valor ótimo de K pode ser uma tarefa não trivial. Um K muito pequeno pode levar ao sobreajuste, enquanto um K muito grande pode resultar em underfitting.
    • Sensibilidade à escala das características. O K-NN é sensível ao escalonamento de características, por isso pode ser importante a normalização prévia dos dados.
    • Complexidade computacional: Com grandes volumes de dados e um grande número de características, calcular as distâncias entre todos os pares de objetos pode ser computacionalmente custoso.
    • Falta de interpretabilidade: Os resultados do K-NN podem ser difíceis de interpretar, especialmente se K for grande e houver muitos dados.

    O K-NN Classifier é um método de aprendizado de máquina que pode ser útil em tarefas onde a proximidade dos objetos é importante, como em tarefas de recomendação, classificação de dados textuais e reconhecimento de padrões. Ele é bem adequado para análise inicial de dados e prototipagem rápida de modelos.


    2.11.1. Código de criação do modelo K-Nearest Neighbors (K-NN) Classifier

    Este código demonstra o processo de treinamento do modelo K-Nearest Neighbors (K-NN) Classifier no conjunto de dados Iris, exportação para o formato ONNX e realização de classificação usando o modelo ONNX. Também avalia a precisão tanto do modelo original quanto do modelo 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)
    

    Resultado:

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


    2.11.2. Código no MQL5 para trabalhar com o modelo K-Nearest Neighbors (K-NN) Classifier

    //+------------------------------------------------------------------+
    //|                             Iris_KNearestNeighborsClassifier.mq5 |
    //|                                  Copyright 2023, MetaQuotes Ltd. |
    //|                                             https://www.mql5.com |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2023, MetaQuotes Ltd."
    #property link      "https://www.mql5.com"
    #property version   "1.00"
    
    #include "iris.mqh"
    #resource "knn_iris.onnx" as const uchar ExtModel[];
    
    //+------------------------------------------------------------------+
    //| Test IRIS dataset samples                                        |
    //+------------------------------------------------------------------+
    bool TestSamples(long model,float &input_data[][4], int &model_classes_id[])
      {
    //--- check number of input samples
       ulong batch_size=input_data.Range(0);
       if(batch_size==0)
          return(false);
    //--- prepare output array
       ArrayResize(model_classes_id,(int)batch_size);
    //---
       float output_data[];
    //---
       struct Map
         {
          ulong          key[];
          float          value[];
         } output_data_map[];
    //--- check consistency
       bool res=ArrayResize(output_data,(int)batch_size)==batch_size;
    //---
       if(res)
         {
          //--- set input shape
          ulong input_shape[]= {batch_size,input_data.Range(1)};
          OnnxSetInputShape(model,0,input_shape);
          //--- set output shapeы
          ulong output_shape1[]= {batch_size};
          ulong output_shape2[]= {batch_size};
          OnnxSetOutputShape(model,0,output_shape1);
          OnnxSetOutputShape(model,1,output_shape2);
          //--- run the model
          res=OnnxRun(model,0,input_data,output_data,output_data_map);
          //--- postprocessing
          if(res)
            {
             //--- postprocessing of sequence map data
             //--- find class with maximum probability
             ulong output_keys[];
             float output_values[];
             //---
             for(uint n=0; n<output_data_map.Size(); n++)
               {
                int model_class_id=-1;
                int max_idx=-1;
                float max_value=-1;
                //--- copy to arrays
                ArrayCopy(output_keys,output_data_map[n].key);
                ArrayCopy(output_values,output_data_map[n].value);
                //ArrayPrint(output_keys);
                //ArrayPrint(output_values);
                //--- find the key with maximum probability
                for(int k=0; k<ArraySize(output_values); k++)
                  {
                   if(k==0)
                     {
                      max_idx=0;
                      max_value=output_values[max_idx];
                      model_class_id=(int)output_keys[max_idx];
                     }
                   else
                     {
                      if(output_values[k]>max_value)
                        {
                         max_idx=k;
                         max_value=output_values[max_idx];
                         model_class_id=(int)output_keys[max_idx];
                        }
                     }
                  }
                //--- store the result to the output array
                model_classes_id[n]=model_class_id;
                //Print("model_class_id=",model_class_id);
               }
            }
         }
    //---
       return(res);
      }
    
    //+------------------------------------------------------------------+
    //| Test all samples from IRIS dataset (150)                         |
    //| Here we test all samples with batch=1, sample by sample          |
    //+------------------------------------------------------------------+
    bool TestAllIrisDataset(const long model,const string model_name,double &model_accuracy)
      {
       sIRISsample iris_samples[];
    //--- load dataset from file
       PrepareIrisDataset(iris_samples);
    //--- test
       int total_samples=ArraySize(iris_samples);
       if(total_samples==0)
         {
          Print("iris dataset not prepared");
          return(false);
         }
    //--- show dataset
       for(int k=0; k<total_samples; k++)
         {
          //PrintFormat("%d (%.2f,%.2f,%.2f,%.2f) class %d (%s)",iris_samples[k].sample_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3],iris_samples[k].class_id,iris_samples[k].class_name);
         }
    //--- array for output classes
       int model_output_classes_id[];
    //--- check all Iris dataset samples
       int correct_results=0;
       for(int k=0; k<total_samples; k++)
         {
          //--- input array
          float iris_sample_input_data[1][4];
          //--- prepare input data from kth iris sample dataset
          iris_sample_input_data[0][0]=(float)iris_samples[k].features[0];
          iris_sample_input_data[0][1]=(float)iris_samples[k].features[1];
          iris_sample_input_data[0][2]=(float)iris_samples[k].features[2];
          iris_sample_input_data[0][3]=(float)iris_samples[k].features[3];
          //--- run model
          bool res=TestSamples(model,iris_sample_input_data,model_output_classes_id);
          //--- check result
          if(res)
            {
             if(model_output_classes_id[0]==iris_samples[k].class_id)
               {
                correct_results++;
               }
             else
               {
                PrintFormat("model:%s  sample=%d FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f]",model_name,iris_samples[k].sample_id,model_output_classes_id[0],iris_samples[k].class_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3]);
               }
            }
         }
       model_accuracy=1.0*correct_results/total_samples;
    //---
       PrintFormat("model:%s   correct results: %.2f%%",model_name,100*model_accuracy);
    //---
       return(true);
      }
    
    //+------------------------------------------------------------------+
    //| Here we test batch execution of the model                        |
    //+------------------------------------------------------------------+
    bool TestBatchExecution(const long model,const string model_name,double &model_accuracy)
      {
       model_accuracy=0;
    //--- array for output classes
       int model_output_classes_id[];
       int correct_results=0;
       int total_results=0;
       bool res=false;
    
    //--- run batch with 3 samples
       float input_data_batch3[3][4]=
         {
            {5.1f,3.5f,1.4f,0.2f}, // iris dataset sample id=1, Iris-setosa
            {6.3f,2.5f,4.9f,1.5f}, // iris dataset sample id=73, Iris-versicolor
            {6.3f,2.7f,4.9f,1.8f}  // iris dataset sample id=124, Iris-virginica
         };
       int correct_classes_batch3[3]= {0,1,2};
    //--- run model
       res=TestSamples(model,input_data_batch3,model_output_classes_id);
       if(res)
         {
          //--- check result
          for(int j=0; j<ArraySize(model_output_classes_id); j++)
            {
             //--- check result
             if(model_output_classes_id[j]==correct_classes_batch3[j])
                correct_results++;
             else
               {
                PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch3[j],input_data_batch3[j][0],input_data_batch3[j][1],input_data_batch3[j][2],input_data_batch3[j][3]);
               }
             total_results++;
            }
         }
       else
          return(false);
    
    //--- run batch with 10 samples
       float input_data_batch10[10][4]=
         {
            {5.5f,3.5f,1.3f,0.2f}, // iris dataset sample id=37 (Iris-setosa)
            {4.9f,3.1f,1.5f,0.1f}, // iris dataset sample id=38 (Iris-setosa)
            {4.4f,3.0f,1.3f,0.2f}, // iris dataset sample id=39 (Iris-setosa)
            {5.0f,3.3f,1.4f,0.2f}, // iris dataset sample id=50 (Iris-setosa)
            {7.0f,3.2f,4.7f,1.4f}, // iris dataset sample id=51 (Iris-versicolor)
            {6.4f,3.2f,4.5f,1.5f}, // iris dataset sample id=52 (Iris-versicolor)
            {6.3f,3.3f,6.0f,2.5f}, // iris dataset sample id=101 (Iris-virginica)
            {5.8f,2.7f,5.1f,1.9f}, // iris dataset sample id=102 (Iris-virginica)
            {7.1f,3.0f,5.9f,2.1f}, // iris dataset sample id=103 (Iris-virginica)
            {6.3f,2.9f,5.6f,1.8f}  // iris dataset sample id=104 (Iris-virginica)
         };
    //--- correct classes for all 10 samples in the batch
       int correct_classes_batch10[10]= {0,0,0,0,1,1,2,2,2,2};
    
    //--- run model
       res=TestSamples(model,input_data_batch10,model_output_classes_id);
    //--- check result
       if(res)
         {
          for(int j=0; j<ArraySize(model_output_classes_id); j++)
            {
             if(model_output_classes_id[j]==correct_classes_batch10[j])
                correct_results++;
             else
               {
                double f1=input_data_batch10[j][0];
                double f2=input_data_batch10[j][1];
                double f3=input_data_batch10[j][2];
                double f4=input_data_batch10[j][3];
                PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch10[j],input_data_batch10[j][0],input_data_batch10[j][1],input_data_batch10[j][2],input_data_batch10[j][3]);
               }
             total_results++;
            }
         }
       else
          return(false);
    
    //--- calculate accuracy
       model_accuracy=correct_results/total_results;
    //---
       return(res);
      }
    //+------------------------------------------------------------------+
    //| Script program start function                                    |
    //+------------------------------------------------------------------+
    int OnStart(void)
      {
       string model_name="KNearestNeighborsClassifier";
    //---
       long model=OnnxCreateFromBuffer(ExtModel,ONNX_DEFAULT);
       if(model==INVALID_HANDLE)
         {
          PrintFormat("model_name=%s OnnxCreate error %d for",model_name,GetLastError());
         }
       else
         {
          //--- test all dataset
          double model_accuracy=0;
          //-- test sample by sample execution for all Iris dataset
          if(TestAllIrisDataset(model,model_name,model_accuracy))
             PrintFormat("model=%s all samples accuracy=%f",model_name,model_accuracy);
          else
             PrintFormat("error in testing model=%s ",model_name);
          //--- test batch execution for several samples
          if(TestBatchExecution(model,model_name,model_accuracy))
             PrintFormat("model=%s batch test accuracy=%f",model_name,model_accuracy);
          else
             PrintFormat("error in testing model=%s ",model_name);
          //--- release model
          OnnxRelease(model);
         }
       return(0);
      }
    //+------------------------------------------------------------------+
    

    Resultado:

    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
    

    A precisão do modelo ONNX exportado no iris dataset completo é de 96%, o que corresponde à precisão do original.


    2.11.3. Representação ONNX do modelo K-Nearest Neighbors (K-NN) Classifier

    Fig.25. Representação ONNX do modelo K-Nearest Neighbors no Netron

    Fig.25. Representação ONNX do modelo K-Nearest Neighbors no Netron


    2.12. Decision Tree Classifier

    O Decision Tree Classifier é um método de aprendizado de máquina utilizado para tarefas de classificação, baseado na construção de uma árvore de decisão. Este método divide o conjunto de dados em subgrupos menores, realizando uma série de testes condicionais nas características e determina a classe de um objeto com base no caminho que ele percorre na árvore.

    Princípios do Decision Tree Classifier:

    1. Construção da árvore de decisão: Inicialmente, todos os dados são representados na raiz da árvore. Para cada nó da árvore, os dados são divididos em duas ou mais subgrupos, com base nos valores de uma das características. Isso é feito de forma a minimizar ao máximo a incerteza (por exemplo, entropia ou índice de Gini) em cada subgrupo.
    2. Construção recursiva: O processo de divisão dos dados é realizado recursivamente até que se atinjam as folhas da árvore. As folhas representam as classes finais dos objetos.
    3. Tomada de decisão: Quando um objeto é inserido na árvore, ele segue o caminho da raiz até uma das folhas, onde sua classe é determinada com base na maioria dos objetos naquela folha.
    Vantagens do Decision Tree Classifier:
    • Simplicidade de interpretação: As árvores de decisão são facilmente interpretáveis e visualizáveis. As regras de decisão usadas para classificação são compreensíveis.
    • Trabalho com diferentes tipos de dados: O classificador de árvore de decisão pode trabalhar tanto com características numéricas quanto com características categóricas.
    • Identificação de características importantes: As árvores de decisão podem avaliar a importância das características, o que ajuda os pesquisadores e engenheiros de características a entender os dados.
    Limitações do Decision Tree Classifier:
    • Sobreajuste: Árvores grandes e profundas podem tender ao sobreajuste, tornando-as menos generalizáveis para novos dados.
    • Sensibilidade ao ruído: As árvores de decisão podem ser sensíveis ao ruído e valores atípicos nos dados.
    • Construção gananciosa: As árvores de decisão são construídas usando um algoritmo ganancioso, o que pode levar à impossibilidade de encontrar a solução ótima global.
    • Instabilidade com mudanças nos dados: Pequenas mudanças nos dados podem levar a grandes mudanças na estrutura da árvore.

    O Decision Tree Classifier é um método útil de aprendizado de máquina para tarefas de classificação, especialmente em situações onde a interpretabilidade do modelo é importante e é necessário entender quais características influenciam a decisão. Este método também pode ser usado em métodos de ensemble, como Random Forest e Gradient Boosting.


    2.12.1. Código de criação do modelo Decision Tree Classifier

    Este código demonstra o processo de treinamento do modelo Decision Tree Classifier no conjunto de dados Iris, exportação para o formato ONNX e realização de classificação usando o modelo ONNX. Também avalia a precisão tanto do modelo original quanto do modelo 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)
    

    Resultado:

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

    O modelo Decision Tree Classifier (e sua versão ONNX) mostrou 100% de precisão na classificação do conjunto completo de íris de Fisher.


    2.12.2. Código no MQL5 para trabalhar com o modelo Decision Tree Classifier

    //+------------------------------------------------------------------+
    //|                                  Iris_DecisionTreeClassifier.mq5 |
    //|                                  Copyright 2023, MetaQuotes Ltd. |
    //|                                             https://www.mql5.com |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2023, MetaQuotes Ltd."
    #property link      "https://www.mql5.com"
    #property version   "1.00"
    
    #include "iris.mqh"
    #resource "decision_tree_iris.onnx" as const uchar ExtModel[];
    
    //+------------------------------------------------------------------+
    //| Test IRIS dataset samples                                        |
    //+------------------------------------------------------------------+
    bool TestSamples(long model,float &input_data[][4], int &model_classes_id[])
      {
    //--- check number of input samples
       ulong batch_size=input_data.Range(0);
       if(batch_size==0)
          return(false);
    //--- prepare output array
       ArrayResize(model_classes_id,(int)batch_size);
    //---
       float output_data[];
    //---
       struct Map
         {
          ulong          key[];
          float          value[];
         } output_data_map[];
    //--- check consistency
       bool res=ArrayResize(output_data,(int)batch_size)==batch_size;
    //---
       if(res)
         {
          //--- set input shape
          ulong input_shape[]= {batch_size,input_data.Range(1)};
          OnnxSetInputShape(model,0,input_shape);
          //--- set output shapeы
          ulong output_shape1[]= {batch_size};
          ulong output_shape2[]= {batch_size};
          OnnxSetOutputShape(model,0,output_shape1);
          OnnxSetOutputShape(model,1,output_shape2);
          //--- run the model
          res=OnnxRun(model,0,input_data,output_data,output_data_map);
          //--- postprocessing
          if(res)
            {
             //--- postprocessing of sequence map data
             //--- find class with maximum probability
             ulong output_keys[];
             float output_values[];
             //---
             for(uint n=0; n<output_data_map.Size(); n++)
               {
                int model_class_id=-1;
                int max_idx=-1;
                float max_value=-1;
                //--- copy to arrays
                ArrayCopy(output_keys,output_data_map[n].key);
                ArrayCopy(output_values,output_data_map[n].value);
                //ArrayPrint(output_keys);
                //ArrayPrint(output_values);
                //--- find the key with maximum probability
                for(int k=0; k<ArraySize(output_values); k++)
                  {
                   if(k==0)
                     {
                      max_idx=0;
                      max_value=output_values[max_idx];
                      model_class_id=(int)output_keys[max_idx];
                     }
                   else
                     {
                      if(output_values[k]>max_value)
                        {
                         max_idx=k;
                         max_value=output_values[max_idx];
                         model_class_id=(int)output_keys[max_idx];
                        }
                     }
                  }
                //--- store the result to the output array
                model_classes_id[n]=model_class_id;
                //Print("model_class_id=",model_class_id);
               }
            }
         }
    //---
       return(res);
      }
    
    //+------------------------------------------------------------------+
    //| Test all samples from IRIS dataset (150)                         |
    //| Here we test all samples with batch=1, sample by sample          |
    //+------------------------------------------------------------------+
    bool TestAllIrisDataset(const long model,const string model_name,double &model_accuracy)
      {
       sIRISsample iris_samples[];
    //--- load dataset from file
       PrepareIrisDataset(iris_samples);
    //--- test
       int total_samples=ArraySize(iris_samples);
       if(total_samples==0)
         {
          Print("iris dataset not prepared");
          return(false);
         }
    //--- show dataset
       for(int k=0; k<total_samples; k++)
         {
          //PrintFormat("%d (%.2f,%.2f,%.2f,%.2f) class %d (%s)",iris_samples[k].sample_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3],iris_samples[k].class_id,iris_samples[k].class_name);
         }
    //--- array for output classes
       int model_output_classes_id[];
    //--- check all Iris dataset samples
       int correct_results=0;
       for(int k=0; k<total_samples; k++)
         {
          //--- input array
          float iris_sample_input_data[1][4];
          //--- prepare input data from kth iris sample dataset
          iris_sample_input_data[0][0]=(float)iris_samples[k].features[0];
          iris_sample_input_data[0][1]=(float)iris_samples[k].features[1];
          iris_sample_input_data[0][2]=(float)iris_samples[k].features[2];
          iris_sample_input_data[0][3]=(float)iris_samples[k].features[3];
          //--- run model
          bool res=TestSamples(model,iris_sample_input_data,model_output_classes_id);
          //--- check result
          if(res)
            {
             if(model_output_classes_id[0]==iris_samples[k].class_id)
               {
                correct_results++;
               }
             else
               {
                PrintFormat("model:%s  sample=%d FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f]",model_name,iris_samples[k].sample_id,model_output_classes_id[0],iris_samples[k].class_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3]);
               }
            }
         }
       model_accuracy=1.0*correct_results/total_samples;
    //---
       PrintFormat("model:%s   correct results: %.2f%%",model_name,100*model_accuracy);
    //---
       return(true);
      }
    
    //+------------------------------------------------------------------+
    //| Here we test batch execution of the model                        |
    //+------------------------------------------------------------------+
    bool TestBatchExecution(const long model,const string model_name,double &model_accuracy)
      {
       model_accuracy=0;
    //--- array for output classes
       int model_output_classes_id[];
       int correct_results=0;
       int total_results=0;
       bool res=false;
    
    //--- run batch with 3 samples
       float input_data_batch3[3][4]=
         {
            {5.1f,3.5f,1.4f,0.2f}, // iris dataset sample id=1, Iris-setosa
            {6.3f,2.5f,4.9f,1.5f}, // iris dataset sample id=73, Iris-versicolor
            {6.3f,2.7f,4.9f,1.8f}  // iris dataset sample id=124, Iris-virginica
         };
       int correct_classes_batch3[3]= {0,1,2};
    //--- run model
       res=TestSamples(model,input_data_batch3,model_output_classes_id);
       if(res)
         {
          //--- check result
          for(int j=0; j<ArraySize(model_output_classes_id); j++)
            {
             //--- check result
             if(model_output_classes_id[j]==correct_classes_batch3[j])
                correct_results++;
             else
               {
                PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch3[j],input_data_batch3[j][0],input_data_batch3[j][1],input_data_batch3[j][2],input_data_batch3[j][3]);
               }
             total_results++;
            }
         }
       else
          return(false);
    
    //--- run batch with 10 samples
       float input_data_batch10[10][4]=
         {
            {5.5f,3.5f,1.3f,0.2f}, // iris dataset sample id=37 (Iris-setosa)
            {4.9f,3.1f,1.5f,0.1f}, // iris dataset sample id=38 (Iris-setosa)
            {4.4f,3.0f,1.3f,0.2f}, // iris dataset sample id=39 (Iris-setosa)
            {5.0f,3.3f,1.4f,0.2f}, // iris dataset sample id=50 (Iris-setosa)
            {7.0f,3.2f,4.7f,1.4f}, // iris dataset sample id=51 (Iris-versicolor)
            {6.4f,3.2f,4.5f,1.5f}, // iris dataset sample id=52 (Iris-versicolor)
            {6.3f,3.3f,6.0f,2.5f}, // iris dataset sample id=101 (Iris-virginica)
            {5.8f,2.7f,5.1f,1.9f}, // iris dataset sample id=102 (Iris-virginica)
            {7.1f,3.0f,5.9f,2.1f}, // iris dataset sample id=103 (Iris-virginica)
            {6.3f,2.9f,5.6f,1.8f}  // iris dataset sample id=104 (Iris-virginica)
         };
    //--- correct classes for all 10 samples in the batch
       int correct_classes_batch10[10]= {0,0,0,0,1,1,2,2,2,2};
    
    //--- run model
       res=TestSamples(model,input_data_batch10,model_output_classes_id);
    //--- check result
       if(res)
         {
          for(int j=0; j<ArraySize(model_output_classes_id); j++)
            {
             if(model_output_classes_id[j]==correct_classes_batch10[j])
                correct_results++;
             else
               {
                double f1=input_data_batch10[j][0];
                double f2=input_data_batch10[j][1];
                double f3=input_data_batch10[j][2];
                double f4=input_data_batch10[j][3];
                PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch10[j],input_data_batch10[j][0],input_data_batch10[j][1],input_data_batch10[j][2],input_data_batch10[j][3]);
               }
             total_results++;
            }
         }
       else
          return(false);
    
    //--- calculate accuracy
       model_accuracy=correct_results/total_results;
    //---
       return(res);
      }
    //+------------------------------------------------------------------+
    //| Script program start function                                    |
    //+------------------------------------------------------------------+
    int OnStart(void)
      {
       string model_name="DecisionTreeClassifier";
    //---
       long model=OnnxCreateFromBuffer(ExtModel,ONNX_DEFAULT);
       if(model==INVALID_HANDLE)
         {
          PrintFormat("model_name=%s OnnxCreate error %d for",model_name,GetLastError());
         }
       else
         {
          //--- test all dataset
          double model_accuracy=0;
          //-- test sample by sample execution for all Iris dataset
          if(TestAllIrisDataset(model,model_name,model_accuracy))
             PrintFormat("model=%s all samples accuracy=%f",model_name,model_accuracy);
          else
             PrintFormat("error in testing model=%s ",model_name);
          //--- test batch execution for several samples
          if(TestBatchExecution(model,model_name,model_accuracy))
             PrintFormat("model=%s batch test accuracy=%f",model_name,model_accuracy);
          else
             PrintFormat("error in testing model=%s ",model_name);
          //--- release model
          OnnxRelease(model);
         }
       return(0);
      }
    //+------------------------------------------------------------------+
    

    Resultado:

    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
    

    A precisão do modelo ONNX exportado no conjunto de dados completo de íris é de 100%, o que corresponde à precisão do original.


    2.12.3. Representação ONNX do modelo Decision Tree Classifier

    Fig.26. Representação ONNX do modelo Decision Tree Classifier no Netron

    Fig.26. Representação ONNX do modelo Decision Tree Classifier no Netron


    Nota sobre os modelos LogisticRegression e LogisticRegressionCV

    LogisticRegression e LogisticRegressionCV são dois classificadores usados para classificação binária por meio de regressão logística, mas diferem na maneira como os parâmetros do modelo são ajustados:

        LogisticRegression:

    • LogisticRegression é um classificador que usa a função logística para modelar a probabilidade de pertencer a uma de duas classes (classificação binária).
    • Ele oferece parâmetros básicos para ajuste, como C (força inversa da regularização), penalty (tipo de regularização, por exemplo, L1 ou L2), solver (algoritmo de otimização) e outros.
    • Ao usar LogisticRegression, você normalmente escolhe os valores dos parâmetros e suas combinações e, em seguida, treina o modelo nos dados.

        LogisticRegressionCV:

    • LogisticRegressionCV é uma extensão do LogisticRegression que fornece suporte embutido para validação cruzada e seleção do valor ótimo do parâmetro de regularização C.
    • Em vez de selecionar manualmente C, você pode fornecer ao LogisticRegressionCV uma lista de valores de C para explorar e especificar o método de validação cruzada (por exemplo, através do parâmetro cv).
    • O LogisticRegressionCV automaticamente selecionará o valor ótimo de C que proporciona o melhor desempenho na validação cruzada.
    • Isso é conveniente quando você precisa ajustar a regularização automaticamente, especialmente se você tem muitos dados ou não sabe qual valor de C escolher.

    Assim, a principal diferença entre os dois é o nível de automação na definição dos parâmetros C. O LogisticRegression requer ajuste manual de C, enquanto o LogisticRegressionCV permite que você selecione automaticamente o valor ideal de C usando validação cruzada. A escolha entre eles depende de suas necessidades e do desejo de automação no processo de ajuste do modelo.



    2.13. Logistic Regression Classifier

    O classificador de regressão logística é um método de aprendizado de máquina usado para tarefas de classificação binária e multiclasse. O termo "regressão" pode ser enganador, mas a regressão logística na verdade prevê a probabilidade de um objeto pertencer a uma das classes. Com base nessas probabilidades, é tomada a decisão final sobre a classificação do objeto.

    Princípios de funcionamento do classificador de regressão logística:
    1. Previsão de probabilidade: A regressão logística modela a probabilidade de um objeto pertencer a uma classe específica usando a função logística (sigmoidal).
    2. Definição da fronteira de decisão: Com base nas probabilidades previstas, a regressão logística define uma fronteira de decisão que separa as classes. Se a probabilidade exceder um determinado limiar (geralmente 0,5), o objeto é classificado como pertencente a uma classe, caso contrário, a outra.
    3. Treinamento dos parâmetros: O modelo de regressão logística é treinado em um conjunto de dados de treinamento ajustando os pesos (coeficientes) das características para minimizar a função de perda.
    Vantagens do classificador de regressão logística:
    • Simplicidade e interpretabilidade: A regressão logística é um modelo simples, facilmente interpretável em termos do impacto das características na previsão das classes.
    • Eficiência em grandes volumes de dados: A regressão logística pode lidar eficientemente com grandes volumes de dados e ser treinada rapidamente.
    • Uso em métodos de ensemble: A regressão logística pode ser usada como um classificador base em métodos de ensemble, como empilhamento.
    Limitações do classificador de regressão logística:
    • Linearidade: A regressão logística assume uma dependência linear entre as características e o logaritmo das chances, o que pode ser insuficiente para tarefas complexas.
    • Limitação para classificação multiclasse: Na sua forma original, a regressão logística é destinada à classificação binária, mas existem métodos para estendê-la para classificação multiclasse, como One-vs-All (One-vs-Rest).
    • Sensibilidade a valores atípicos: A regressão logística pode ser sensível a valores atípicos nos dados.

    A regressão logística é um método clássico de aprendizado de máquina, amplamente usado na prática para tarefas de classificação, especialmente em casos onde a interpretabilidade do modelo é importante e os dados têm uma estrutura linear ou quase linear. Ela também é usada em estatística e análise de dados médicos para avaliar o impacto de fatores na probabilidade de eventos.


    2.13.1. Código de criação do modelo Logistic Regression Classifier

    Este código demonstra o processo de treinamento do modelo Logistic Regression Classifier no conjunto de dados Iris, exportação para o formato ONNX e realização de classificação usando o modelo ONNX. Também avalia a precisão tanto do modelo original quanto do modelo 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)
    

    Resultado:

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


    2.13.2. Código no MQL5 para trabalhar com o modelo Regression Classifier

    //+------------------------------------------------------------------+
    //|                            Iris_LogisticRegressionClassifier.mq5 |
    //|                                  Copyright 2023, MetaQuotes Ltd. |
    //|                                             https://www.mql5.com |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2023, MetaQuotes Ltd."
    #property link      "https://www.mql5.com"
    #property version   "1.00"
    
    #include "iris.mqh"
    #resource "logistic_regression_iris.onnx" as const uchar ExtModel[];
    
    //+------------------------------------------------------------------+
    //| Test IRIS dataset samples                                        |
    //+------------------------------------------------------------------+
    bool TestSamples(long model,float &input_data[][4], int &model_classes_id[])
      {
    //--- check number of input samples
       ulong batch_size=input_data.Range(0);
       if(batch_size==0)
          return(false);
    //--- prepare output array
       ArrayResize(model_classes_id,(int)batch_size);
    //---
       float output_data[];
    //---
       struct Map
         {
          ulong          key[];
          float          value[];
         } output_data_map[];
    //--- check consistency
       bool res=ArrayResize(output_data,(int)batch_size)==batch_size;
    //---
       if(res)
         {
          //--- set input shape
          ulong input_shape[]= {batch_size,input_data.Range(1)};
          OnnxSetInputShape(model,0,input_shape);
          //--- set output shapeы
          ulong output_shape1[]= {batch_size};
          ulong output_shape2[]= {batch_size};
          OnnxSetOutputShape(model,0,output_shape1);
          OnnxSetOutputShape(model,1,output_shape2);
          //--- run the model
          res=OnnxRun(model,0,input_data,output_data,output_data_map);
          //--- postprocessing
          if(res)
            {
             //--- postprocessing of sequence map data
             //--- find class with maximum probability
             ulong output_keys[];
             float output_values[];
             //---
             for(uint n=0; n<output_data_map.Size(); n++)
               {
                int model_class_id=-1;
                int max_idx=-1;
                float max_value=-1;
                //--- copy to arrays
                ArrayCopy(output_keys,output_data_map[n].key);
                ArrayCopy(output_values,output_data_map[n].value);
                //ArrayPrint(output_keys);
                //ArrayPrint(output_values);
                //--- find the key with maximum probability
                for(int k=0; k<ArraySize(output_values); k++)
                  {
                   if(k==0)
                     {
                      max_idx=0;
                      max_value=output_values[max_idx];
                      model_class_id=(int)output_keys[max_idx];
                     }
                   else
                     {
                      if(output_values[k]>max_value)
                        {
                         max_idx=k;
                         max_value=output_values[max_idx];
                         model_class_id=(int)output_keys[max_idx];
                        }
                     }
                  }
                //--- store the result to the output array
                model_classes_id[n]=model_class_id;
                //Print("model_class_id=",model_class_id);
               }
            }
         }
    //---
       return(res);
      }
    
    //+------------------------------------------------------------------+
    //| Test all samples from IRIS dataset (150)                         |
    //| Here we test all samples with batch=1, sample by sample          |
    //+------------------------------------------------------------------+
    bool TestAllIrisDataset(const long model,const string model_name,double &model_accuracy)
      {
       sIRISsample iris_samples[];
    //--- load dataset from file
       PrepareIrisDataset(iris_samples);
    //--- test
       int total_samples=ArraySize(iris_samples);
       if(total_samples==0)
         {
          Print("iris dataset not prepared");
          return(false);
         }
    //--- show dataset
       for(int k=0; k<total_samples; k++)
         {
          //PrintFormat("%d (%.2f,%.2f,%.2f,%.2f) class %d (%s)",iris_samples[k].sample_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3],iris_samples[k].class_id,iris_samples[k].class_name);
         }
    //--- array for output classes
       int model_output_classes_id[];
    //--- check all Iris dataset samples
       int correct_results=0;
       for(int k=0; k<total_samples; k++)
         {
          //--- input array
          float iris_sample_input_data[1][4];
          //--- prepare input data from kth iris sample dataset
          iris_sample_input_data[0][0]=(float)iris_samples[k].features[0];
          iris_sample_input_data[0][1]=(float)iris_samples[k].features[1];
          iris_sample_input_data[0][2]=(float)iris_samples[k].features[2];
          iris_sample_input_data[0][3]=(float)iris_samples[k].features[3];
          //--- run model
          bool res=TestSamples(model,iris_sample_input_data,model_output_classes_id);
          //--- check result
          if(res)
            {
             if(model_output_classes_id[0]==iris_samples[k].class_id)
               {
                correct_results++;
               }
             else
               {
                PrintFormat("model:%s  sample=%d FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f]",model_name,iris_samples[k].sample_id,model_output_classes_id[0],iris_samples[k].class_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3]);
               }
            }
         }
       model_accuracy=1.0*correct_results/total_samples;
    //---
       PrintFormat("model:%s   correct results: %.2f%%",model_name,100*model_accuracy);
    //---
       return(true);
      }
    
    //+------------------------------------------------------------------+
    //| Here we test batch execution of the model                        |
    //+------------------------------------------------------------------+
    bool TestBatchExecution(const long model,const string model_name,double &model_accuracy)
      {
       model_accuracy=0;
    //--- array for output classes
       int model_output_classes_id[];
       int correct_results=0;
       int total_results=0;
       bool res=false;
    
    //--- run batch with 3 samples
       float input_data_batch3[3][4]=
         {
            {5.1f,3.5f,1.4f,0.2f}, // iris dataset sample id=1, Iris-setosa
            {6.3f,2.5f,4.9f,1.5f}, // iris dataset sample id=73, Iris-versicolor
            {6.3f,2.7f,4.9f,1.8f}  // iris dataset sample id=124, Iris-virginica
         };
       int correct_classes_batch3[3]= {0,1,2};
    //--- run model
       res=TestSamples(model,input_data_batch3,model_output_classes_id);
       if(res)
         {
          //--- check result
          for(int j=0; j<ArraySize(model_output_classes_id); j++)
            {
             //--- check result
             if(model_output_classes_id[j]==correct_classes_batch3[j])
                correct_results++;
             else
               {
                PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch3[j],input_data_batch3[j][0],input_data_batch3[j][1],input_data_batch3[j][2],input_data_batch3[j][3]);
               }
             total_results++;
            }
         }
       else
          return(false);
    
    //--- run batch with 10 samples
       float input_data_batch10[10][4]=
         {
            {5.5f,3.5f,1.3f,0.2f}, // iris dataset sample id=37 (Iris-setosa)
            {4.9f,3.1f,1.5f,0.1f}, // iris dataset sample id=38 (Iris-setosa)
            {4.4f,3.0f,1.3f,0.2f}, // iris dataset sample id=39 (Iris-setosa)
            {5.0f,3.3f,1.4f,0.2f}, // iris dataset sample id=50 (Iris-setosa)
            {7.0f,3.2f,4.7f,1.4f}, // iris dataset sample id=51 (Iris-versicolor)
            {6.4f,3.2f,4.5f,1.5f}, // iris dataset sample id=52 (Iris-versicolor)
            {6.3f,3.3f,6.0f,2.5f}, // iris dataset sample id=101 (Iris-virginica)
            {5.8f,2.7f,5.1f,1.9f}, // iris dataset sample id=102 (Iris-virginica)
            {7.1f,3.0f,5.9f,2.1f}, // iris dataset sample id=103 (Iris-virginica)
            {6.3f,2.9f,5.6f,1.8f}  // iris dataset sample id=104 (Iris-virginica)
         };
    //--- correct classes for all 10 samples in the batch
       int correct_classes_batch10[10]= {0,0,0,0,1,1,2,2,2,2};
    
    //--- run model
       res=TestSamples(model,input_data_batch10,model_output_classes_id);
    //--- check result
       if(res)
         {
          for(int j=0; j<ArraySize(model_output_classes_id); j++)
            {
             if(model_output_classes_id[j]==correct_classes_batch10[j])
                correct_results++;
             else
               {
                double f1=input_data_batch10[j][0];
                double f2=input_data_batch10[j][1];
                double f3=input_data_batch10[j][2];
                double f4=input_data_batch10[j][3];
                PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch10[j],input_data_batch10[j][0],input_data_batch10[j][1],input_data_batch10[j][2],input_data_batch10[j][3]);
               }
             total_results++;
            }
         }
       else
          return(false);
    
    //--- calculate accuracy
       model_accuracy=correct_results/total_results;
    //---
       return(res);
      }
    //+------------------------------------------------------------------+
    //| Script program start function                                    |
    //+------------------------------------------------------------------+
    int OnStart(void)
      {
       string model_name="LogisticRegressionClassifier";
    //---
       long model=OnnxCreateFromBuffer(ExtModel,ONNX_DEFAULT);
       if(model==INVALID_HANDLE)
         {
          PrintFormat("model_name=%s OnnxCreate error %d for",model_name,GetLastError());
         }
       else
         {
          //--- test all dataset
          double model_accuracy=0;
          //-- test sample by sample execution for all Iris dataset
          if(TestAllIrisDataset(model,model_name,model_accuracy))
             PrintFormat("model=%s all samples accuracy=%f",model_name,model_accuracy);
          else
             PrintFormat("error in testing model=%s ",model_name);
          //--- test batch execution for several samples
          if(TestBatchExecution(model,model_name,model_accuracy))
             PrintFormat("model=%s batch test accuracy=%f",model_name,model_accuracy);
          else
             PrintFormat("error in testing model=%s ",model_name);
          //--- release model
          OnnxRelease(model);
         }
       return(0);
      }
    //+------------------------------------------------------------------+
    
    Resultado:
    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
    

    A precisão do modelo ONNX exportado no iris dataset completo é de 97.33%, o que corresponde à precisão do original.


    2.13.3. Representação ONNX do modelo Logistic Regression Classifier

    Fig.27. Representação ONNX do modelo Logistic Regression Classifier no Netron

    Fig.27. Representação ONNX do modelo Logistic Regression Classifier no Netron


    2.14. LogisticRegressionCV Classifier

    LogisticRegressionCV (Regressão Logística com validação cruzada) é um método poderoso e flexível de classificação binária. Este método não apenas permite que você crie modelos de classificação baseados em regressão logística, mas também ajusta automaticamente os parâmetros para alcançar o melhor desempenho.

    Princípios de funcionamento do LogisticRegressionCV:

    1. Regressão Logística: A base do método LogisticRegressionCV é a regressão logística. A regressão logística é um método estatístico usado para modelar a probabilidade de um objeto pertencer a uma de duas classes. Este modelo é aplicado quando a variável dependente é binária (duas classes) ou quando pode ser convertida em binária.
    2. Validação cruzada: A principal vantagem do LogisticRegressionCV reside em sua validação cruzada integrada. Isso significa que, em vez de ajustar manualmente o valor ótimo do parâmetro de regularização C, o método automaticamente itera sobre diferentes valores de C e escolhe aquele que oferece o melhor desempenho na validação cruzada.
    3. Escolha do C ótimo: O LogisticRegressionCV utiliza a estratégia de validação cruzada para avaliar o desempenho do modelo sob diferentes valores de C. C é um parâmetro de regularização que controla o grau de regularização do modelo. Um valor pequeno de C significa uma forte regularização, enquanto um valor grande de C significa uma fraca regularização. A validação cruzada permite selecionar o valor ótimo de C para equilibrar entre o subajuste e o sobreajuste.
    4. Regularização: O LogisticRegressionCV também suporta diferentes tipos de regularização, incluindo L1 (lasso) e L2 (ridge). Esses tipos de regularização ajudam a melhorar a capacidade de generalização do modelo e a prevenir o sobreajuste.

    Vantagens do LogisticRegressionCV:

    • Ajuste automático de parâmetros: Uma das principais vantagens do LogisticRegressionCV é sua capacidade de escolher automaticamente o valor ótimo do parâmetro C usando validação cruzada. Isso elimina a necessidade de ajuste manual do modelo, permitindo que você se concentre nos dados e na tarefa.
    • Resistência ao sobreajuste: A regularização suportada pelo LogisticRegressionCV ajuda a gerenciar a complexidade do modelo e a reduzir o risco de sobreajuste, especialmente quando se tem um volume pequeno de dados.
    • Transparência: A regressão logística é um método com boa interpretabilidade. Você pode analisar a contribuição de cada característica para a previsão, o que é útil para entender a importância das características.
    • Alta performance: A regressão logística pode ser rápida e eficiente, especialmente quando há muitos dados.

    Limitações do LogisticRegressionCV:

    • Dependências lineares: O LogisticRegressionCV é adequado para resolver problemas de classificação lineares e quase lineares. Se a dependência entre as características e a variável alvo for fortemente não linear, o modelo pode não ser suficientemente preciso.
    • Dificuldade em lidar com um grande número de características: Com muitas características, a regressão logística pode exigir grandes volumes de dados ou métodos de redução de dimensionalidade para evitar o sobreajuste.
    • Dependência da representação dos dados: A eficácia da regressão logística pode depender de como os dados são representados e quais características são selecionadas.

    O LogisticRegressionCV é uma ferramenta poderosa para classificação binária com ajuste automático de parâmetros e resistência ao sobreajuste. É particularmente útil quando você precisa rapidamente criar um modelo de classificação com boa interpretabilidade. No entanto, é importante lembrar que ele funciona melhor em casos onde os dados têm dependências lineares ou próximas do linear.


    2.14.1. Código de criação do modelo LogisticRegressionCV Classifier

    Este código demonstra o processo de treinamento do modelo LogisticRegressionCV Classifier no conjunto de dados Iris, exportação para o formato ONNX e realização de classificação usando o modelo ONNX. Também avalia a precisão tanto do modelo original quanto do modelo 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)
    

    Resultado:

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


    2.14.2. Código no MQL5 para trabalhar com o modelo LogisticRegressionCV Classifier

    //+------------------------------------------------------------------+
    //|                          Iris_LogisticRegressionCVClassifier.mq5 |
    //|                                  Copyright 2023, MetaQuotes Ltd. |
    //|                                             https://www.mql5.com |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2023, MetaQuotes Ltd."
    #property link      "https://www.mql5.com"
    #property version   "1.00"
    
    #include "iris.mqh"
    #resource "logistic_regressioncv_iris.onnx" as const uchar ExtModel[];
    
    //+------------------------------------------------------------------+
    //| Test IRIS dataset samples                                        |
    //+------------------------------------------------------------------+
    bool TestSamples(long model,float &input_data[][4], int &model_classes_id[])
      {
    //--- check number of input samples
       ulong batch_size=input_data.Range(0);
       if(batch_size==0)
          return(false);
    //--- prepare output array
       ArrayResize(model_classes_id,(int)batch_size);
    //---
       float output_data[];
    //---
       struct Map
         {
          ulong          key[];
          float          value[];
         } output_data_map[];
    //--- check consistency
       bool res=ArrayResize(output_data,(int)batch_size)==batch_size;
    //---
       if(res)
         {
          //--- set input shape
          ulong input_shape[]= {batch_size,input_data.Range(1)};
          OnnxSetInputShape(model,0,input_shape);
          //--- set output shapeы
          ulong output_shape1[]= {batch_size};
          ulong output_shape2[]= {batch_size};
          OnnxSetOutputShape(model,0,output_shape1);
          OnnxSetOutputShape(model,1,output_shape2);
          //--- run the model
          res=OnnxRun(model,0,input_data,output_data,output_data_map);
          //--- postprocessing
          if(res)
            {
             //--- postprocessing of sequence map data
             //--- find class with maximum probability
             ulong output_keys[];
             float output_values[];
             //---
             for(uint n=0; n<output_data_map.Size(); n++)
               {
                int model_class_id=-1;
                int max_idx=-1;
                float max_value=-1;
                //--- copy to arrays
                ArrayCopy(output_keys,output_data_map[n].key);
                ArrayCopy(output_values,output_data_map[n].value);
                //ArrayPrint(output_keys);
                //ArrayPrint(output_values);
                //--- find the key with maximum probability
                for(int k=0; k<ArraySize(output_values); k++)
                  {
                   if(k==0)
                     {
                      max_idx=0;
                      max_value=output_values[max_idx];
                      model_class_id=(int)output_keys[max_idx];
                     }
                   else
                     {
                      if(output_values[k]>max_value)
                        {
                         max_idx=k;
                         max_value=output_values[max_idx];
                         model_class_id=(int)output_keys[max_idx];
                        }
                     }
                  }
                //--- store the result to the output array
                model_classes_id[n]=model_class_id;
                //Print("model_class_id=",model_class_id);
               }
            }
         }
    //---
       return(res);
      }
    
    //+------------------------------------------------------------------+
    //| Test all samples from IRIS dataset (150)                         |
    //| Here we test all samples with batch=1, sample by sample          |
    //+------------------------------------------------------------------+
    bool TestAllIrisDataset(const long model,const string model_name,double &model_accuracy)
      {
       sIRISsample iris_samples[];
    //--- load dataset from file
       PrepareIrisDataset(iris_samples);
    //--- test
       int total_samples=ArraySize(iris_samples);
       if(total_samples==0)
         {
          Print("iris dataset not prepared");
          return(false);
         }
    //--- show dataset
       for(int k=0; k<total_samples; k++)
         {
          //PrintFormat("%d (%.2f,%.2f,%.2f,%.2f) class %d (%s)",iris_samples[k].sample_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3],iris_samples[k].class_id,iris_samples[k].class_name);
         }
    //--- array for output classes
       int model_output_classes_id[];
    //--- check all Iris dataset samples
       int correct_results=0;
       for(int k=0; k<total_samples; k++)
         {
          //--- input array
          float iris_sample_input_data[1][4];
          //--- prepare input data from kth iris sample dataset
          iris_sample_input_data[0][0]=(float)iris_samples[k].features[0];
          iris_sample_input_data[0][1]=(float)iris_samples[k].features[1];
          iris_sample_input_data[0][2]=(float)iris_samples[k].features[2];
          iris_sample_input_data[0][3]=(float)iris_samples[k].features[3];
          //--- run model
          bool res=TestSamples(model,iris_sample_input_data,model_output_classes_id);
          //--- check result
          if(res)
            {
             if(model_output_classes_id[0]==iris_samples[k].class_id)
               {
                correct_results++;
               }
             else
               {
                PrintFormat("model:%s  sample=%d FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f]",model_name,iris_samples[k].sample_id,model_output_classes_id[0],iris_samples[k].class_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3]);
               }
            }
         }
       model_accuracy=1.0*correct_results/total_samples;
    //---
       PrintFormat("model:%s   correct results: %.2f%%",model_name,100*model_accuracy);
    //---
       return(true);
      }
    
    //+------------------------------------------------------------------+
    //| Here we test batch execution of the model                        |
    //+------------------------------------------------------------------+
    bool TestBatchExecution(const long model,const string model_name,double &model_accuracy)
      {
       model_accuracy=0;
    //--- array for output classes
       int model_output_classes_id[];
       int correct_results=0;
       int total_results=0;
       bool res=false;
    
    //--- run batch with 3 samples
       float input_data_batch3[3][4]=
         {
            {5.1f,3.5f,1.4f,0.2f}, // iris dataset sample id=1, Iris-setosa
            {6.3f,2.5f,4.9f,1.5f}, // iris dataset sample id=73, Iris-versicolor
            {6.3f,2.7f,4.9f,1.8f}  // iris dataset sample id=124, Iris-virginica
         };
       int correct_classes_batch3[3]= {0,1,2};
    //--- run model
       res=TestSamples(model,input_data_batch3,model_output_classes_id);
       if(res)
         {
          //--- check result
          for(int j=0; j<ArraySize(model_output_classes_id); j++)
            {
             //--- check result
             if(model_output_classes_id[j]==correct_classes_batch3[j])
                correct_results++;
             else
               {
                PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch3[j],input_data_batch3[j][0],input_data_batch3[j][1],input_data_batch3[j][2],input_data_batch3[j][3]);
               }
             total_results++;
            }
         }
       else
          return(false);
    
    //--- run batch with 10 samples
       float input_data_batch10[10][4]=
         {
            {5.5f,3.5f,1.3f,0.2f}, // iris dataset sample id=37 (Iris-setosa)
            {4.9f,3.1f,1.5f,0.1f}, // iris dataset sample id=38 (Iris-setosa)
            {4.4f,3.0f,1.3f,0.2f}, // iris dataset sample id=39 (Iris-setosa)
            {5.0f,3.3f,1.4f,0.2f}, // iris dataset sample id=50 (Iris-setosa)
            {7.0f,3.2f,4.7f,1.4f}, // iris dataset sample id=51 (Iris-versicolor)
            {6.4f,3.2f,4.5f,1.5f}, // iris dataset sample id=52 (Iris-versicolor)
            {6.3f,3.3f,6.0f,2.5f}, // iris dataset sample id=101 (Iris-virginica)
            {5.8f,2.7f,5.1f,1.9f}, // iris dataset sample id=102 (Iris-virginica)
            {7.1f,3.0f,5.9f,2.1f}, // iris dataset sample id=103 (Iris-virginica)
            {6.3f,2.9f,5.6f,1.8f}  // iris dataset sample id=104 (Iris-virginica)
         };
    //--- correct classes for all 10 samples in the batch
       int correct_classes_batch10[10]= {0,0,0,0,1,1,2,2,2,2};
    
    //--- run model
       res=TestSamples(model,input_data_batch10,model_output_classes_id);
    //--- check result
       if(res)
         {
          for(int j=0; j<ArraySize(model_output_classes_id); j++)
            {
             if(model_output_classes_id[j]==correct_classes_batch10[j])
                correct_results++;
             else
               {
                double f1=input_data_batch10[j][0];
                double f2=input_data_batch10[j][1];
                double f3=input_data_batch10[j][2];
                double f4=input_data_batch10[j][3];
                PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch10[j],input_data_batch10[j][0],input_data_batch10[j][1],input_data_batch10[j][2],input_data_batch10[j][3]);
               }
             total_results++;
            }
         }
       else
          return(false);
    
    //--- calculate accuracy
       model_accuracy=correct_results/total_results;
    //---
       return(res);
      }
    //+------------------------------------------------------------------+
    //| Script program start function                                    |
    //+------------------------------------------------------------------+
    int OnStart(void)
      {
       string model_name="LogisticRegressionCVClassifier";
    //---
       long model=OnnxCreateFromBuffer(ExtModel,ONNX_DEFAULT);
       if(model==INVALID_HANDLE)
         {
          PrintFormat("model_name=%s OnnxCreate error %d for",model_name,GetLastError());
         }
       else
         {
          //--- test all dataset
          double model_accuracy=0;
          //-- test sample by sample execution for all Iris dataset
          if(TestAllIrisDataset(model,model_name,model_accuracy))
             PrintFormat("model=%s all samples accuracy=%f",model_name,model_accuracy);
          else
             PrintFormat("error in testing model=%s ",model_name);
          //--- test batch execution for several samples
          if(TestBatchExecution(model,model_name,model_accuracy))
             PrintFormat("model=%s batch test accuracy=%f",model_name,model_accuracy);
          else
             PrintFormat("error in testing model=%s ",model_name);
          //--- release model
          OnnxRelease(model);
         }
       return(0);
      }
    //+------------------------------------------------------------------+
    

    Resultado:

    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
    

    A precisão do modelo ONNX exportado no iris dataset completo é de 98%, o que corresponde à precisão do original.


    2.14.3. Representação ONNX do modelo LogisticRegressionCV Classifier

    Fig.28. Representação ONNX do modelo LogisticRegressionCV Classifier no Netron

    Fig.28. Representação ONNX do modelo LogisticRegressionCV Classifier no Netron



    2.15. Passive-Aggressive (PA) Classifier

    O classificador passivo-agressivo (PA) é um método de aprendizado de máquina utilizado para tarefas de classificação. A ideia principal deste método é adaptar os pesos (coeficientes) do modelo durante o treinamento para minimizar o erro de classificação. O classificador passivo-agressivo pode ser útil em tarefas de aprendizado online e em situações onde os dados mudam ao longo do tempo.

    Princípios de funcionamento do classificador passivo-agressivo:
    1. Adaptação dos pesos: Em vez de atualizar os pesos do modelo na direção da minimização da função de perda, como é feito no método de descida de gradiente estocástico, o classificador passivo-agressivo adapta os pesos na direção que minimiza o erro de classificação para o exemplo atual.
    2. Manutenção da agressividade: O método tem um parâmetro chamado agressividade (C), que determina quão fortemente os pesos do modelo precisam ser adaptados. Valores grandes de C tornam o método mais agressivo na adaptação, enquanto valores pequenos são menos agressivos.
    Vantagens do classificador passivo-agressivo:
    • Adequado para aprendizado online: O classificador passivo-agressivo pode ser atualizado à medida que novos dados são recebidos, tornando-o adequado para tarefas de aprendizado online, onde os dados chegam em fluxo.
    • Adaptabilidade a mudanças nos dados: O método lida bem com dados mutáveis, adaptando o modelo a novas circunstâncias.
    Limitações do classificador passivo-agressivo:
    • Sensibilidade à escolha do parâmetro de agressividade: A escolha do valor ótimo do parâmetro C pode exigir ajustes e depende das características dos dados.
    • Nem sempre é adequado para tarefas complexas: O classificador passivo-agressivo pode não fornecer alta precisão em tarefas complexas, onde é necessário considerar dependências complexas entre as características.
    • Interpretação dos pesos: Os pesos do modelo obtidos usando este método podem ser menos interpretáveis do que os pesos obtidos usando regressão linear ou regressão logística.

    O classificador passivo-agressivo é um método de aprendizado de máquina adequado para tarefas de classificação com dados mutáveis e para situações onde é importante adaptar rapidamente o modelo a novas circunstâncias. Ele encontra aplicação em várias áreas, incluindo análise de dados textuais, classificação de imagens e outras tarefas.


    2.15.1. Código de criação do modelo Passive-Aggressive (PA) Classifier

    Este código demonstra o processo de treinamento do modelo classificador passivo-agressivo (PA) no conjunto de dados Iris, exportação para o formato ONNX e realização de classificação usando o modelo ONNX. Também avalia a precisão tanto do modelo original quanto do modelo 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)
    

    Resultado:

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


    2.15.2. Código no MQL5 para trabalhar com o modelo Passive-Aggressive (PA) Classifier

    //+------------------------------------------------------------------+
    //|                              Iris_PassiveAgressiveClassifier.mq5 |
    //|                                  Copyright 2023, MetaQuotes Ltd. |
    //|                                             https://www.mql5.com |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2023, MetaQuotes Ltd."
    #property link      "https://www.mql5.com"
    #property version   "1.00"
    
    #include "iris.mqh"
    #resource "pa_classifier_iris.onnx" as const uchar ExtModel[];
    
    //+------------------------------------------------------------------+
    //| Test IRIS dataset samples                                        |
    //+------------------------------------------------------------------+
    bool TestSamples(long model,float &input_data[][4], int &model_classes_id[])
      {
    //--- check number of input samples
       ulong batch_size=input_data.Range(0);
       if(batch_size==0)
          return(false);
    //--- prepare output array
       ArrayResize(model_classes_id,(int)batch_size);
    //---
       float output_data[];
    //---
       struct Map
         {
          ulong          key[];
          float          value[];
         } output_data_map[];
    //--- check consistency
       bool res=ArrayResize(output_data,(int)batch_size)==batch_size;
    //---
       if(res)
         {
          //--- set input shape
          ulong input_shape[]= {batch_size,input_data.Range(1)};
          OnnxSetInputShape(model,0,input_shape);
          //--- set output shapeы
          ulong output_shape1[]= {batch_size};
          ulong output_shape2[]= {batch_size};
          OnnxSetOutputShape(model,0,output_shape1);
          OnnxSetOutputShape(model,1,output_shape2);
          //--- run the model
          res=OnnxRun(model,0,input_data,output_data,output_data_map);
          //--- postprocessing
          if(res)
            {
             //--- postprocessing of sequence map data
             //--- find class with maximum probability
             ulong output_keys[];
             float output_values[];
             //---
             for(uint n=0; n<output_data_map.Size(); n++)
               {
                int model_class_id=-1;
                int max_idx=-1;
                float max_value=-1;
                //--- copy to arrays
                ArrayCopy(output_keys,output_data_map[n].key);
                ArrayCopy(output_values,output_data_map[n].value);
                //ArrayPrint(output_keys);
                //ArrayPrint(output_values);
                //--- find the key with maximum probability
                for(int k=0; k<ArraySize(output_values); k++)
                  {
                   if(k==0)
                     {
                      max_idx=0;
                      max_value=output_values[max_idx];
                      model_class_id=(int)output_keys[max_idx];
                     }
                   else
                     {
                      if(output_values[k]>max_value)
                        {
                         max_idx=k;
                         max_value=output_values[max_idx];
                         model_class_id=(int)output_keys[max_idx];
                        }
                     }
                  }
                //--- store the result to the output array
                model_classes_id[n]=model_class_id;
                //Print("model_class_id=",model_class_id);
               }
            }
         }
    //---
       return(res);
      }
    
    //+------------------------------------------------------------------+
    //| Test all samples from IRIS dataset (150)                         |
    //| Here we test all samples with batch=1, sample by sample          |
    //+------------------------------------------------------------------+
    bool TestAllIrisDataset(const long model,const string model_name,double &model_accuracy)
      {
       sIRISsample iris_samples[];
    //--- load dataset from file
       PrepareIrisDataset(iris_samples);
    //--- test
       int total_samples=ArraySize(iris_samples);
       if(total_samples==0)
         {
          Print("iris dataset not prepared");
          return(false);
         }
    //--- show dataset
       for(int k=0; k<total_samples; k++)
         {
          //PrintFormat("%d (%.2f,%.2f,%.2f,%.2f) class %d (%s)",iris_samples[k].sample_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3],iris_samples[k].class_id,iris_samples[k].class_name);
         }
    //--- array for output classes
       int model_output_classes_id[];
    //--- check all Iris dataset samples
       int correct_results=0;
       for(int k=0; k<total_samples; k++)
         {
          //--- input array
          float iris_sample_input_data[1][4];
          //--- prepare input data from kth iris sample dataset
          iris_sample_input_data[0][0]=(float)iris_samples[k].features[0];
          iris_sample_input_data[0][1]=(float)iris_samples[k].features[1];
          iris_sample_input_data[0][2]=(float)iris_samples[k].features[2];
          iris_sample_input_data[0][3]=(float)iris_samples[k].features[3];
          //--- run model
          bool res=TestSamples(model,iris_sample_input_data,model_output_classes_id);
          //--- check result
          if(res)
            {
             if(model_output_classes_id[0]==iris_samples[k].class_id)
               {
                correct_results++;
               }
             else
               {
                PrintFormat("model:%s  sample=%d FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f]",model_name,iris_samples[k].sample_id,model_output_classes_id[0],iris_samples[k].class_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3]);
               }
            }
         }
       model_accuracy=1.0*correct_results/total_samples;
    //---
       PrintFormat("model:%s   correct results: %.2f%%",model_name,100*model_accuracy);
    //---
       return(true);
      }
    
    //+------------------------------------------------------------------+
    //| Here we test batch execution of the model                        |
    //+------------------------------------------------------------------+
    bool TestBatchExecution(const long model,const string model_name,double &model_accuracy)
      {
       model_accuracy=0;
    //--- array for output classes
       int model_output_classes_id[];
       int correct_results=0;
       int total_results=0;
       bool res=false;
    
    //--- run batch with 3 samples
       float input_data_batch3[3][4]=
         {
            {5.1f,3.5f,1.4f,0.2f}, // iris dataset sample id=1, Iris-setosa
            {6.3f,2.5f,4.9f,1.5f}, // iris dataset sample id=73, Iris-versicolor
            {6.3f,2.7f,4.9f,1.8f}  // iris dataset sample id=124, Iris-virginica
         };
       int correct_classes_batch3[3]= {0,1,2};
    //--- run model
       res=TestSamples(model,input_data_batch3,model_output_classes_id);
       if(res)
         {
          //--- check result
          for(int j=0; j<ArraySize(model_output_classes_id); j++)
            {
             //--- check result
             if(model_output_classes_id[j]==correct_classes_batch3[j])
                correct_results++;
             else
               {
                PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch3[j],input_data_batch3[j][0],input_data_batch3[j][1],input_data_batch3[j][2],input_data_batch3[j][3]);
               }
             total_results++;
            }
         }
       else
          return(false);
    
    //--- run batch with 10 samples
       float input_data_batch10[10][4]=
         {
            {5.5f,3.5f,1.3f,0.2f}, // iris dataset sample id=37 (Iris-setosa)
            {4.9f,3.1f,1.5f,0.1f}, // iris dataset sample id=38 (Iris-setosa)
            {4.4f,3.0f,1.3f,0.2f}, // iris dataset sample id=39 (Iris-setosa)
            {5.0f,3.3f,1.4f,0.2f}, // iris dataset sample id=50 (Iris-setosa)
            {7.0f,3.2f,4.7f,1.4f}, // iris dataset sample id=51 (Iris-versicolor)
            {6.4f,3.2f,4.5f,1.5f}, // iris dataset sample id=52 (Iris-versicolor)
            {6.3f,3.3f,6.0f,2.5f}, // iris dataset sample id=101 (Iris-virginica)
            {5.8f,2.7f,5.1f,1.9f}, // iris dataset sample id=102 (Iris-virginica)
            {7.1f,3.0f,5.9f,2.1f}, // iris dataset sample id=103 (Iris-virginica)
            {6.3f,2.9f,5.6f,1.8f}  // iris dataset sample id=104 (Iris-virginica)
         };
    //--- correct classes for all 10 samples in the batch
       int correct_classes_batch10[10]= {0,0,0,0,1,1,2,2,2,2};
    
    //--- run model
       res=TestSamples(model,input_data_batch10,model_output_classes_id);
    //--- check result
       if(res)
         {
          for(int j=0; j<ArraySize(model_output_classes_id); j++)
            {
             if(model_output_classes_id[j]==correct_classes_batch10[j])
                correct_results++;
             else
               {
                double f1=input_data_batch10[j][0];
                double f2=input_data_batch10[j][1];
                double f3=input_data_batch10[j][2];
                double f4=input_data_batch10[j][3];
                PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch10[j],input_data_batch10[j][0],input_data_batch10[j][1],input_data_batch10[j][2],input_data_batch10[j][3]);
               }
             total_results++;
            }
         }
       else
          return(false);
    
    //--- calculate accuracy
       model_accuracy=correct_results/total_results;
    //---
       return(res);
      }
    //+------------------------------------------------------------------+
    //| Script program start function                                    |
    //+------------------------------------------------------------------+
    int OnStart(void)
      {
       string model_name="PassiveAgressiveClassifier";
    //---
       long model=OnnxCreateFromBuffer(ExtModel,ONNX_DEFAULT);
       if(model==INVALID_HANDLE)
         {
          PrintFormat("model_name=%s OnnxCreate error %d for",model_name,GetLastError());
         }
       else
         {
          //--- test all dataset
          double model_accuracy=0;
          //-- test sample by sample execution for all Iris dataset
          if(TestAllIrisDataset(model,model_name,model_accuracy))
             PrintFormat("model=%s all samples accuracy=%f",model_name,model_accuracy);
          else
             PrintFormat("error in testing model=%s ",model_name);
          //--- test batch execution for several samples
          if(TestBatchExecution(model,model_name,model_accuracy))
             PrintFormat("model=%s batch test accuracy=%f",model_name,model_accuracy);
          else
             PrintFormat("error in testing model=%s ",model_name);
          //--- release model
          OnnxRelease(model);
         }
       return(0);
      }
    //+------------------------------------------------------------------+
    

    Resultado:

    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
    

    A precisão do modelo ONNX exportado no iris dataset completo é de 96%, o que corresponde à precisão do original.


    2.15.3. Representação ONNX do modelo Passive-Aggressive (PA) Classifier

    Fig.29. Representação ONNX do modelo classificador passivo-agressivo (PA) no Netron

    Fig.29. Representação ONNX do modelo classificador passivo-agressivo (PA) no Netron

    2.16. Perceptron Classifier

    O classificador perceptron é um classificador binário linear usado para separar duas classes com base em uma hiperplano separador linear. Ele é um dos métodos de aprendizado de máquina mais simples e antigos, e seu princípio básico é treinar os pesos (coeficientes) do modelo de forma a maximizar a precisão da classificação no conjunto de dados de treinamento.

    Princípios de funcionamento do classificador perceptron:
    1. Hiperplano linear: O perceptron constrói um hiperplano linear no espaço multidimensional de características que separa as duas classes. Esse hiperplano é definido pelos pesos (coeficientes) do modelo.
    2. Treinamento dos pesos: Inicialmente, os pesos são inicializados aleatoriamente ou com zeros. Então, para cada objeto no conjunto de treinamento, o modelo prevê a classe com base nos pesos atuais e os ajusta em caso de erro. O treinamento continua até que todos os objetos sejam classificados corretamente ou até que um número máximo de iterações seja alcançado.
    Vantagens do classificador perceptron:
    • Simplicidade: O perceptron é um algoritmo muito simples, fácil de entender e implementar.
    • Alta velocidade de treinamento: O perceptron pode ser treinado rapidamente, especialmente em grandes volumes de dados, e pode ser usado em tarefas de aprendizado online.
    Limitações do classificador perceptron:
    • Limitação a dados linearmente separáveis: O perceptron funciona bem apenas quando os dados são linearmente separáveis. Se os dados não puderem ser separados linearmente, o perceptron não será capaz de alcançar alta precisão.
    • Sensibilidade à escolha dos pesos iniciais: A escolha inicial dos pesos pode afetar a convergência do algoritmo. Uma má escolha dos pesos iniciais pode levar a uma convergência lenta ou até a um perceptron que não consegue separar corretamente as classes.
    • Incapacidade de determinar probabilidades: O perceptron não fornece estimativas de probabilidade de pertencimento às classes, o que pode ser importante para algumas tarefas.

    O classificador perceptron é um algoritmo básico para classificação binária, útil em tarefas simples quando os dados são linearmente separáveis. Também pode servir como fundação para métodos mais complexos, como redes neurais multicamadas. É importante lembrar que, em tarefas mais complexas, onde os dados têm uma estrutura complexa, outros métodos, como regressão logística ou métodos de vetor de suporte (SVM), podem oferecer maior precisão de classificação.


    2.16.1. Código de criação do modelo perceptron Classifier

    Este código demonstra o processo de treinamento do modelo perceptron Classifier no conjunto de dados Iris, exportação para o formato ONNX e realização de classificação usando o modelo ONNX. Também avalia a precisão tanto do modelo original quanto do modelo ONNX.# Iris_PerceptronClassifier.py

    # Iris_PerceptronClassifier.py
    # The code demonstrates the process of training Perceptron Classifier model on the Iris dataset, exporting it to ONNX format, and making predictions using the ONNX model. 
    # It also evaluates the accuracy of both the original model and the ONNX model.
    # Copyright 2023, MetaQuotes Ltd.
    # https://www.mql5.com
    
    # import necessary libraries
    from sklearn import datasets
    from sklearn.linear_model import Perceptron
    from sklearn.metrics import accuracy_score, classification_report
    from skl2onnx import convert_sklearn
    from skl2onnx.common.data_types import FloatTensorType
    import onnxruntime as ort
    import numpy as np
    from sys import argv
    
    # define the path for saving the model
    data_path = argv[0]
    last_index = data_path.rfind("\\") + 1
    data_path = data_path[0:last_index]
    
    # load the Iris dataset
    iris = datasets.load_iris()
    X = iris.data
    y = iris.target
    
    # create a Perceptron Classifier model
    perceptron_model = Perceptron(max_iter=1000, random_state=42)
    
    # train the model on the entire dataset
    perceptron_model.fit(X, y)
    
    # predict classes for the entire dataset
    y_pred = perceptron_model.predict(X)
    
    # evaluate the model's accuracy
    accuracy = accuracy_score(y, y_pred)
    print("Accuracy of Perceptron Classifier model:", accuracy)
    
    # display the classification report
    print("\nClassification Report:\n", classification_report(y, y_pred))
    
    # define the input data type
    initial_type = [('float_input', FloatTensorType([None, X.shape[1]]))]
    
    # export the model to ONNX format with float data type
    onnx_model = convert_sklearn(perceptron_model, initial_types=initial_type, target_opset=12)
    
    # save the model to a file
    onnx_filename = data_path + "perceptron_classifier_iris.onnx"
    with open(onnx_filename, "wb") as f:
        f.write(onnx_model.SerializeToString())
    
    # print model path
    print(f"Model saved to {onnx_filename}")
    
    # load the ONNX model and make predictions
    onnx_session = ort.InferenceSession(onnx_filename)
    input_name = onnx_session.get_inputs()[0].name
    output_name = onnx_session.get_outputs()[0].name
    
    # display information about input tensors in ONNX
    print("\nInformation about input tensors in ONNX:")
    for i, input_tensor in enumerate(onnx_session.get_inputs()):
        print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")
    
    # display information about output tensors in ONNX
    print("\nInformation about output tensors in ONNX:")
    for i, output_tensor in enumerate(onnx_session.get_outputs()):
        print(f"{i + 1}. Name: {output_tensor.name}, Data Type: {output_tensor.type}, Shape: {output_tensor.shape}")
    
    # convert data to floating-point format (float32)
    X_float32 = X.astype(np.float32)
    
    # predict classes for the entire dataset using ONNX
    y_pred_onnx = onnx_session.run([output_name], {input_name: X_float32})[0]
    
    # evaluate the accuracy of the ONNX model
    accuracy_onnx = accuracy_score(y, y_pred_onnx)
    print("\nAccuracy of Perceptron Classifier model in ONNX format:", accuracy_onnx)
    

    Resultado:

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


    2.16.2. Código no MQL5 para trabalhar com o modelo Perceptron Classifier

    //+------------------------------------------------------------------+
    //|                                    Iris_PerceptronClassifier.mq5 |
    //|                                  Copyright 2023, MetaQuotes Ltd. |
    //|                                             https://www.mql5.com |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2023, MetaQuotes Ltd."
    #property link      "https://www.mql5.com"
    #property version   "1.00"
    #include "iris.mqh"
    #resource "perceptron_classifier_iris.onnx" as const uchar ExtModel[];
    
    //+------------------------------------------------------------------+
    //| Test IRIS dataset samples                                        |
    //+------------------------------------------------------------------+
    bool TestSamples(long model,float &input_data[][4], int &model_classes_id[])
      {
    //--- check number of input samples
       ulong batch_size=input_data.Range(0);
       if(batch_size==0)
          return(false);
    //--- prepare output array
       ArrayResize(model_classes_id,(int)batch_size);
    //---
       float output_data[];
    //---
       struct Map
         {
          ulong          key[];
          float          value[];
         } output_data_map[];
    //--- check consistency
       bool res=ArrayResize(output_data,(int)batch_size)==batch_size;
    //---
       if(res)
         {
          //--- set input shape
          ulong input_shape[]= {batch_size,input_data.Range(1)};
          OnnxSetInputShape(model,0,input_shape);
          //--- set output shapeы
          ulong output_shape1[]= {batch_size};
          ulong output_shape2[]= {batch_size};
          OnnxSetOutputShape(model,0,output_shape1);
          OnnxSetOutputShape(model,1,output_shape2);
          //--- run the model
          res=OnnxRun(model,0,input_data,output_data,output_data_map);
          //--- postprocessing
          if(res)
            {
             //--- postprocessing of sequence map data
             //--- find class with maximum probability
             ulong output_keys[];
             float output_values[];
             //---
             for(uint n=0; n<output_data_map.Size(); n++)
               {
                int model_class_id=-1;
                int max_idx=-1;
                float max_value=-1;
                //--- copy to arrays
                ArrayCopy(output_keys,output_data_map[n].key);
                ArrayCopy(output_values,output_data_map[n].value);
                //ArrayPrint(output_keys);
                //ArrayPrint(output_values);
                //--- find the key with maximum probability
                for(int k=0; k<ArraySize(output_values); k++)
                  {
                   if(k==0)
                     {
                      max_idx=0;
                      max_value=output_values[max_idx];
                      model_class_id=(int)output_keys[max_idx];
                     }
                   else
                     {
                      if(output_values[k]>max_value)
                        {
                         max_idx=k;
                         max_value=output_values[max_idx];
                         model_class_id=(int)output_keys[max_idx];
                        }
                     }
                  }
                //--- store the result to the output array
                model_classes_id[n]=model_class_id;
                //Print("model_class_id=",model_class_id);
               }
            }
         }
    //---
       return(res);
      }
    
    //+------------------------------------------------------------------+
    //| Test all samples from IRIS dataset (150)                         |
    //| Here we test all samples with batch=1, sample by sample          |
    //+------------------------------------------------------------------+
    bool TestAllIrisDataset(const long model,const string model_name,double &model_accuracy)
      {
       sIRISsample iris_samples[];
    //--- load dataset from file
       PrepareIrisDataset(iris_samples);
    //--- test
       int total_samples=ArraySize(iris_samples);
       if(total_samples==0)
         {
          Print("iris dataset not prepared");
          return(false);
         }
    //--- show dataset
       for(int k=0; k<total_samples; k++)
         {
          //PrintFormat("%d (%.2f,%.2f,%.2f,%.2f) class %d (%s)",iris_samples[k].sample_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3],iris_samples[k].class_id,iris_samples[k].class_name);
         }
    //--- array for output classes
       int model_output_classes_id[];
    //--- check all Iris dataset samples
       int correct_results=0;
       for(int k=0; k<total_samples; k++)
         {
          //--- input array
          float iris_sample_input_data[1][4];
          //--- prepare input data from kth iris sample dataset
          iris_sample_input_data[0][0]=(float)iris_samples[k].features[0];
          iris_sample_input_data[0][1]=(float)iris_samples[k].features[1];
          iris_sample_input_data[0][2]=(float)iris_samples[k].features[2];
          iris_sample_input_data[0][3]=(float)iris_samples[k].features[3];
          //--- run model
          bool res=TestSamples(model,iris_sample_input_data,model_output_classes_id);
          //--- check result
          if(res)
            {
             if(model_output_classes_id[0]==iris_samples[k].class_id)
               {
                correct_results++;
               }
             else
               {
                PrintFormat("model:%s  sample=%d FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f]",model_name,iris_samples[k].sample_id,model_output_classes_id[0],iris_samples[k].class_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3]);
               }
            }
         }
       model_accuracy=1.0*correct_results/total_samples;
    //---
       PrintFormat("model:%s   correct results: %.2f%%",model_name,100*model_accuracy);
    //---
       return(true);
      }
    
    //+------------------------------------------------------------------+
    //| Here we test batch execution of the model                        |
    //+------------------------------------------------------------------+
    bool TestBatchExecution(const long model,const string model_name,double &model_accuracy)
      {
       model_accuracy=0;
    //--- array for output classes
       int model_output_classes_id[];
       int correct_results=0;
       int total_results=0;
       bool res=false;
    
    //--- run batch with 3 samples
       float input_data_batch3[3][4]=
         {
            {5.1f,3.5f,1.4f,0.2f}, // iris dataset sample id=1, Iris-setosa
            {6.3f,2.5f,4.9f,1.5f}, // iris dataset sample id=73, Iris-versicolor
            {6.3f,2.7f,4.9f,1.8f}  // iris dataset sample id=124, Iris-virginica
         };
       int correct_classes_batch3[3]= {0,1,2};
    //--- run model
       res=TestSamples(model,input_data_batch3,model_output_classes_id);
       if(res)
         {
          //--- check result
          for(int j=0; j<ArraySize(model_output_classes_id); j++)
            {
             //--- check result
             if(model_output_classes_id[j]==correct_classes_batch3[j])
                correct_results++;
             else
               {
                PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch3[j],input_data_batch3[j][0],input_data_batch3[j][1],input_data_batch3[j][2],input_data_batch3[j][3]);
               }
             total_results++;
            }
         }
       else
          return(false);
    
    //--- run batch with 10 samples
       float input_data_batch10[10][4]=
         {
            {5.5f,3.5f,1.3f,0.2f}, // iris dataset sample id=37 (Iris-setosa)
            {4.9f,3.1f,1.5f,0.1f}, // iris dataset sample id=38 (Iris-setosa)
            {4.4f,3.0f,1.3f,0.2f}, // iris dataset sample id=39 (Iris-setosa)
            {5.0f,3.3f,1.4f,0.2f}, // iris dataset sample id=50 (Iris-setosa)
            {7.0f,3.2f,4.7f,1.4f}, // iris dataset sample id=51 (Iris-versicolor)
            {6.4f,3.2f,4.5f,1.5f}, // iris dataset sample id=52 (Iris-versicolor)
            {6.3f,3.3f,6.0f,2.5f}, // iris dataset sample id=101 (Iris-virginica)
            {5.8f,2.7f,5.1f,1.9f}, // iris dataset sample id=102 (Iris-virginica)
            {7.1f,3.0f,5.9f,2.1f}, // iris dataset sample id=103 (Iris-virginica)
            {6.3f,2.9f,5.6f,1.8f}  // iris dataset sample id=104 (Iris-virginica)
         };
    //--- correct classes for all 10 samples in the batch
       int correct_classes_batch10[10]= {0,0,0,0,1,1,2,2,2,2};
    
    //--- run model
       res=TestSamples(model,input_data_batch10,model_output_classes_id);
    //--- check result
       if(res)
         {
          for(int j=0; j<ArraySize(model_output_classes_id); j++)
            {
             if(model_output_classes_id[j]==correct_classes_batch10[j])
                correct_results++;
             else
               {
                double f1=input_data_batch10[j][0];
                double f2=input_data_batch10[j][1];
                double f3=input_data_batch10[j][2];
                double f4=input_data_batch10[j][3];
                PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch10[j],input_data_batch10[j][0],input_data_batch10[j][1],input_data_batch10[j][2],input_data_batch10[j][3]);
               }
             total_results++;
            }
         }
       else
          return(false);
    
    //--- calculate accuracy
       model_accuracy=correct_results/total_results;
    //---
       return(res);
      }
    //+------------------------------------------------------------------+
    //| Script program start function                                    |
    //+------------------------------------------------------------------+
    int OnStart(void)
      {
       string model_name="PerceptronClassifier";
    //---
       long model=OnnxCreateFromBuffer(ExtModel,ONNX_DEFAULT);
       if(model==INVALID_HANDLE)
         {
          PrintFormat("model_name=%s OnnxCreate error %d for",model_name,GetLastError());
         }
       else
         {
          //--- test all dataset
          double model_accuracy=0;
          //-- test sample by sample execution for all Iris dataset
          if(TestAllIrisDataset(model,model_name,model_accuracy))
             PrintFormat("model=%s all samples accuracy=%f",model_name,model_accuracy);
          else
             PrintFormat("error in testing model=%s ",model_name);
          //--- test batch execution for several samples
          if(TestBatchExecution(model,model_name,model_accuracy))
             PrintFormat("model=%s batch test accuracy=%f",model_name,model_accuracy);
          else
             PrintFormat("error in testing model=%s ",model_name);
          //--- release model
          OnnxRelease(model);
         }
       return(0);
      }
    //+------------------------------------------------------------------+
    

    Resultado:

    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
    

    A precisão do modelo ONNX exportado no iris dataset completo é de 61,33%, o que corresponde à precisão do original.

    2.16.3. Representação ONNX do modelo Perceptron Classifier

    Fig.30. Representação do modelo perceptron Classifier em Netron

    Fig.30. Representação do modelo perceptron Classifier em Netron


    2.17. Stochastic Gradient Descent Classifier

    SGD Classifier (Stochastic Gradient Descent Classifier) é um método de aprendizado de máquina usado para tarefas de classificação. Ele é um caso especial de modelos lineares e representa um classificador linear que é treinado usando descida de gradiente estocástica.

    Princípios do funcionamento do SGD Classifier:
    1. Hiperplano linear: SGD Classifier constrói um hiperplano linear no espaço multidimensional de características que separa duas classes. O hiperplano é definido pelos pesos (coeficientes) do modelo.
    2. Descida de gradiente estocástica: O método é treinado usando descida de gradiente estocástica, o que significa que a atualização dos pesos do modelo é realizada em cada objeto do conjunto de treinamento (ou em um subconjunto aleatoriamente selecionado), ao invés de no conjunto de dados completo. Isso torna o SGD Classifier adequado para grandes volumes de dados e aprendizado online.
    3. Função de perda: SGD Classifier otimiza uma função de perda, como a função de perda logística para tarefas de classificação binária ou a função de perda softmax para classificação multiclasse.
    Vantagens do SGD Classifier:
    • Velocidade de treinamento: SGD Classifier é rápido para treinar, especialmente em grandes volumes de dados, graças à descida de gradiente estocástica.
    • Adequado para aprendizado online: O método é bem adequado para tarefas de aprendizado online, onde os dados chegam em fluxo e o modelo precisa ser atualizado à medida que chegam.
    Limitações do SGD Classifier:
    • Sensibilidade aos parâmetros: SGD Classifier tem muitos hiperparâmetros, como a taxa de aprendizado e o parâmetro de regularização, que requerem ajuste cuidadoso.
    • Inicialização dos pesos: A aproximação inicial dos pesos pode afetar a convergência e a qualidade do modelo.
    • Convergência para mínimos locais: Devido à natureza estocástica do método, SGD Classifier pode convergir para mínimos locais da função de perda, o que pode afetar a qualidade do modelo.

    SGD Classifier é um método flexível de aprendizado de máquina que pode ser usado para tarefas de classificação binária e multiclasse, especialmente em casos onde os dados são volumosos e requerem processamento rápido. É importante configurar adequadamente seus hiperparâmetros e monitorar a convergência para alcançar alta precisão de classificação.


    2.17.1. Código de criação do modelo Stochastic Gradient Descent Classifier

    Este código demonstra o processo de treinamento do modelo Stochastic Gradient Descent Classifier no conjunto de dados Iris, exportação para o formato ONNX e realização de classificação usando o modelo ONNX. Também avalia a precisão tanto do modelo original quanto do modelo 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)
    

    Resultado:

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


    2.17.2. Código no MQL5 para trabalhar com o modelo Stochastic Gradient Descent Classifier

    //+------------------------------------------------------------------+
    //|                                           Iris_SGDClassifier.mq5 |
    //|                                  Copyright 2023, MetaQuotes Ltd. |
    //|                                             https://www.mql5.com |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2023, MetaQuotes Ltd."
    #property link      "https://www.mql5.com"
    #property version   "1.00"
    
    #include "iris.mqh"
    #resource "sgd_classifier_iris.onnx" as const uchar ExtModel[];
    
    //+------------------------------------------------------------------+
    //| Test IRIS dataset samples                                        |
    //+------------------------------------------------------------------+
    bool TestSamples(long model,float &input_data[][4], int &model_classes_id[])
      {
    //--- check number of input samples
       ulong batch_size=input_data.Range(0);
       if(batch_size==0)
          return(false);
    //--- prepare output array
       ArrayResize(model_classes_id,(int)batch_size);
    //---
       float output_data[];
    //---
       struct Map
         {
          ulong          key[];
          float          value[];
         } output_data_map[];
    //--- check consistency
       bool res=ArrayResize(output_data,(int)batch_size)==batch_size;
    //---
       if(res)
         {
          //--- set input shape
          ulong input_shape[]= {batch_size,input_data.Range(1)};
          OnnxSetInputShape(model,0,input_shape);
          //--- set output shapeы
          ulong output_shape1[]= {batch_size};
          ulong output_shape2[]= {batch_size};
          OnnxSetOutputShape(model,0,output_shape1);
          OnnxSetOutputShape(model,1,output_shape2);
          //--- run the model
          res=OnnxRun(model,0,input_data,output_data,output_data_map);
          //--- postprocessing
          if(res)
            {
             //--- postprocessing of sequence map data
             //--- find class with maximum probability
             ulong output_keys[];
             float output_values[];
             //---
             for(uint n=0; n<output_data_map.Size(); n++)
               {
                int model_class_id=-1;
                int max_idx=-1;
                float max_value=-1;
                //--- copy to arrays
                ArrayCopy(output_keys,output_data_map[n].key);
                ArrayCopy(output_values,output_data_map[n].value);
                //ArrayPrint(output_keys);
                //ArrayPrint(output_values);
                //--- find the key with maximum probability
                for(int k=0; k<ArraySize(output_values); k++)
                  {
                   if(k==0)
                     {
                      max_idx=0;
                      max_value=output_values[max_idx];
                      model_class_id=(int)output_keys[max_idx];
                     }
                   else
                     {
                      if(output_values[k]>max_value)
                        {
                         max_idx=k;
                         max_value=output_values[max_idx];
                         model_class_id=(int)output_keys[max_idx];
                        }
                     }
                  }
                //--- store the result to the output array
                model_classes_id[n]=model_class_id;
                //Print("model_class_id=",model_class_id);
               }
            }
         }
    //---
       return(res);
      }
    
    //+------------------------------------------------------------------+
    //| Test all samples from IRIS dataset (150)                         |
    //| Here we test all samples with batch=1, sample by sample          |
    //+------------------------------------------------------------------+
    bool TestAllIrisDataset(const long model,const string model_name,double &model_accuracy)
      {
       sIRISsample iris_samples[];
    //--- load dataset from file
       PrepareIrisDataset(iris_samples);
    //--- test
       int total_samples=ArraySize(iris_samples);
       if(total_samples==0)
         {
          Print("iris dataset not prepared");
          return(false);
         }
    //--- show dataset
       for(int k=0; k<total_samples; k++)
         {
          //PrintFormat("%d (%.2f,%.2f,%.2f,%.2f) class %d (%s)",iris_samples[k].sample_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3],iris_samples[k].class_id,iris_samples[k].class_name);
         }
    //--- array for output classes
       int model_output_classes_id[];
    //--- check all Iris dataset samples
       int correct_results=0;
       for(int k=0; k<total_samples; k++)
         {
          //--- input array
          float iris_sample_input_data[1][4];
          //--- prepare input data from kth iris sample dataset
          iris_sample_input_data[0][0]=(float)iris_samples[k].features[0];
          iris_sample_input_data[0][1]=(float)iris_samples[k].features[1];
          iris_sample_input_data[0][2]=(float)iris_samples[k].features[2];
          iris_sample_input_data[0][3]=(float)iris_samples[k].features[3];
          //--- run model
          bool res=TestSamples(model,iris_sample_input_data,model_output_classes_id);
          //--- check result
          if(res)
            {
             if(model_output_classes_id[0]==iris_samples[k].class_id)
               {
                correct_results++;
               }
             else
               {
                PrintFormat("model:%s  sample=%d FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f]",model_name,iris_samples[k].sample_id,model_output_classes_id[0],iris_samples[k].class_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3]);
               }
            }
         }
       model_accuracy=1.0*correct_results/total_samples;
    //---
       PrintFormat("model:%s   correct results: %.2f%%",model_name,100*model_accuracy);
    //---
       return(true);
      }
    
    //+------------------------------------------------------------------+
    //| Here we test batch execution of the model                        |
    //+------------------------------------------------------------------+
    bool TestBatchExecution(const long model,const string model_name,double &model_accuracy)
      {
       model_accuracy=0;
    //--- array for output classes
       int model_output_classes_id[];
       int correct_results=0;
       int total_results=0;
       bool res=false;
    
    //--- run batch with 3 samples
       float input_data_batch3[3][4]=
         {
            {5.1f,3.5f,1.4f,0.2f}, // iris dataset sample id=1, Iris-setosa
            {6.3f,2.5f,4.9f,1.5f}, // iris dataset sample id=73, Iris-versicolor
            {6.3f,2.7f,4.9f,1.8f}  // iris dataset sample id=124, Iris-virginica
         };
       int correct_classes_batch3[3]= {0,1,2};
    //--- run model
       res=TestSamples(model,input_data_batch3,model_output_classes_id);
       if(res)
         {
          //--- check result
          for(int j=0; j<ArraySize(model_output_classes_id); j++)
            {
             //--- check result
             if(model_output_classes_id[j]==correct_classes_batch3[j])
                correct_results++;
             else
               {
                PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch3[j],input_data_batch3[j][0],input_data_batch3[j][1],input_data_batch3[j][2],input_data_batch3[j][3]);
               }
             total_results++;
            }
         }
       else
          return(false);
    
    //--- run batch with 10 samples
       float input_data_batch10[10][4]=
         {
            {5.5f,3.5f,1.3f,0.2f}, // iris dataset sample id=37 (Iris-setosa)
            {4.9f,3.1f,1.5f,0.1f}, // iris dataset sample id=38 (Iris-setosa)
            {4.4f,3.0f,1.3f,0.2f}, // iris dataset sample id=39 (Iris-setosa)
            {5.0f,3.3f,1.4f,0.2f}, // iris dataset sample id=50 (Iris-setosa)
            {7.0f,3.2f,4.7f,1.4f}, // iris dataset sample id=51 (Iris-versicolor)
            {6.4f,3.2f,4.5f,1.5f}, // iris dataset sample id=52 (Iris-versicolor)
            {6.3f,3.3f,6.0f,2.5f}, // iris dataset sample id=101 (Iris-virginica)
            {5.8f,2.7f,5.1f,1.9f}, // iris dataset sample id=102 (Iris-virginica)
            {7.1f,3.0f,5.9f,2.1f}, // iris dataset sample id=103 (Iris-virginica)
            {6.3f,2.9f,5.6f,1.8f}  // iris dataset sample id=104 (Iris-virginica)
         };
    //--- correct classes for all 10 samples in the batch
       int correct_classes_batch10[10]= {0,0,0,0,1,1,2,2,2,2};
    
    //--- run model
       res=TestSamples(model,input_data_batch10,model_output_classes_id);
    //--- check result
       if(res)
         {
          for(int j=0; j<ArraySize(model_output_classes_id); j++)
            {
             if(model_output_classes_id[j]==correct_classes_batch10[j])
                correct_results++;
             else
               {
                double f1=input_data_batch10[j][0];
                double f2=input_data_batch10[j][1];
                double f3=input_data_batch10[j][2];
                double f4=input_data_batch10[j][3];
                PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch10[j],input_data_batch10[j][0],input_data_batch10[j][1],input_data_batch10[j][2],input_data_batch10[j][3]);
               }
             total_results++;
            }
         }
       else
          return(false);
    
    //--- calculate accuracy
       model_accuracy=correct_results/total_results;
    //---
       return(res);
      }
    //+------------------------------------------------------------------+
    //| Script program start function                                    |
    //+------------------------------------------------------------------+
    int OnStart(void)
      {
       string model_name="SGDClassifier";
    //---
       long model=OnnxCreateFromBuffer(ExtModel,ONNX_DEFAULT);
       if(model==INVALID_HANDLE)
         {
          PrintFormat("model_name=%s OnnxCreate error %d for",model_name,GetLastError());
         }
       else
         {
          //--- test all dataset
          double model_accuracy=0;
          //-- test sample by sample execution for all Iris dataset
          if(TestAllIrisDataset(model,model_name,model_accuracy))
             PrintFormat("model=%s all samples accuracy=%f",model_name,model_accuracy);
          else
             PrintFormat("error in testing model=%s ",model_name);
          //--- test batch execution for several samples
          if(TestBatchExecution(model,model_name,model_accuracy))
             PrintFormat("model=%s batch test accuracy=%f",model_name,model_accuracy);
          else
             PrintFormat("error in testing model=%s ",model_name);
          //--- release model
          OnnxRelease(model);
         }
       return(0);
      }
    //+------------------------------------------------------------------+
    

    Resultado:

    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
    

    A precisão do modelo ONNX exportado no iris dataset completo é de 93,33%, o que corresponde à precisão do original.


    2.17.3. Representação ONNX do modelo Stochastic Gradient Descent Classifier

    Fig.31. Representação do modelo Stochastic Gradient Descent Classifier em Netron

    Fig.31. Representação do modelo Stochastic Gradient Descent Classifier em Netron


    2.18. Gaussian Naive Bayes (GNB) Classifier

    Gaussian Naive Bayes (GNB) Classifier é um método de aprendizado de máquina baseado no modelo probabilístico bayesiano e usado para tarefas de classificação. Ele faz parte da família de classificadores bayesianos ingênuos e assume que todas as características são independentes e seguem uma distribuição normal.

    Princípios de funcionamento do Gaussian Naive Bayes Classifier:
    1. Abordagem Bayesiana: GNB é baseado na abordagem bayesiana para classificação, que utiliza o teorema de Bayes para calcular a probabilidade de um objeto pertencer a cada classe.
    2. Suposição Ingênua: A principal suposição feita pelo GNB é que todas as características são independentes e seguem uma distribuição normal (gaussiana). Esta é uma suposição ingênua porque, na realidade, as características frequentemente se correlacionam entre si.
    3. Treinamento dos parâmetros: O modelo GNB é treinado no conjunto de dados de treinamento, calculando os parâmetros de distribuição (média e desvio padrão) para cada característica em cada classe.
    Vantagens do Gaussian Naive Bayes Classifier:
    • Simplicidade e rapidez no treinamento: GNB é um algoritmo muito simples e treina rapidamente, mesmo em grandes volumes de dados.
    • Eficiência para tamanhos de dados pequenos e médios: GNB pode ser eficaz para tarefas de classificação com um número pequeno ou médio de características, especialmente quando a suposição de distribuição normal das características é bem atendida.
    Limitações do Gaussian Naive Bayes Classifier:
    • Suposição Ingênua: A suposição de independência das características e distribuição normal pode ser excessivamente simplificada e incorreta para dados reais. Isso pode levar a uma diminuição na precisão da classificação.
    • Sensibilidade a valores atípicos: GNB pode ser sensível a valores atípicos nos dados, pois eles podem distorcer significativamente os parâmetros da distribuição normal.
    • Incapacidade de considerar dependências entre características: Devido à suposição de independência, o GNB não leva em conta as dependências entre as características.

    O Gaussian Naive Bayes Classifier é uma boa escolha para tarefas simples de classificação, especialmente quando a suposição de distribuição normal das características é mais ou menos verdadeira. No entanto, em tarefas mais complexas, onde as características estão correlacionados entre si ou a distribuição não é normal, outros métodos, como Support Vector Machines (SVM) ou gradient boosting, podem fornecer resultados mais precisos.


    2.18.1. Código de criação do modelo Gaussian Naive Bayes (GNB) Classifier

    Este código demonstra o processo de treinamento do modelo Gaussian Naive Bayes (GNB) Classifier no conjunto de dados Iris, exportação para o formato ONNX e realização de classificação usando o modelo ONNX. Também avalia a precisão tanto do modelo original quanto do modelo 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)
    

    Resultado:

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


    2.18.2. Código no MQL5 para trabalhar com o modelo Gaussian Naive Bayes (GNB) Classifier

    //+------------------------------------------------------------------+
    //|                            Iris_GaussianNaiveBayesClassifier.mq5 |
    //|                                  Copyright 2023, MetaQuotes Ltd. |
    //|                                             https://www.mql5.com |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2023, MetaQuotes Ltd."
    #property link      "https://www.mql5.com"
    #property version   "1.00"
    
    #include "iris.mqh"
    #resource "gnb_classifier_iris.onnx" as const uchar ExtModel[];
    
    //+------------------------------------------------------------------+
    //| Test IRIS dataset samples                                        |
    //+------------------------------------------------------------------+
    bool TestSamples(long model,float &input_data[][4], int &model_classes_id[])
      {
    //--- check number of input samples
       ulong batch_size=input_data.Range(0);
       if(batch_size==0)
          return(false);
    //--- prepare output array
       ArrayResize(model_classes_id,(int)batch_size);
    //---
       float output_data[];
    //---
       struct Map
         {
          ulong          key[];
          float          value[];
         } output_data_map[];
    //--- check consistency
       bool res=ArrayResize(output_data,(int)batch_size)==batch_size;
    //---
       if(res)
         {
          //--- set input shape
          ulong input_shape[]= {batch_size,input_data.Range(1)};
          OnnxSetInputShape(model,0,input_shape);
          //--- set output shapeы
          ulong output_shape1[]= {batch_size};
          ulong output_shape2[]= {batch_size};
          OnnxSetOutputShape(model,0,output_shape1);
          OnnxSetOutputShape(model,1,output_shape2);
          //--- run the model
          res=OnnxRun(model,0,input_data,output_data,output_data_map);
          //--- postprocessing
          if(res)
            {
             //--- postprocessing of sequence map data
             //--- find class with maximum probability
             ulong output_keys[];
             float output_values[];
             //---
             for(uint n=0; n<output_data_map.Size(); n++)
               {
                int model_class_id=-1;
                int max_idx=-1;
                float max_value=-1;
                //--- copy to arrays
                ArrayCopy(output_keys,output_data_map[n].key);
                ArrayCopy(output_values,output_data_map[n].value);
                //ArrayPrint(output_keys);
                //ArrayPrint(output_values);
                //--- find the key with maximum probability
                for(int k=0; k<ArraySize(output_values); k++)
                  {
                   if(k==0)
                     {
                      max_idx=0;
                      max_value=output_values[max_idx];
                      model_class_id=(int)output_keys[max_idx];
                     }
                   else
                     {
                      if(output_values[k]>max_value)
                        {
                         max_idx=k;
                         max_value=output_values[max_idx];
                         model_class_id=(int)output_keys[max_idx];
                        }
                     }
                  }
                //--- store the result to the output array
                model_classes_id[n]=model_class_id;
                //Print("model_class_id=",model_class_id);
               }
            }
         }
    //---
       return(res);
      }
    
    //+------------------------------------------------------------------+
    //| Test all samples from IRIS dataset (150)                         |
    //| Here we test all samples with batch=1, sample by sample          |
    //+------------------------------------------------------------------+
    bool TestAllIrisDataset(const long model,const string model_name,double &model_accuracy)
      {
       sIRISsample iris_samples[];
    //--- load dataset from file
       PrepareIrisDataset(iris_samples);
    //--- test
       int total_samples=ArraySize(iris_samples);
       if(total_samples==0)
         {
          Print("iris dataset not prepared");
          return(false);
         }
    //--- show dataset
       for(int k=0; k<total_samples; k++)
         {
          //PrintFormat("%d (%.2f,%.2f,%.2f,%.2f) class %d (%s)",iris_samples[k].sample_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3],iris_samples[k].class_id,iris_samples[k].class_name);
         }
    //--- array for output classes
       int model_output_classes_id[];
    //--- check all Iris dataset samples
       int correct_results=0;
       for(int k=0; k<total_samples; k++)
         {
          //--- input array
          float iris_sample_input_data[1][4];
          //--- prepare input data from kth iris sample dataset
          iris_sample_input_data[0][0]=(float)iris_samples[k].features[0];
          iris_sample_input_data[0][1]=(float)iris_samples[k].features[1];
          iris_sample_input_data[0][2]=(float)iris_samples[k].features[2];
          iris_sample_input_data[0][3]=(float)iris_samples[k].features[3];
          //--- run model
          bool res=TestSamples(model,iris_sample_input_data,model_output_classes_id);
          //--- check result
          if(res)
            {
             if(model_output_classes_id[0]==iris_samples[k].class_id)
               {
                correct_results++;
               }
             else
               {
                PrintFormat("model:%s  sample=%d FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f]",model_name,iris_samples[k].sample_id,model_output_classes_id[0],iris_samples[k].class_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3]);
               }
            }
         }
       model_accuracy=1.0*correct_results/total_samples;
    //---
       PrintFormat("model:%s   correct results: %.2f%%",model_name,100*model_accuracy);
    //---
       return(true);
      }
    
    //+------------------------------------------------------------------+
    //| Here we test batch execution of the model                        |
    //+------------------------------------------------------------------+
    bool TestBatchExecution(const long model,const string model_name,double &model_accuracy)
      {
       model_accuracy=0;
    //--- array for output classes
       int model_output_classes_id[];
       int correct_results=0;
       int total_results=0;
       bool res=false;
    
    //--- run batch with 3 samples
       float input_data_batch3[3][4]=
         {
            {5.1f,3.5f,1.4f,0.2f}, // iris dataset sample id=1, Iris-setosa
            {6.3f,2.5f,4.9f,1.5f}, // iris dataset sample id=73, Iris-versicolor
            {6.3f,2.7f,4.9f,1.8f}  // iris dataset sample id=124, Iris-virginica
         };
       int correct_classes_batch3[3]= {0,1,2};
    //--- run model
       res=TestSamples(model,input_data_batch3,model_output_classes_id);
       if(res)
         {
          //--- check result
          for(int j=0; j<ArraySize(model_output_classes_id); j++)
            {
             //--- check result
             if(model_output_classes_id[j]==correct_classes_batch3[j])
                correct_results++;
             else
               {
                PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch3[j],input_data_batch3[j][0],input_data_batch3[j][1],input_data_batch3[j][2],input_data_batch3[j][3]);
               }
             total_results++;
            }
         }
       else
          return(false);
    
    //--- run batch with 10 samples
       float input_data_batch10[10][4]=
         {
            {5.5f,3.5f,1.3f,0.2f}, // iris dataset sample id=37 (Iris-setosa)
            {4.9f,3.1f,1.5f,0.1f}, // iris dataset sample id=38 (Iris-setosa)
            {4.4f,3.0f,1.3f,0.2f}, // iris dataset sample id=39 (Iris-setosa)
            {5.0f,3.3f,1.4f,0.2f}, // iris dataset sample id=50 (Iris-setosa)
            {7.0f,3.2f,4.7f,1.4f}, // iris dataset sample id=51 (Iris-versicolor)
            {6.4f,3.2f,4.5f,1.5f}, // iris dataset sample id=52 (Iris-versicolor)
            {6.3f,3.3f,6.0f,2.5f}, // iris dataset sample id=101 (Iris-virginica)
            {5.8f,2.7f,5.1f,1.9f}, // iris dataset sample id=102 (Iris-virginica)
            {7.1f,3.0f,5.9f,2.1f}, // iris dataset sample id=103 (Iris-virginica)
            {6.3f,2.9f,5.6f,1.8f}  // iris dataset sample id=104 (Iris-virginica)
         };
    //--- correct classes for all 10 samples in the batch
       int correct_classes_batch10[10]= {0,0,0,0,1,1,2,2,2,2};
    
    //--- run model
       res=TestSamples(model,input_data_batch10,model_output_classes_id);
    //--- check result
       if(res)
         {
          for(int j=0; j<ArraySize(model_output_classes_id); j++)
            {
             if(model_output_classes_id[j]==correct_classes_batch10[j])
                correct_results++;
             else
               {
                double f1=input_data_batch10[j][0];
                double f2=input_data_batch10[j][1];
                double f3=input_data_batch10[j][2];
                double f4=input_data_batch10[j][3];
                PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch10[j],input_data_batch10[j][0],input_data_batch10[j][1],input_data_batch10[j][2],input_data_batch10[j][3]);
               }
             total_results++;
            }
         }
       else
          return(false);
    
    //--- calculate accuracy
       model_accuracy=correct_results/total_results;
    //---
       return(res);
      }
    //+------------------------------------------------------------------+
    //| Script program start function                                    |
    //+------------------------------------------------------------------+
    int OnStart(void)
      {
       string model_name="GaussianNaiveBayesClassifier";
    //---
       long model=OnnxCreateFromBuffer(ExtModel,ONNX_DEFAULT);
       if(model==INVALID_HANDLE)
         {
          PrintFormat("model_name=%s OnnxCreate error %d for",model_name,GetLastError());
         }
       else
         {
          //--- test all dataset
          double model_accuracy=0;
          //-- test sample by sample execution for all Iris dataset
          if(TestAllIrisDataset(model,model_name,model_accuracy))
             PrintFormat("model=%s all samples accuracy=%f",model_name,model_accuracy);
          else
             PrintFormat("error in testing model=%s ",model_name);
          //--- test batch execution for several samples
          if(TestBatchExecution(model,model_name,model_accuracy))
             PrintFormat("model=%s batch test accuracy=%f",model_name,model_accuracy);
          else
             PrintFormat("error in testing model=%s ",model_name);
          //--- release model
          OnnxRelease(model);
         }
       return(0);
      }
    //+------------------------------------------------------------------+
    

    Resultado:

    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
    

    A precisão do modelo ONNX exportado no iris dataset completo é de 96%, o que corresponde à precisão do original.


    2.18.3. Representação ONNX do modelo Gaussian Naive Bayes (GNB) Classifier

    Fig.32. Representação do modelo Gaussian Naive Bayes (GNB) Classifier em Netron

    Fig.32. Representação do modelo Gaussian Naive Bayes (GNB) Classifier em Netron


    2.19. Multinomial Naive Bayes (MNB) Classifier

    Multinomial Naive Bayes (MNB) Classifier é um método de aprendizado de máquina baseado em modelo probabilístico bayesiano, usado para tarefas de classificação, especialmente em processamento de texto. Ele é uma das variantes dos classificadores bayesianos ingênuos e assume que as características representam contagens, como o número de palavras em um texto.

    Princípios do funcionamento do Multinomial Naive Bayes Classifier:
    1. Abordagem Bayesiana: MNB também é baseado na abordagem bayesiana para classificação e usa o teorema de Bayes para calcular a probabilidade de um objeto pertencer a cada classe.
    2. Suposição de distribuição multinomial: A principal suposição do MNB é que as características representam contagens, como a frequência de palavras em um texto, e têm uma distribuição multinomial. Essa suposição é frequentemente válida para dados textuais.
    3. Treinamento dos parâmetros: O modelo MNB é treinado no conjunto de dados de treinamento, calculando os parâmetros de distribuição para cada recurso em cada classe.
    Vantagens do Multinomial Naive Bayes Classifier:
    • Eficiência em processamento de texto: MNB funciona bem em tarefas de análise de dados textuais, como classificação de texto ou filtragem de spam, graças à suposição sobre contadores de características.
    • Simplicidade e rapidez no treinamento: Como outros classificadores bayesianos ingênuos, MNB é um algoritmo simples que treina rapidamente, mesmo em grandes volumes de dados textuais.
    Limitações do Multinomial Naive Bayes Classifier:
    • Suposição Ingênua: A suposição de distribuição multinomial das características pode ser demasiadamente simplificada e imprecisa para dados reais, especialmente em casos onde as características têm uma estrutura complexa.
    • Incapacidade de considerar a ordem das palavras: MNB não leva em conta a ordem das palavras no texto, o que pode ser importante em algumas tarefas de análise de texto.
    • Sensibilidade a palavras raras: O MNB pode ser sensível a palavras raras, e a sua escassez pode diminuir a precisão da classificação.

    O Multinomial Naive Bayes Classifier é um método útil para tarefas de análise de texto, especialmente quando as características estão relacionadas a contagens, como o número de palavras em um texto. Ele é amplamente utilizado no processamento de linguagem natural (NLP) para tarefas de classificação de texto, categorização de documentos e outras análises textuais.


    2.19.1. Código de criação do modelo Multinomial Naive Bayes (MNB) Classifier

    Este código demonstra o processo de treinamento do modelo Multinomial Naive Bayes (MNB) Classifier no conjunto de dados Iris, exportação para o formato ONNX e realização de classificação usando o modelo ONNX. Também avalia a precisão tanto do modelo original quanto do modelo 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)
    

    Resultado:

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


    2.19.2. Código no MQL5 para trabalhar com o modelo Multinomial Naive Bayes (MNB) Classifier

    //+------------------------------------------------------------------+
    //|                         Iris_MultinomialNaiveBayesClassifier.mq5 |
    //|                                  Copyright 2023, MetaQuotes Ltd. |
    //|                                             https://www.mql5.com |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2023, MetaQuotes Ltd."
    #property link      "https://www.mql5.com"
    #property version   "1.00"
    
    #include "iris.mqh"
    #resource "mnb_classifier_iris.onnx" as const uchar ExtModel[];
    
    //+------------------------------------------------------------------+
    //| Test IRIS dataset samples                                        |
    //+------------------------------------------------------------------+
    bool TestSamples(long model,float &input_data[][4], int &model_classes_id[])
      {
    //--- check number of input samples
       ulong batch_size=input_data.Range(0);
       if(batch_size==0)
          return(false);
    //--- prepare output array
       ArrayResize(model_classes_id,(int)batch_size);
    //---
       float output_data[];
    //---
       struct Map
         {
          ulong          key[];
          float          value[];
         } output_data_map[];
    //--- check consistency
       bool res=ArrayResize(output_data,(int)batch_size)==batch_size;
    //---
       if(res)
         {
          //--- set input shape
          ulong input_shape[]= {batch_size,input_data.Range(1)};
          OnnxSetInputShape(model,0,input_shape);
          //--- set output shapeы
          ulong output_shape1[]= {batch_size};
          ulong output_shape2[]= {batch_size};
          OnnxSetOutputShape(model,0,output_shape1);
          OnnxSetOutputShape(model,1,output_shape2);
          //--- run the model
          res=OnnxRun(model,0,input_data,output_data,output_data_map);
          //--- postprocessing
          if(res)
            {
             //--- postprocessing of sequence map data
             //--- find class with maximum probability
             ulong output_keys[];
             float output_values[];
             //---
             for(uint n=0; n<output_data_map.Size(); n++)
               {
                int model_class_id=-1;
                int max_idx=-1;
                float max_value=-1;
                //--- copy to arrays
                ArrayCopy(output_keys,output_data_map[n].key);
                ArrayCopy(output_values,output_data_map[n].value);
                //ArrayPrint(output_keys);
                //ArrayPrint(output_values);
                //--- find the key with maximum probability
                for(int k=0; k<ArraySize(output_values); k++)
                  {
                   if(k==0)
                     {
                      max_idx=0;
                      max_value=output_values[max_idx];
                      model_class_id=(int)output_keys[max_idx];
                     }
                   else
                     {
                      if(output_values[k]>max_value)
                        {
                         max_idx=k;
                         max_value=output_values[max_idx];
                         model_class_id=(int)output_keys[max_idx];
                        }
                     }
                  }
                //--- store the result to the output array
                model_classes_id[n]=model_class_id;
                //Print("model_class_id=",model_class_id);
               }
            }
         }
    //---
       return(res);
      }
    
    //+------------------------------------------------------------------+
    //| Test all samples from IRIS dataset (150)                         |
    //| Here we test all samples with batch=1, sample by sample          |
    //+------------------------------------------------------------------+
    bool TestAllIrisDataset(const long model,const string model_name,double &model_accuracy)
      {
       sIRISsample iris_samples[];
    //--- load dataset from file
       PrepareIrisDataset(iris_samples);
    //--- test
       int total_samples=ArraySize(iris_samples);
       if(total_samples==0)
         {
          Print("iris dataset not prepared");
          return(false);
         }
    //--- show dataset
       for(int k=0; k<total_samples; k++)
         {
          //PrintFormat("%d (%.2f,%.2f,%.2f,%.2f) class %d (%s)",iris_samples[k].sample_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3],iris_samples[k].class_id,iris_samples[k].class_name);
         }
    //--- array for output classes
       int model_output_classes_id[];
    //--- check all Iris dataset samples
       int correct_results=0;
       for(int k=0; k<total_samples; k++)
         {
          //--- input array
          float iris_sample_input_data[1][4];
          //--- prepare input data from kth iris sample dataset
          iris_sample_input_data[0][0]=(float)iris_samples[k].features[0];
          iris_sample_input_data[0][1]=(float)iris_samples[k].features[1];
          iris_sample_input_data[0][2]=(float)iris_samples[k].features[2];
          iris_sample_input_data[0][3]=(float)iris_samples[k].features[3];
          //--- run model
          bool res=TestSamples(model,iris_sample_input_data,model_output_classes_id);
          //--- check result
          if(res)
            {
             if(model_output_classes_id[0]==iris_samples[k].class_id)
               {
                correct_results++;
               }
             else
               {
                PrintFormat("model:%s  sample=%d FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f]",model_name,iris_samples[k].sample_id,model_output_classes_id[0],iris_samples[k].class_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3]);
               }
            }
         }
       model_accuracy=1.0*correct_results/total_samples;
    //---
       PrintFormat("model:%s   correct results: %.2f%%",model_name,100*model_accuracy);
    //---
       return(true);
      }
    
    //+------------------------------------------------------------------+
    //| Here we test batch execution of the model                        |
    //+------------------------------------------------------------------+
    bool TestBatchExecution(const long model,const string model_name,double &model_accuracy)
      {
       model_accuracy=0;
    //--- array for output classes
       int model_output_classes_id[];
       int correct_results=0;
       int total_results=0;
       bool res=false;
    
    //--- run batch with 3 samples
       float input_data_batch3[3][4]=
         {
            {5.1f,3.5f,1.4f,0.2f}, // iris dataset sample id=1, Iris-setosa
            {6.3f,2.5f,4.9f,1.5f}, // iris dataset sample id=73, Iris-versicolor
            {6.3f,2.7f,4.9f,1.8f}  // iris dataset sample id=124, Iris-virginica
         };
       int correct_classes_batch3[3]= {0,1,2};
    //--- run model
       res=TestSamples(model,input_data_batch3,model_output_classes_id);
       if(res)
         {
          //--- check result
          for(int j=0; j<ArraySize(model_output_classes_id); j++)
            {
             //--- check result
             if(model_output_classes_id[j]==correct_classes_batch3[j])
                correct_results++;
             else
               {
                PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch3[j],input_data_batch3[j][0],input_data_batch3[j][1],input_data_batch3[j][2],input_data_batch3[j][3]);
               }
             total_results++;
            }
         }
       else
          return(false);
    
    //--- run batch with 10 samples
       float input_data_batch10[10][4]=
         {
            {5.5f,3.5f,1.3f,0.2f}, // iris dataset sample id=37 (Iris-setosa)
            {4.9f,3.1f,1.5f,0.1f}, // iris dataset sample id=38 (Iris-setosa)
            {4.4f,3.0f,1.3f,0.2f}, // iris dataset sample id=39 (Iris-setosa)
            {5.0f,3.3f,1.4f,0.2f}, // iris dataset sample id=50 (Iris-setosa)
            {7.0f,3.2f,4.7f,1.4f}, // iris dataset sample id=51 (Iris-versicolor)
            {6.4f,3.2f,4.5f,1.5f}, // iris dataset sample id=52 (Iris-versicolor)
            {6.3f,3.3f,6.0f,2.5f}, // iris dataset sample id=101 (Iris-virginica)
            {5.8f,2.7f,5.1f,1.9f}, // iris dataset sample id=102 (Iris-virginica)
            {7.1f,3.0f,5.9f,2.1f}, // iris dataset sample id=103 (Iris-virginica)
            {6.3f,2.9f,5.6f,1.8f}  // iris dataset sample id=104 (Iris-virginica)
         };
    //--- correct classes for all 10 samples in the batch
       int correct_classes_batch10[10]= {0,0,0,0,1,1,2,2,2,2};
    
    //--- run model
       res=TestSamples(model,input_data_batch10,model_output_classes_id);
    //--- check result
       if(res)
         {
          for(int j=0; j<ArraySize(model_output_classes_id); j++)
            {
             if(model_output_classes_id[j]==correct_classes_batch10[j])
                correct_results++;
             else
               {
                double f1=input_data_batch10[j][0];
                double f2=input_data_batch10[j][1];
                double f3=input_data_batch10[j][2];
                double f4=input_data_batch10[j][3];
                PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch10[j],input_data_batch10[j][0],input_data_batch10[j][1],input_data_batch10[j][2],input_data_batch10[j][3]);
               }
             total_results++;
            }
         }
       else
          return(false);
    
    //--- calculate accuracy
       model_accuracy=correct_results/total_results;
    //---
       return(res);
      }
    //+------------------------------------------------------------------+
    //| Script program start function                                    |
    //+------------------------------------------------------------------+
    int OnStart(void)
      {
       string model_name="MultinomialNaiveBayesClassifier";
    //---
       long model=OnnxCreateFromBuffer(ExtModel,ONNX_DEFAULT);
       if(model==INVALID_HANDLE)
         {
          PrintFormat("model_name=%s OnnxCreate error %d for",model_name,GetLastError());
         }
       else
         {
          //--- test all dataset
          double model_accuracy=0;
          //-- test sample by sample execution for all Iris dataset
          if(TestAllIrisDataset(model,model_name,model_accuracy))
             PrintFormat("model=%s all samples accuracy=%f",model_name,model_accuracy);
          else
             PrintFormat("error in testing model=%s ",model_name);
          //--- test batch execution for several samples
          if(TestBatchExecution(model,model_name,model_accuracy))
             PrintFormat("model=%s batch test accuracy=%f",model_name,model_accuracy);
          else
             PrintFormat("error in testing model=%s ",model_name);
          //--- release model
          OnnxRelease(model);
         }
       return(0);
      }
    //+------------------------------------------------------------------+
    

    Resultado:

    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
    

    A precisão do modelo ONNX exportado no iris dataset completo é de 95,33%, o que corresponde à precisão do original.


    2.19.3. Representação ONNX do modelo Multinomial Naive Bayes (MNB) Classifier

    Fig.33. Representação do modelo Multinomial Naive Bayes (MNB) Classifier em Netron

    Fig.33. Representação do modelo Multinomial Naive Bayes (MNB) Classifier em Netron


    2.20. Complement Naive Bayes (CNB) Classifier

    Complement Naive Bayes (CNB) Classifier é uma variante do classificador bayesiano ingênuo, especificamente projetada para lidar com dados desbalanceados, onde uma classe pode ser significativamente mais comum do que outra. Este classificador é uma adaptação do método bayesiano ingênuo clássico, que tenta levar em conta o desbalanceamento de classes.

    Princípios do funcionamento do Complement Naive Bayes Classifier:
    1. Abordagem Bayesiana: Assim como outros classificadores bayesianos, o CNB é baseado na abordagem bayesiana para classificação e usa o teorema de Bayes para calcular a probabilidade de um objeto pertencer a cada classe.
    2. Correção do desbalanceamento de classes: O principal propósito do CNB é a correção do desbalanceamento de classes. Em vez de considerar a probabilidade das características na classe, como faz o método bayesiano ingênuo padrão, o CNB tenta levar em conta a probabilidade das características fora da classe. Isso é particularmente útil quando uma classe é significativamente menos representada do que a outra.
    3. Treinamento dos parâmetros: O modelo CNB é treinado no conjunto de dados de treinamento, calculando os parâmetros de distribuição para cada recurso fora da classe.
    Vantagens do Complement Naive Bayes Classifier:
    • Equidade para dados desbalanceados: O CNB lida bem com tarefas de classificação em dados desbalanceados, onde as classes têm diferentes frequências de ocorrência.
    • Simplicidade e rapidez no treinamento: Como outros classificadores bayesianos ingênuos, o CNB é um algoritmo simples que treina rapidamente, mesmo em grandes volumes de dados.
    Limitações do Complement Naive Bayes Classifier:
    • Sensibilidade à escolha do parâmetro de regularização: Como em outros métodos bayesianos, escolher o valor correto para o parâmetro de regularização pode requerer ajuste e avaliação.
    • Suposição Ingênua: Assim como outros classificadores bayesianos ingênuos, o CNB faz a suposição de independência entre as características, o que pode ser demasiado simplificado para algumas tarefas.

    O Complement Naive Bayes Classifier é uma boa escolha para tarefas de classificação em dados desbalanceados, especialmente quando uma classe é significativamente menos representada do que outra. Pode ser particularmente útil em tarefas de classificação de texto, onde as palavras podem ser fortemente desbalanceadas entre as classes, como análise de sentimento ou filtragem de spam.


    2.20.1. Código de criação do modelo Complement Naive Bayes (CNB) Classifier

    Este código demonstra o processo de treinamento do modelo Complement Naive Bayes (CNB) Classifier no conjunto de dados Iris, exportação para o formato ONNX e realização de classificação usando o modelo ONNX. Também avalia a precisão tanto do modelo original quanto do modelo 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)
    

    Resultado:

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


    2.20.2. Código no MQL5 para trabalhar com o modelo Complement Naive Bayes (CNB) Classifier

    //+------------------------------------------------------------------+
    //|                                           Iris_CNBClassifier.mq5 |
    //|                                  Copyright 2023, MetaQuotes Ltd. |
    //|                                             https://www.mql5.com |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2023, MetaQuotes Ltd."
    #property link      "https://www.mql5.com"
    #property version   "1.00"
    
    #include "iris.mqh"
    #resource "cnb_classifier_iris.onnx" as const uchar ExtModel[];
    
    //+------------------------------------------------------------------+
    //| Test IRIS dataset samples                                        |
    //+------------------------------------------------------------------+
    bool TestSamples(long model,float &input_data[][4], int &model_classes_id[])
      {
    //--- check number of input samples
       ulong batch_size=input_data.Range(0);
       if(batch_size==0)
          return(false);
    //--- prepare output array
       ArrayResize(model_classes_id,(int)batch_size);
    //---
       float output_data[];
    //---
       struct Map
         {
          ulong          key[];
          float          value[];
         } output_data_map[];
    //--- check consistency
       bool res=ArrayResize(output_data,(int)batch_size)==batch_size;
    //---
       if(res)
         {
          //--- set input shape
          ulong input_shape[]= {batch_size,input_data.Range(1)};
          OnnxSetInputShape(model,0,input_shape);
          //--- set output shapeы
          ulong output_shape1[]= {batch_size};
          ulong output_shape2[]= {batch_size};
          OnnxSetOutputShape(model,0,output_shape1);
          OnnxSetOutputShape(model,1,output_shape2);
          //--- run the model
          res=OnnxRun(model,0,input_data,output_data,output_data_map);
          //--- postprocessing
          if(res)
            {
             //--- postprocessing of sequence map data
             //--- find class with maximum probability
             ulong output_keys[];
             float output_values[];
             //---
             for(uint n=0; n<output_data_map.Size(); n++)
               {
                int model_class_id=-1;
                int max_idx=-1;
                float max_value=-1;
                //--- copy to arrays
                ArrayCopy(output_keys,output_data_map[n].key);
                ArrayCopy(output_values,output_data_map[n].value);
                //ArrayPrint(output_keys);
                //ArrayPrint(output_values);
                //--- find the key with maximum probability
                for(int k=0; k<ArraySize(output_values); k++)
                  {
                   if(k==0)
                     {
                      max_idx=0;
                      max_value=output_values[max_idx];
                      model_class_id=(int)output_keys[max_idx];
                     }
                   else
                     {
                      if(output_values[k]>max_value)
                        {
                         max_idx=k;
                         max_value=output_values[max_idx];
                         model_class_id=(int)output_keys[max_idx];
                        }
                     }
                  }
                //--- store the result to the output array
                model_classes_id[n]=model_class_id;
                //Print("model_class_id=",model_class_id);
               }
            }
         }
    //---
       return(res);
      }
    
    //+------------------------------------------------------------------+
    //| Test all samples from IRIS dataset (150)                         |
    //| Here we test all samples with batch=1, sample by sample          |
    //+------------------------------------------------------------------+
    bool TestAllIrisDataset(const long model,const string model_name,double &model_accuracy)
      {
       sIRISsample iris_samples[];
    //--- load dataset from file
       PrepareIrisDataset(iris_samples);
    //--- test
       int total_samples=ArraySize(iris_samples);
       if(total_samples==0)
         {
          Print("iris dataset not prepared");
          return(false);
         }
    //--- show dataset
       for(int k=0; k<total_samples; k++)
         {
          //PrintFormat("%d (%.2f,%.2f,%.2f,%.2f) class %d (%s)",iris_samples[k].sample_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3],iris_samples[k].class_id,iris_samples[k].class_name);
         }
    //--- array for output classes
       int model_output_classes_id[];
    //--- check all Iris dataset samples
       int correct_results=0;
       for(int k=0; k<total_samples; k++)
         {
          //--- input array
          float iris_sample_input_data[1][4];
          //--- prepare input data from kth iris sample dataset
          iris_sample_input_data[0][0]=(float)iris_samples[k].features[0];
          iris_sample_input_data[0][1]=(float)iris_samples[k].features[1];
          iris_sample_input_data[0][2]=(float)iris_samples[k].features[2];
          iris_sample_input_data[0][3]=(float)iris_samples[k].features[3];
          //--- run model
          bool res=TestSamples(model,iris_sample_input_data,model_output_classes_id);
          //--- check result
          if(res)
            {
             if(model_output_classes_id[0]==iris_samples[k].class_id)
               {
                correct_results++;
               }
             else
               {
                PrintFormat("model:%s  sample=%d FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f]",model_name,iris_samples[k].sample_id,model_output_classes_id[0],iris_samples[k].class_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3]);
               }
            }
         }
       model_accuracy=1.0*correct_results/total_samples;
    //---
       PrintFormat("model:%s   correct results: %.2f%%",model_name,100*model_accuracy);
    //---
       return(true);
      }
    
    //+------------------------------------------------------------------+
    //| Here we test batch execution of the model                        |
    //+------------------------------------------------------------------+
    bool TestBatchExecution(const long model,const string model_name,double &model_accuracy)
      {
       model_accuracy=0;
    //--- array for output classes
       int model_output_classes_id[];
       int correct_results=0;
       int total_results=0;
       bool res=false;
    
    //--- run batch with 3 samples
       float input_data_batch3[3][4]=
         {
            {5.1f,3.5f,1.4f,0.2f}, // iris dataset sample id=1, Iris-setosa
            {6.3f,2.5f,4.9f,1.5f}, // iris dataset sample id=73, Iris-versicolor
            {6.3f,2.7f,4.9f,1.8f}  // iris dataset sample id=124, Iris-virginica
         };
       int correct_classes_batch3[3]= {0,1,2};
    //--- run model
       res=TestSamples(model,input_data_batch3,model_output_classes_id);
       if(res)
         {
          //--- check result
          for(int j=0; j<ArraySize(model_output_classes_id); j++)
            {
             //--- check result
             if(model_output_classes_id[j]==correct_classes_batch3[j])
                correct_results++;
             else
               {
                PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch3[j],input_data_batch3[j][0],input_data_batch3[j][1],input_data_batch3[j][2],input_data_batch3[j][3]);
               }
             total_results++;
            }
         }
       else
          return(false);
    
    //--- run batch with 10 samples
       float input_data_batch10[10][4]=
         {
            {5.5f,3.5f,1.3f,0.2f}, // iris dataset sample id=37 (Iris-setosa)
            {4.9f,3.1f,1.5f,0.1f}, // iris dataset sample id=38 (Iris-setosa)
            {4.4f,3.0f,1.3f,0.2f}, // iris dataset sample id=39 (Iris-setosa)
            {5.0f,3.3f,1.4f,0.2f}, // iris dataset sample id=50 (Iris-setosa)
            {7.0f,3.2f,4.7f,1.4f}, // iris dataset sample id=51 (Iris-versicolor)
            {6.4f,3.2f,4.5f,1.5f}, // iris dataset sample id=52 (Iris-versicolor)
            {6.3f,3.3f,6.0f,2.5f}, // iris dataset sample id=101 (Iris-virginica)
            {5.8f,2.7f,5.1f,1.9f}, // iris dataset sample id=102 (Iris-virginica)
            {7.1f,3.0f,5.9f,2.1f}, // iris dataset sample id=103 (Iris-virginica)
            {6.3f,2.9f,5.6f,1.8f}  // iris dataset sample id=104 (Iris-virginica)
         };
    //--- correct classes for all 10 samples in the batch
       int correct_classes_batch10[10]= {0,0,0,0,1,1,2,2,2,2};
    
    //--- run model
       res=TestSamples(model,input_data_batch10,model_output_classes_id);
    //--- check result
       if(res)
         {
          for(int j=0; j<ArraySize(model_output_classes_id); j++)
            {
             if(model_output_classes_id[j]==correct_classes_batch10[j])
                correct_results++;
             else
               {
                double f1=input_data_batch10[j][0];
                double f2=input_data_batch10[j][1];
                double f3=input_data_batch10[j][2];
                double f4=input_data_batch10[j][3];
                PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch10[j],input_data_batch10[j][0],input_data_batch10[j][1],input_data_batch10[j][2],input_data_batch10[j][3]);
               }
             total_results++;
            }
         }
       else
          return(false);
    
    //--- calculate accuracy
       model_accuracy=correct_results/total_results;
    //---
       return(res);
      }
    //+------------------------------------------------------------------+
    //| Script program start function                                    |
    //+------------------------------------------------------------------+
    int OnStart(void)
      {
       string model_name="CNBClassifier";
    //---
       long model=OnnxCreateFromBuffer(ExtModel,ONNX_DEFAULT);
       if(model==INVALID_HANDLE)
         {
          PrintFormat("model_name=%s OnnxCreate error %d for",model_name,GetLastError());
         }
       else
         {
          //--- test all dataset
          double model_accuracy=0;
          //-- test sample by sample execution for all Iris dataset
          if(TestAllIrisDataset(model,model_name,model_accuracy))
             PrintFormat("model=%s all samples accuracy=%f",model_name,model_accuracy);
          else
             PrintFormat("error in testing model=%s ",model_name);
          //--- test batch execution for several samples
          if(TestBatchExecution(model,model_name,model_accuracy))
             PrintFormat("model=%s batch test accuracy=%f",model_name,model_accuracy);
          else
             PrintFormat("error in testing model=%s ",model_name);
          //--- release model
          OnnxRelease(model);
         }
       return(0);
      }
    //+------------------------------------------------------------------+
    

    Resultado:

    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
    
    A precisão do modelo ONNX exportado no iris dataset completo é de 66,67%, o que corresponde à precisão do original.


    2.20.3. Representação ONNX do modelo Complement Naive Bayes (CNB) Classifier

    Fig.34. Representação do modelo Complement Naive Bayes (CNB) Classifier em Netron

    Fig.34. Representação do modelo Complement Naive Bayes (CNB) Classifier em Netron


    2.21. Bernoulli Naive Bayes (BNB) Classifier

    Bernoulli Naive Bayes (BNB) Classifier é outra variante do classificador bayesiano ingênuo, utilizado para tarefas de classificação binária. Este classificador é particularmente útil em situações onde as características são representadas por dados binários, por exemplo, em tarefas de análise de texto, onde as características podem ser a presença ou ausência de palavras no texto.

    Princípios do funcionamento do Bernoulli Naive Bayes Classifier:

    1. Abordagem Bayesiana: Assim como outros classificadores bayesianos, o BNB é baseado na abordagem bayesiana para classificação e usa o teorema de Bayes para calcular a probabilidade de um objeto pertencer a cada classe.
    2. Suposição sobre características binárias: A principal suposição do BNB é que as características são dados binários, ou seja, eles podem ter apenas dois valores, como 1 e 0, onde 1 indica a presença da característica e 0 a sua ausência.
    3. Treinamento dos parâmetros: O modelo BNB é treinado no conjunto de dados de treinamento, calculando os parâmetros de distribuição para cada recurso em cada classe.
    Vantagens do Bernoulli Naive Bayes Classifier:
    • Eficiência para dados binários: O BNB funciona bem em tarefas onde as características são representadas por dados binários, e pode ser especialmente útil em tarefas de análise de texto ou classificação de eventos.
    • Simplicidade e rapidez no treinamento: Como outros classificadores bayesianos ingênuos, o BNB é um algoritmo simples que treina rapidamente.
    Limitações do Bernoulli Naive Bayes Classifier:
    • Limitação para características binárias: O BNB não é adequado para tarefas onde as características não são binárias. Se as características têm mais de dois valores, o BNB não leva essa informação em conta.
    • Suposição Ingênua: Assim como outros classificadores bayesianos ingênuos, o BNB faz a suposição de independência entre as características, o que pode ser demasiadamente simplificado para algumas tarefas.

    O Bernoulli Naive Bayes Classifier é uma boa escolha para tarefas de classificação binária com características binárias, como análise de sentimento ou classificação de spam. Ele é simples de usar e lida bem com dados desse tipo.


    2.21.1. Código de criação do modelo Bernoulli Naive Bayes (BNB) Classifier

    Este código demonstra o processo de treinamento do modelo Bernoulli Naive Bayes (BNB) Classifier no conjunto de dados Iris, exportação para o formato ONNX e realização de classificação usando o modelo ONNX. Também avalia a precisão tanto do modelo original quanto do modelo 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)
    

    Resultado:

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


    2.21.2. Código no MQL5 para trabalhar com o modelo Bernoulli Naive Bayes (BNB) Classifier

    //+------------------------------------------------------------------+
    //|                                           Iris_BNBClassifier.mq5 |
    //|                                  Copyright 2023, MetaQuotes Ltd. |
    //|                                             https://www.mql5.com |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2023, MetaQuotes Ltd."
    #property link      "https://www.mql5.com"
    #property version   "1.00"
    
    #include "iris.mqh"
    #resource "bnb_classifier_iris.onnx" as const uchar ExtModel[];
    
    //+------------------------------------------------------------------+
    //| Test IRIS dataset samples                                        |
    //+------------------------------------------------------------------+
    bool TestSamples(long model,float &input_data[][4], int &model_classes_id[])
      {
    //--- check number of input samples
       ulong batch_size=input_data.Range(0);
       if(batch_size==0)
          return(false);
    //--- prepare output array
       ArrayResize(model_classes_id,(int)batch_size);
    //---
       float output_data[];
    //---
       struct Map
         {
          ulong          key[];
          float          value[];
         } output_data_map[];
    //--- check consistency
       bool res=ArrayResize(output_data,(int)batch_size)==batch_size;
    //---
       if(res)
         {
          //--- set input shape
          ulong input_shape[]= {batch_size,input_data.Range(1)};
          OnnxSetInputShape(model,0,input_shape);
          //--- set output shapeы
          ulong output_shape1[]= {batch_size};
          ulong output_shape2[]= {batch_size};
          OnnxSetOutputShape(model,0,output_shape1);
          OnnxSetOutputShape(model,1,output_shape2);
          //--- run the model
          res=OnnxRun(model,0,input_data,output_data,output_data_map);
          //--- postprocessing
          if(res)
            {
             //--- postprocessing of sequence map data
             //--- find class with maximum probability
             ulong output_keys[];
             float output_values[];
             //---
             for(uint n=0; n<output_data_map.Size(); n++)
               {
                int model_class_id=-1;
                int max_idx=-1;
                float max_value=-1;
                //--- copy to arrays
                ArrayCopy(output_keys,output_data_map[n].key);
                ArrayCopy(output_values,output_data_map[n].value);
                //ArrayPrint(output_keys);
                //ArrayPrint(output_values);
                //--- find the key with maximum probability
                for(int k=0; k<ArraySize(output_values); k++)
                  {
                   if(k==0)
                     {
                      max_idx=0;
                      max_value=output_values[max_idx];
                      model_class_id=(int)output_keys[max_idx];
                     }
                   else
                     {
                      if(output_values[k]>max_value)
                        {
                         max_idx=k;
                         max_value=output_values[max_idx];
                         model_class_id=(int)output_keys[max_idx];
                        }
                     }
                  }
                //--- store the result to the output array
                model_classes_id[n]=model_class_id;
                //Print("model_class_id=",model_class_id);
               }
            }
         }
    //---
       return(res);
      }
    
    //+------------------------------------------------------------------+
    //| Test all samples from IRIS dataset (150)                         |
    //| Here we test all samples with batch=1, sample by sample          |
    //+------------------------------------------------------------------+
    bool TestAllIrisDataset(const long model,const string model_name,double &model_accuracy)
      {
       sIRISsample iris_samples[];
    //--- load dataset from file
       PrepareIrisDataset(iris_samples);
    //--- test
       int total_samples=ArraySize(iris_samples);
       if(total_samples==0)
         {
          Print("iris dataset not prepared");
          return(false);
         }
    //--- show dataset
       for(int k=0; k<total_samples; k++)
         {
          //PrintFormat("%d (%.2f,%.2f,%.2f,%.2f) class %d (%s)",iris_samples[k].sample_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3],iris_samples[k].class_id,iris_samples[k].class_name);
         }
    //--- array for output classes
       int model_output_classes_id[];
    //--- check all Iris dataset samples
       int correct_results=0;
       for(int k=0; k<total_samples; k++)
         {
          //--- input array
          float iris_sample_input_data[1][4];
          //--- prepare input data from kth iris sample dataset
          iris_sample_input_data[0][0]=(float)iris_samples[k].features[0];
          iris_sample_input_data[0][1]=(float)iris_samples[k].features[1];
          iris_sample_input_data[0][2]=(float)iris_samples[k].features[2];
          iris_sample_input_data[0][3]=(float)iris_samples[k].features[3];
          //--- run model
          bool res=TestSamples(model,iris_sample_input_data,model_output_classes_id);
          //--- check result
          if(res)
            {
             if(model_output_classes_id[0]==iris_samples[k].class_id)
               {
                correct_results++;
               }
             else
               {
                PrintFormat("model:%s  sample=%d FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f]",model_name,iris_samples[k].sample_id,model_output_classes_id[0],iris_samples[k].class_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3]);
               }
            }
         }
       model_accuracy=1.0*correct_results/total_samples;
    //---
       PrintFormat("model:%s   correct results: %.2f%%",model_name,100*model_accuracy);
    //---
       return(true);
      }
    
    //+------------------------------------------------------------------+
    //| Here we test batch execution of the model                        |
    //+------------------------------------------------------------------+
    bool TestBatchExecution(const long model,const string model_name,double &model_accuracy)
      {
       model_accuracy=0;
    //--- array for output classes
       int model_output_classes_id[];
       int correct_results=0;
       int total_results=0;
       bool res=false;
    
    //--- run batch with 3 samples
       float input_data_batch3[3][4]=
         {
            {5.1f,3.5f,1.4f,0.2f}, // iris dataset sample id=1, Iris-setosa
            {6.3f,2.5f,4.9f,1.5f}, // iris dataset sample id=73, Iris-versicolor
            {6.3f,2.7f,4.9f,1.8f}  // iris dataset sample id=124, Iris-virginica
         };
       int correct_classes_batch3[3]= {0,1,2};
    //--- run model
       res=TestSamples(model,input_data_batch3,model_output_classes_id);
       if(res)
         {
          //--- check result
          for(int j=0; j<ArraySize(model_output_classes_id); j++)
            {
             //--- check result
             if(model_output_classes_id[j]==correct_classes_batch3[j])
                correct_results++;
             else
               {
                PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch3[j],input_data_batch3[j][0],input_data_batch3[j][1],input_data_batch3[j][2],input_data_batch3[j][3]);
               }
             total_results++;
            }
         }
       else
          return(false);
    
    //--- run batch with 10 samples
       float input_data_batch10[10][4]=
         {
            {5.5f,3.5f,1.3f,0.2f}, // iris dataset sample id=37 (Iris-setosa)
            {4.9f,3.1f,1.5f,0.1f}, // iris dataset sample id=38 (Iris-setosa)
            {4.4f,3.0f,1.3f,0.2f}, // iris dataset sample id=39 (Iris-setosa)
            {5.0f,3.3f,1.4f,0.2f}, // iris dataset sample id=50 (Iris-setosa)
            {7.0f,3.2f,4.7f,1.4f}, // iris dataset sample id=51 (Iris-versicolor)
            {6.4f,3.2f,4.5f,1.5f}, // iris dataset sample id=52 (Iris-versicolor)
            {6.3f,3.3f,6.0f,2.5f}, // iris dataset sample id=101 (Iris-virginica)
            {5.8f,2.7f,5.1f,1.9f}, // iris dataset sample id=102 (Iris-virginica)
            {7.1f,3.0f,5.9f,2.1f}, // iris dataset sample id=103 (Iris-virginica)
            {6.3f,2.9f,5.6f,1.8f}  // iris dataset sample id=104 (Iris-virginica)
         };
    //--- correct classes for all 10 samples in the batch
       int correct_classes_batch10[10]= {0,0,0,0,1,1,2,2,2,2};
    
    //--- run model
       res=TestSamples(model,input_data_batch10,model_output_classes_id);
    //--- check result
       if(res)
         {
          for(int j=0; j<ArraySize(model_output_classes_id); j++)
            {
             if(model_output_classes_id[j]==correct_classes_batch10[j])
                correct_results++;
             else
               {
                double f1=input_data_batch10[j][0];
                double f2=input_data_batch10[j][1];
                double f3=input_data_batch10[j][2];
                double f4=input_data_batch10[j][3];
                PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch10[j],input_data_batch10[j][0],input_data_batch10[j][1],input_data_batch10[j][2],input_data_batch10[j][3]);
               }
             total_results++;
            }
         }
       else
          return(false);
    
    //--- calculate accuracy
       model_accuracy=correct_results/total_results;
    //---
       return(res);
      }
    //+------------------------------------------------------------------+
    //| Script program start function                                    |
    //+------------------------------------------------------------------+
    int OnStart(void)
      {
       string model_name="BNBClassifier";
    //---
       long model=OnnxCreateFromBuffer(ExtModel,ONNX_DEFAULT);
       if(model==INVALID_HANDLE)
         {
          PrintFormat("model_name=%s OnnxCreate error %d for",model_name,GetLastError());
         }
       else
         {
          //--- test all dataset
          double model_accuracy=0;
          //-- test sample by sample execution for all Iris dataset
          if(TestAllIrisDataset(model,model_name,model_accuracy))
             PrintFormat("model=%s all samples accuracy=%f",model_name,model_accuracy);
          else
             PrintFormat("error in testing model=%s ",model_name);
          //--- test batch execution for several samples
          if(TestBatchExecution(model,model_name,model_accuracy))
             PrintFormat("model=%s batch test accuracy=%f",model_name,model_accuracy);
          else
             PrintFormat("error in testing model=%s ",model_name);
          //--- release model
          OnnxRelease(model);
         }
       return(0);
      }
    //+------------------------------------------------------------------+
    

    Resultado:

    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
    

    A precisão do modelo ONNX exportado no iris dataset completo é de 33,33%, o que corresponde à precisão do original.


    2.21.3. Representação ONNX do modelo Bernoulli Naive Bayes (BNB) Classifier

    Fig.35. Representação do modelo Bernoulli Naive Bayes (BNB) Classifier em Netron

    Fig.35. Representação do modelo Bernoulli Naive Bayes (BNB) Classifier em Netron


    2.22. Multilayer perceptron Classifier

    O MLP Classifier (Multilayer perceptron Classifier) é uma rede neural multicamadas usada para tarefas de classificação. Ele é composto por um perceptron multicamadas, que inclui várias camadas de neurônios, incluindo a camada de entrada, camadas ocultas e a camada de saída. O MLP Classifier tem a capacidade de aprender dependências complexas não lineares nos dados.

    Princípios do funcionamento do MLP Classifier:
    1. Arquitetura multicamada: O MLP Classifier possui uma arquitetura multicamada, que inclui a camada de entrada, uma ou mais camadas ocultas e a camada de saída. Cada neurônio nas camadas está conectado aos neurônios nas camadas adjacentes com pesos que são ajustados durante o treinamento.
    2. Funções de ativação: Dentro de cada neurônio, uma função de ativação é aplicada, introduzindo não linearidade no modelo e permitindo que o MLP Classifier modele dependências complexas nos dados.
    3. Treinamento com retropropagação: O MLP Classifier é treinado usando o método de retropropagação (backpropagation), que minimiza o erro entre as previsões do modelo e as verdadeiras labels das classes.
    Vantagens do MLP Classifier:
    • Capacidade de modelar dependências complexas: O MLP Classifier pode aprender dependências complexas não lineares nos dados e, portanto, pode oferecer bons resultados em tarefas onde modelos lineares simples são insuficientes.
    • Versatilidade: O MLP Classifier pode ser usado para uma ampla gama de tarefas de classificação, incluindo classificação multiclasse e tarefas multitarefa.
    Limitações do MLP Classifier:
    • Sensibilidade a hiperparâmetros: O MLP Classifier tem muitos hiperparâmetros, como o número de camadas ocultas, o número de neurônios em cada camada, a taxa de aprendizado, entre outros. Ajustar esses parâmetros pode exigir muito tempo e recursos.
    • Necessidade de uma grande quantidade de dados: O MLP Classifier requer uma grande quantidade de dados para treinamento, a fim de evitar sobreajuste, especialmente quando o modelo possui muitos parâmetros.
    • Tendência ao sobreajuste: Se o modelo tiver muitos parâmetros ou dados insuficientes, pode ocorrer sobreajuste, resultando em desempenho ruim em novos dados.

    O MLP Classifier é uma ferramenta poderosa para tarefas de classificação, especialmente em casos onde os dados apresentam dependências complexas. Ele é usado com frequência nas áreas de aprendizado de máquina e aprendizado profundo para resolver várias tarefas de classificação. No entanto, para aplicar com sucesso esse modelo, é importante ajustar corretamente seus hiperparâmetros e garantir um volume suficiente de dados de treinamento.


    2.22.1. Código de criação do modelo Multilayer Perceptron Classifier

    Este código demonstra o processo de treinamento do modelo Multilayer perceptron Classifier no conjunto de dados Iris, exportação para o formato ONNX e realização de classificação usando o modelo ONNX. Também avalia a precisão tanto do modelo original quanto do modelo 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)
    

    Resultado:

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


    2.22.2. Código no MQL5 para trabalhar com o modelo Multilayer perceptron Classifier

    //+------------------------------------------------------------------+
    //|                                           Iris_MLPClassifier.mq5 |
    //|                                  Copyright 2023, MetaQuotes Ltd. |
    //|                                             https://www.mql5.com |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2023, MetaQuotes Ltd."
    #property link      "https://www.mql5.com"
    #property version   "1.00"
    
    #include "iris.mqh"
    #resource "mlp_classifier_iris.onnx" as const uchar ExtModel[];
    
    //+------------------------------------------------------------------+
    //| Test IRIS dataset samples                                        |
    //+------------------------------------------------------------------+
    bool TestSamples(long model,float &input_data[][4], int &model_classes_id[])
      {
    //--- check number of input samples
       ulong batch_size=input_data.Range(0);
       if(batch_size==0)
          return(false);
    //--- prepare output array
       ArrayResize(model_classes_id,(int)batch_size);
    //---
       float output_data[];
    //---
       struct Map
         {
          ulong          key[];
          float          value[];
         } output_data_map[];
    //--- check consistency
       bool res=ArrayResize(output_data,(int)batch_size)==batch_size;
    //---
       if(res)
         {
          //--- set input shape
          ulong input_shape[]= {batch_size,input_data.Range(1)};
          OnnxSetInputShape(model,0,input_shape);
          //--- set output shapeы
          ulong output_shape1[]= {batch_size};
          ulong output_shape2[]= {batch_size};
          OnnxSetOutputShape(model,0,output_shape1);
          OnnxSetOutputShape(model,1,output_shape2);
          //--- run the model
          res=OnnxRun(model,0,input_data,output_data,output_data_map);
          //--- postprocessing
          if(res)
            {
             //--- postprocessing of sequence map data
             //--- find class with maximum probability
             ulong output_keys[];
             float output_values[];
             //---
             for(uint n=0; n<output_data_map.Size(); n++)
               {
                int model_class_id=-1;
                int max_idx=-1;
                float max_value=-1;
                //--- copy to arrays
                ArrayCopy(output_keys,output_data_map[n].key);
                ArrayCopy(output_values,output_data_map[n].value);
                //ArrayPrint(output_keys);
                //ArrayPrint(output_values);
                //--- find the key with maximum probability
                for(int k=0; k<ArraySize(output_values); k++)
                  {
                   if(k==0)
                     {
                      max_idx=0;
                      max_value=output_values[max_idx];
                      model_class_id=(int)output_keys[max_idx];
                     }
                   else
                     {
                      if(output_values[k]>max_value)
                        {
                         max_idx=k;
                         max_value=output_values[max_idx];
                         model_class_id=(int)output_keys[max_idx];
                        }
                     }
                  }
                //--- store the result to the output array
                model_classes_id[n]=model_class_id;
                //Print("model_class_id=",model_class_id);
               }
            }
         }
    //---
       return(res);
      }
    
    //+------------------------------------------------------------------+
    //| Test all samples from IRIS dataset (150)                         |
    //| Here we test all samples with batch=1, sample by sample          |
    //+------------------------------------------------------------------+
    bool TestAllIrisDataset(const long model,const string model_name,double &model_accuracy)
      {
       sIRISsample iris_samples[];
    //--- load dataset from file
       PrepareIrisDataset(iris_samples);
    //--- test
       int total_samples=ArraySize(iris_samples);
       if(total_samples==0)
         {
          Print("iris dataset not prepared");
          return(false);
         }
    //--- show dataset
       for(int k=0; k<total_samples; k++)
         {
          //PrintFormat("%d (%.2f,%.2f,%.2f,%.2f) class %d (%s)",iris_samples[k].sample_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3],iris_samples[k].class_id,iris_samples[k].class_name);
         }
    //--- array for output classes
       int model_output_classes_id[];
    //--- check all Iris dataset samples
       int correct_results=0;
       for(int k=0; k<total_samples; k++)
         {
          //--- input array
          float iris_sample_input_data[1][4];
          //--- prepare input data from kth iris sample dataset
          iris_sample_input_data[0][0]=(float)iris_samples[k].features[0];
          iris_sample_input_data[0][1]=(float)iris_samples[k].features[1];
          iris_sample_input_data[0][2]=(float)iris_samples[k].features[2];
          iris_sample_input_data[0][3]=(float)iris_samples[k].features[3];
          //--- run model
          bool res=TestSamples(model,iris_sample_input_data,model_output_classes_id);
          //--- check result
          if(res)
            {
             if(model_output_classes_id[0]==iris_samples[k].class_id)
               {
                correct_results++;
               }
             else
               {
                PrintFormat("model:%s  sample=%d FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f]",model_name,iris_samples[k].sample_id,model_output_classes_id[0],iris_samples[k].class_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3]);
               }
            }
         }
       model_accuracy=1.0*correct_results/total_samples;
    //---
       PrintFormat("model:%s   correct results: %.2f%%",model_name,100*model_accuracy);
    //---
       return(true);
      }
    
    //+------------------------------------------------------------------+
    //| Here we test batch execution of the model                        |
    //+------------------------------------------------------------------+
    bool TestBatchExecution(const long model,const string model_name,double &model_accuracy)
      {
       model_accuracy=0;
    //--- array for output classes
       int model_output_classes_id[];
       int correct_results=0;
       int total_results=0;
       bool res=false;
    
    //--- run batch with 3 samples
       float input_data_batch3[3][4]=
         {
            {5.1f,3.5f,1.4f,0.2f}, // iris dataset sample id=1, Iris-setosa
            {6.3f,2.5f,4.9f,1.5f}, // iris dataset sample id=73, Iris-versicolor
            {6.3f,2.7f,4.9f,1.8f}  // iris dataset sample id=124, Iris-virginica
         };
       int correct_classes_batch3[3]= {0,1,2};
    //--- run model
       res=TestSamples(model,input_data_batch3,model_output_classes_id);
       if(res)
         {
          //--- check result
          for(int j=0; j<ArraySize(model_output_classes_id); j++)
            {
             //--- check result
             if(model_output_classes_id[j]==correct_classes_batch3[j])
                correct_results++;
             else
               {
                PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch3[j],input_data_batch3[j][0],input_data_batch3[j][1],input_data_batch3[j][2],input_data_batch3[j][3]);
               }
             total_results++;
            }
         }
       else
          return(false);
    
    //--- run batch with 10 samples
       float input_data_batch10[10][4]=
         {
            {5.5f,3.5f,1.3f,0.2f}, // iris dataset sample id=37 (Iris-setosa)
            {4.9f,3.1f,1.5f,0.1f}, // iris dataset sample id=38 (Iris-setosa)
            {4.4f,3.0f,1.3f,0.2f}, // iris dataset sample id=39 (Iris-setosa)
            {5.0f,3.3f,1.4f,0.2f}, // iris dataset sample id=50 (Iris-setosa)
            {7.0f,3.2f,4.7f,1.4f}, // iris dataset sample id=51 (Iris-versicolor)
            {6.4f,3.2f,4.5f,1.5f}, // iris dataset sample id=52 (Iris-versicolor)
            {6.3f,3.3f,6.0f,2.5f}, // iris dataset sample id=101 (Iris-virginica)
            {5.8f,2.7f,5.1f,1.9f}, // iris dataset sample id=102 (Iris-virginica)
            {7.1f,3.0f,5.9f,2.1f}, // iris dataset sample id=103 (Iris-virginica)
            {6.3f,2.9f,5.6f,1.8f}  // iris dataset sample id=104 (Iris-virginica)
         };
    //--- correct classes for all 10 samples in the batch
       int correct_classes_batch10[10]= {0,0,0,0,1,1,2,2,2,2};
    
    //--- run model
       res=TestSamples(model,input_data_batch10,model_output_classes_id);
    //--- check result
       if(res)
         {
          for(int j=0; j<ArraySize(model_output_classes_id); j++)
            {
             if(model_output_classes_id[j]==correct_classes_batch10[j])
                correct_results++;
             else
               {
                double f1=input_data_batch10[j][0];
                double f2=input_data_batch10[j][1];
                double f3=input_data_batch10[j][2];
                double f4=input_data_batch10[j][3];
                PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch10[j],input_data_batch10[j][0],input_data_batch10[j][1],input_data_batch10[j][2],input_data_batch10[j][3]);
               }
             total_results++;
            }
         }
       else
          return(false);
    
    //--- calculate accuracy
       model_accuracy=correct_results/total_results;
    //---
       return(res);
      }
    //+------------------------------------------------------------------+
    //| Script program start function                                    |
    //+------------------------------------------------------------------+
    int OnStart(void)
      {
       string model_name="MLPClassifier";
    //---
       long model=OnnxCreateFromBuffer(ExtModel,ONNX_DEFAULT);
       if(model==INVALID_HANDLE)
         {
          PrintFormat("model_name=%s OnnxCreate error %d for",model_name,GetLastError());
         }
       else
         {
          //--- test all dataset
          double model_accuracy=0;
          //-- test sample by sample execution for all Iris dataset
          if(TestAllIrisDataset(model,model_name,model_accuracy))
             PrintFormat("model=%s all samples accuracy=%f",model_name,model_accuracy);
          else
             PrintFormat("error in testing model=%s ",model_name);
          //--- test batch execution for several samples
          if(TestBatchExecution(model,model_name,model_accuracy))
             PrintFormat("model=%s batch test accuracy=%f",model_name,model_accuracy);
          else
             PrintFormat("error in testing model=%s ",model_name);
          //--- release model
          OnnxRelease(model);
         }
       return(0);
      }
    //+------------------------------------------------------------------+
    

    Resultado:

    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
    

    A precisão do modelo ONNX exportado no iris dataset completo é de 98%, o que corresponde à precisão do original.


    2.22.3. Representação ONNX do modelo Multilayer perceptron Classifier

    Fig.36. Representação do modelo Multilayer perceptron Classifier em Netron

    Fig.36. Representação do modelo Multilayer perceptron Classifier em Netron


    2.23. Linear Discriminant Analysis (LDA) Classifier

    O Linear Discriminant Analysis (LDA) Classifier é um método de aprendizado de máquina usado para resolver tarefas de classificação. Ele pertence à família de métodos de redução de dimensionalidade e classificação em um espaço de dimensão menor. O LDA constrói hiperplanos de forma a maximizar a separação entre as classes.

    Princípios do funcionamento do LDA Classifier:
    1. Redução de dimensionalidade: A ideia principal do LDA é a redução de dimensionalidade dos dados. Ele busca encontrar um novo espaço de características onde as classes de dados são maximamente separadas entre si.
    2. Maximização da separação: O LDA constrói hiperplanos (combinações lineares de características) que maximizam a diferença entre as médias dos valores das características em diferentes classes e minimizam a variação dentro de cada classe.
    3. Treinamento dos parâmetros: O modelo LDA é treinado no conjunto de dados de treinamento, calculando os parâmetros dos hiperplanos e projeções dos dados para o novo espaço.
    Vantagens do LDA Classifier:
    • Melhoria na separação das classes: O LDA pode melhorar significativamente a separação das classes nos dados, especialmente em casos onde as classes se sobrepõem significativamente no espaço original de características.
    • Redução de dimensionalidade: O LDA também pode ser usado para reduzir a dimensionalidade dos dados, o que pode ser útil para visualização e redução da complexidade computacional da tarefa.
    Limitações do LDA Classifier:
    • Necessidade de distribuição normal: O LDA assume que as características têm uma distribuição normal e que as classes têm matrizes de covariância iguais. Se essas suposições não forem atendidas, o LDA pode fornecer resultados menos precisos.
    • Sensibilidade a valores atípicos: O LDA pode ser sensível a valores atípicos nos dados, pois eles podem afetar o cálculo dos parâmetros do modelo.
    • Dificuldades na classificação multiclasse: O LDA foi originalmente desenvolvido para classificação binária, e sua aplicação a tarefas multiclasse requer a expansão do método.

    O LDA Classifier é um método útil para tarefas de classificação e redução de dimensionalidade de dados, especialmente quando há a necessidade de melhorar a separação entre as classes. É frequentemente utilizado em estatística, biologia, análise médica e outras áreas para análise de dados e classificação.


    2.23.1. Código de criação do modelo Linear Discriminant Analysis (LDA) Classifier

    Este código demonstra o processo de treinamento do modelo Linear Discriminant Analysis (LDA) Classifier no conjunto de dados Iris, exportação para o formato ONNX e realização de classificação usando o modelo ONNX. Também avalia a precisão tanto do modelo original quanto do modelo 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)
    

    Resultado:

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


    2.23.2. Código no MQL5 para trabalhar com o modelo Linear Discriminant Analysis (LDA) Classifier

    //+------------------------------------------------------------------+
    //|                                           Iris_LDAClassifier.mq5 |
    //|                                  Copyright 2023, MetaQuotes Ltd. |
    //|                                             https://www.mql5.com |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2023, MetaQuotes Ltd."
    #property link      "https://www.mql5.com"
    #property version   "1.00"
    
    #include "iris.mqh"
    #resource "lda_classifier_iris.onnx" as const uchar ExtModel[];
    
    //+------------------------------------------------------------------+
    //| Test IRIS dataset samples                                        |
    //+------------------------------------------------------------------+
    bool TestSamples(long model,float &input_data[][4], int &model_classes_id[])
      {
    //--- check number of input samples
       ulong batch_size=input_data.Range(0);
       if(batch_size==0)
          return(false);
    //--- prepare output array
       ArrayResize(model_classes_id,(int)batch_size);
    //---
       float output_data[];
    //---
       struct Map
         {
          ulong          key[];
          float          value[];
         } output_data_map[];
    //--- check consistency
       bool res=ArrayResize(output_data,(int)batch_size)==batch_size;
    //---
       if(res)
         {
          //--- set input shape
          ulong input_shape[]= {batch_size,input_data.Range(1)};
          OnnxSetInputShape(model,0,input_shape);
          //--- set output shapeы
          ulong output_shape1[]= {batch_size};
          ulong output_shape2[]= {batch_size};
          OnnxSetOutputShape(model,0,output_shape1);
          OnnxSetOutputShape(model,1,output_shape2);
          //--- run the model
          res=OnnxRun(model,0,input_data,output_data,output_data_map);
          //--- postprocessing
          if(res)
            {
             //--- postprocessing of sequence map data
             //--- find class with maximum probability
             ulong output_keys[];
             float output_values[];
             //---
             for(uint n=0; n<output_data_map.Size(); n++)
               {
                int model_class_id=-1;
                int max_idx=-1;
                float max_value=-1;
                //--- copy to arrays
                ArrayCopy(output_keys,output_data_map[n].key);
                ArrayCopy(output_values,output_data_map[n].value);
                //ArrayPrint(output_keys);
                //ArrayPrint(output_values);
                //--- find the key with maximum probability
                for(int k=0; k<ArraySize(output_values); k++)
                  {
                   if(k==0)
                     {
                      max_idx=0;
                      max_value=output_values[max_idx];
                      model_class_id=(int)output_keys[max_idx];
                     }
                   else
                     {
                      if(output_values[k]>max_value)
                        {
                         max_idx=k;
                         max_value=output_values[max_idx];
                         model_class_id=(int)output_keys[max_idx];
                        }
                     }
                  }
                //--- store the result to the output array
                model_classes_id[n]=model_class_id;
                //Print("model_class_id=",model_class_id);
               }
            }
         }
    //---
       return(res);
      }
    
    //+------------------------------------------------------------------+
    //| Test all samples from IRIS dataset (150)                         |
    //| Here we test all samples with batch=1, sample by sample          |
    //+------------------------------------------------------------------+
    bool TestAllIrisDataset(const long model,const string model_name,double &model_accuracy)
      {
       sIRISsample iris_samples[];
    //--- load dataset from file
       PrepareIrisDataset(iris_samples);
    //--- test
       int total_samples=ArraySize(iris_samples);
       if(total_samples==0)
         {
          Print("iris dataset not prepared");
          return(false);
         }
    //--- show dataset
       for(int k=0; k<total_samples; k++)
         {
          //PrintFormat("%d (%.2f,%.2f,%.2f,%.2f) class %d (%s)",iris_samples[k].sample_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3],iris_samples[k].class_id,iris_samples[k].class_name);
         }
    //--- array for output classes
       int model_output_classes_id[];
    //--- check all Iris dataset samples
       int correct_results=0;
       for(int k=0; k<total_samples; k++)
         {
          //--- input array
          float iris_sample_input_data[1][4];
          //--- prepare input data from kth iris sample dataset
          iris_sample_input_data[0][0]=(float)iris_samples[k].features[0];
          iris_sample_input_data[0][1]=(float)iris_samples[k].features[1];
          iris_sample_input_data[0][2]=(float)iris_samples[k].features[2];
          iris_sample_input_data[0][3]=(float)iris_samples[k].features[3];
          //--- run model
          bool res=TestSamples(model,iris_sample_input_data,model_output_classes_id);
          //--- check result
          if(res)
            {
             if(model_output_classes_id[0]==iris_samples[k].class_id)
               {
                correct_results++;
               }
             else
               {
                PrintFormat("model:%s  sample=%d FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f]",model_name,iris_samples[k].sample_id,model_output_classes_id[0],iris_samples[k].class_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3]);
               }
            }
         }
       model_accuracy=1.0*correct_results/total_samples;
    //---
       PrintFormat("model:%s   correct results: %.2f%%",model_name,100*model_accuracy);
    //---
       return(true);
      }
    
    //+------------------------------------------------------------------+
    //| Here we test batch execution of the model                        |
    //+------------------------------------------------------------------+
    bool TestBatchExecution(const long model,const string model_name,double &model_accuracy)
      {
       model_accuracy=0;
    //--- array for output classes
       int model_output_classes_id[];
       int correct_results=0;
       int total_results=0;
       bool res=false;
    
    //--- run batch with 3 samples
       float input_data_batch3[3][4]=
         {
            {5.1f,3.5f,1.4f,0.2f}, // iris dataset sample id=1, Iris-setosa
            {6.3f,2.5f,4.9f,1.5f}, // iris dataset sample id=73, Iris-versicolor
            {6.3f,2.7f,4.9f,1.8f}  // iris dataset sample id=124, Iris-virginica
         };
       int correct_classes_batch3[3]= {0,1,2};
    //--- run model
       res=TestSamples(model,input_data_batch3,model_output_classes_id);
       if(res)
         {
          //--- check result
          for(int j=0; j<ArraySize(model_output_classes_id); j++)
            {
             //--- check result
             if(model_output_classes_id[j]==correct_classes_batch3[j])
                correct_results++;
             else
               {
                PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch3[j],input_data_batch3[j][0],input_data_batch3[j][1],input_data_batch3[j][2],input_data_batch3[j][3]);
               }
             total_results++;
            }
         }
       else
          return(false);
    
    //--- run batch with 10 samples
       float input_data_batch10[10][4]=
         {
            {5.5f,3.5f,1.3f,0.2f}, // iris dataset sample id=37 (Iris-setosa)
            {4.9f,3.1f,1.5f,0.1f}, // iris dataset sample id=38 (Iris-setosa)
            {4.4f,3.0f,1.3f,0.2f}, // iris dataset sample id=39 (Iris-setosa)
            {5.0f,3.3f,1.4f,0.2f}, // iris dataset sample id=50 (Iris-setosa)
            {7.0f,3.2f,4.7f,1.4f}, // iris dataset sample id=51 (Iris-versicolor)
            {6.4f,3.2f,4.5f,1.5f}, // iris dataset sample id=52 (Iris-versicolor)
            {6.3f,3.3f,6.0f,2.5f}, // iris dataset sample id=101 (Iris-virginica)
            {5.8f,2.7f,5.1f,1.9f}, // iris dataset sample id=102 (Iris-virginica)
            {7.1f,3.0f,5.9f,2.1f}, // iris dataset sample id=103 (Iris-virginica)
            {6.3f,2.9f,5.6f,1.8f}  // iris dataset sample id=104 (Iris-virginica)
         };
    //--- correct classes for all 10 samples in the batch
       int correct_classes_batch10[10]= {0,0,0,0,1,1,2,2,2,2};
    
    //--- run model
       res=TestSamples(model,input_data_batch10,model_output_classes_id);
    //--- check result
       if(res)
         {
          for(int j=0; j<ArraySize(model_output_classes_id); j++)
            {
             if(model_output_classes_id[j]==correct_classes_batch10[j])
                correct_results++;
             else
               {
                double f1=input_data_batch10[j][0];
                double f2=input_data_batch10[j][1];
                double f3=input_data_batch10[j][2];
                double f4=input_data_batch10[j][3];
                PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch10[j],input_data_batch10[j][0],input_data_batch10[j][1],input_data_batch10[j][2],input_data_batch10[j][3]);
               }
             total_results++;
            }
         }
       else
          return(false);
    
    //--- calculate accuracy
       model_accuracy=correct_results/total_results;
    //---
       return(res);
      }
    //+------------------------------------------------------------------+
    //| Script program start function                                    |
    //+------------------------------------------------------------------+
    int OnStart(void)
      {
       string model_name="LDAClassifier";
    //---
       long model=OnnxCreateFromBuffer(ExtModel,ONNX_DEFAULT);
       if(model==INVALID_HANDLE)
         {
          PrintFormat("model_name=%s OnnxCreate error %d for",model_name,GetLastError());
         }
       else
         {
          //--- test all dataset
          double model_accuracy=0;
          //-- test sample by sample execution for all Iris dataset
          if(TestAllIrisDataset(model,model_name,model_accuracy))
             PrintFormat("model=%s all samples accuracy=%f",model_name,model_accuracy);
          else
             PrintFormat("error in testing model=%s ",model_name);
          //--- test batch execution for several samples
          if(TestBatchExecution(model,model_name,model_accuracy))
             PrintFormat("model=%s batch test accuracy=%f",model_name,model_accuracy);
          else
             PrintFormat("error in testing model=%s ",model_name);
          //--- release model
          OnnxRelease(model);
         }
       return(0);
      }
    //+------------------------------------------------------------------+
    

    Resultado:

    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
    

    A precisão do modelo ONNX exportado no iris dataset completo é de 98%, o que corresponde à precisão do original.


    2.23.3. Representação ONNX do modelo Linear Discriminant Analysis (LDA) Classifier

    Fig.37. Representação do modelo Linear Discriminant Analysis (LDA) Classifier em Netron

    Fig.37. Representação do modelo Linear Discriminant Analysis (LDA) Classifier em Netron


    2.24. Hist Gradient Boosting

    O Hist Gradient Boosting Classifier é um algoritmo de aprendizado de máquina que pertence à família do gradient boosting e é destinado a tarefas de classificação. É um método eficaz e poderoso, amplamente utilizado em análise de dados e aprendizado de máquina.

    Princípios do funcionamento do Hist Gradient Boosting Classifier:

    1. Gradient boosting: O Hist Gradient Boosting Classifier se baseia no método de gradient boosting, que constrói um ensemble de árvores de decisão para melhorar a classificação. Isso é feito treinando modelos fracos sucessivamente e ajustando os erros dos modelos anteriores.
    2. Uso de histogramas: Hist no nome significa que este algoritmo utiliza histogramas para processar dados eficientemente. Em vez de fazer uma busca completa nas características, o Hist Gradient Boosting constrói histogramas das características , o que permite calcular rapidamente as divisões nas árvores de decisão.
    3. Treinamento nos resíduos: Como outros métodos de gradient boosting, o Hist Gradient Boosting treina cada nova árvore nos resíduos do modelo anterior para refinar a previsão.
    Vantagens do Hist Gradient Boosting Classifier:
    • Alta precisão: O Hist Gradient Boosting Classifier geralmente oferece alta precisão na classificação, especialmente quando muitas árvores são usadas.
    • Eficiência: O uso de histogramas permite que o algoritmo processe grandes volumes de dados eficientemente e construa o ensemble rapidamente.
    • Capacidade de lidar com dados heterogêneos: O algoritmo é capaz de processar dados heterogêneos, incluindo características categóricas e numéricos.
    Limitações do Hist Gradient Boosting Classifier:
    • Sensibilidade ao sobreajuste: Com configurações inadequadas de parâmetros ou uso de muitas árvores, o Hist Gradient Boosting Classifier pode sofrer de sobreajuste.
    • Ajuste de parâmetros: Como outros algoritmos de gradient boosting, o Hist Gradient Boosting requer um ajuste cuidadoso dos parâmetros para alcançar o melhor desempenho.

    O Hist Gradient Boosting Classifier é um algoritmo poderoso para tarefas de classificação e regressão, fornecendo alta precisão e eficiência no processamento de dados. Ele encontra aplicação em várias áreas, como análise de dados, bioinformática, finanças e outras.


    2.24.1. Código de criação do modelo Histogram-Based Gradient Boosting Classifier

    Este código demonstra o processo de treinamento do modelo Histogram-Based Gradient Boosting Classifier no conjunto de dados Iris, exportação para o formato ONNX e realização de classificação usando o modelo ONNX. Também avalia a precisão tanto do modelo original quanto do modelo 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)
    

    Resultado:

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


    2.24.2. Código no MQL5 para trabalhar com o modelo Histogram-Based Gradient Boosting Classifier

    //+------------------------------------------------------------------+
    //|                          Iris_HistGradientBoostingClassifier.mq5 |
    //|                                  Copyright 2023, MetaQuotes Ltd. |
    //|                                             https://www.mql5.com |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2023, MetaQuotes Ltd."
    #property link      "https://www.mql5.com"
    #property version   "1.00"
    
    #include "iris.mqh"
    #resource "hist_gradient_boosting_classifier_iris.onnx" as const uchar ExtModel[];
    
    //+------------------------------------------------------------------+
    //| Test IRIS dataset samples                                        |
    //+------------------------------------------------------------------+
    bool TestSamples(long model,float &input_data[][4], int &model_classes_id[])
      {
    //--- check number of input samples
       ulong batch_size=input_data.Range(0);
       if(batch_size==0)
          return(false);
    //--- prepare output array
       ArrayResize(model_classes_id,(int)batch_size);
    //---
       float output_data[];
    //---
       struct Map
         {
          ulong          key[];
          float          value[];
         } output_data_map[];
    //--- check consistency
       bool res=ArrayResize(output_data,(int)batch_size)==batch_size;
    //---
       if(res)
         {
          //--- set input shape
          ulong input_shape[]= {batch_size,input_data.Range(1)};
          OnnxSetInputShape(model,0,input_shape);
          //--- set output shapeы
          ulong output_shape1[]= {batch_size};
          ulong output_shape2[]= {batch_size};
          OnnxSetOutputShape(model,0,output_shape1);
          OnnxSetOutputShape(model,1,output_shape2);
          //--- run the model
          res=OnnxRun(model,0,input_data,output_data,output_data_map);
          //--- postprocessing
          if(res)
            {
             //--- postprocessing of sequence map data
             //--- find class with maximum probability
             ulong output_keys[];
             float output_values[];
             //---
             for(uint n=0; n<output_data_map.Size(); n++)
               {
                int model_class_id=-1;
                int max_idx=-1;
                float max_value=-1;
                //--- copy to arrays
                ArrayCopy(output_keys,output_data_map[n].key);
                ArrayCopy(output_values,output_data_map[n].value);
                //ArrayPrint(output_keys);
                //ArrayPrint(output_values);
                //--- find the key with maximum probability
                for(int k=0; k<ArraySize(output_values); k++)
                  {
                   if(k==0)
                     {
                      max_idx=0;
                      max_value=output_values[max_idx];
                      model_class_id=(int)output_keys[max_idx];
                     }
                   else
                     {
                      if(output_values[k]>max_value)
                        {
                         max_idx=k;
                         max_value=output_values[max_idx];
                         model_class_id=(int)output_keys[max_idx];
                        }
                     }
                  }
                //--- store the result to the output array
                model_classes_id[n]=model_class_id;
                //Print("model_class_id=",model_class_id);
               }
            }
         }
    //---
       return(res);
      }
    
    //+------------------------------------------------------------------+
    //| Test all samples from IRIS dataset (150)                         |
    //| Here we test all samples with batch=1, sample by sample          |
    //+------------------------------------------------------------------+
    bool TestAllIrisDataset(const long model,const string model_name,double &model_accuracy)
      {
       sIRISsample iris_samples[];
    //--- load dataset from file
       PrepareIrisDataset(iris_samples);
    //--- test
       int total_samples=ArraySize(iris_samples);
       if(total_samples==0)
         {
          Print("iris dataset not prepared");
          return(false);
         }
    //--- show dataset
       for(int k=0; k<total_samples; k++)
         {
          //PrintFormat("%d (%.2f,%.2f,%.2f,%.2f) class %d (%s)",iris_samples[k].sample_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3],iris_samples[k].class_id,iris_samples[k].class_name);
         }
    //--- array for output classes
       int model_output_classes_id[];
    //--- check all Iris dataset samples
       int correct_results=0;
       for(int k=0; k<total_samples; k++)
         {
          //--- input array
          float iris_sample_input_data[1][4];
          //--- prepare input data from kth iris sample dataset
          iris_sample_input_data[0][0]=(float)iris_samples[k].features[0];
          iris_sample_input_data[0][1]=(float)iris_samples[k].features[1];
          iris_sample_input_data[0][2]=(float)iris_samples[k].features[2];
          iris_sample_input_data[0][3]=(float)iris_samples[k].features[3];
          //--- run model
          bool res=TestSamples(model,iris_sample_input_data,model_output_classes_id);
          //--- check result
          if(res)
            {
             if(model_output_classes_id[0]==iris_samples[k].class_id)
               {
                correct_results++;
               }
             else
               {
                PrintFormat("model:%s  sample=%d FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f]",model_name,iris_samples[k].sample_id,model_output_classes_id[0],iris_samples[k].class_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3]);
               }
            }
         }
       model_accuracy=1.0*correct_results/total_samples;
    //---
       PrintFormat("model:%s   correct results: %.2f%%",model_name,100*model_accuracy);
    //---
       return(true);
      }
    
    //+------------------------------------------------------------------+
    //| Here we test batch execution of the model                        |
    //+------------------------------------------------------------------+
    bool TestBatchExecution(const long model,const string model_name,double &model_accuracy)
      {
       model_accuracy=0;
    //--- array for output classes
       int model_output_classes_id[];
       int correct_results=0;
       int total_results=0;
       bool res=false;
    
    //--- run batch with 3 samples
       float input_data_batch3[3][4]=
         {
            {5.1f,3.5f,1.4f,0.2f}, // iris dataset sample id=1, Iris-setosa
            {6.3f,2.5f,4.9f,1.5f}, // iris dataset sample id=73, Iris-versicolor
            {6.3f,2.7f,4.9f,1.8f}  // iris dataset sample id=124, Iris-virginica
         };
       int correct_classes_batch3[3]= {0,1,2};
    //--- run model
       res=TestSamples(model,input_data_batch3,model_output_classes_id);
       if(res)
         {
          //--- check result
          for(int j=0; j<ArraySize(model_output_classes_id); j++)
            {
             //--- check result
             if(model_output_classes_id[j]==correct_classes_batch3[j])
                correct_results++;
             else
               {
                PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch3[j],input_data_batch3[j][0],input_data_batch3[j][1],input_data_batch3[j][2],input_data_batch3[j][3]);
               }
             total_results++;
            }
         }
       else
          return(false);
    
    //--- run batch with 10 samples
       float input_data_batch10[10][4]=
         {
            {5.5f,3.5f,1.3f,0.2f}, // iris dataset sample id=37 (Iris-setosa)
            {4.9f,3.1f,1.5f,0.1f}, // iris dataset sample id=38 (Iris-setosa)
            {4.4f,3.0f,1.3f,0.2f}, // iris dataset sample id=39 (Iris-setosa)
            {5.0f,3.3f,1.4f,0.2f}, // iris dataset sample id=50 (Iris-setosa)
            {7.0f,3.2f,4.7f,1.4f}, // iris dataset sample id=51 (Iris-versicolor)
            {6.4f,3.2f,4.5f,1.5f}, // iris dataset sample id=52 (Iris-versicolor)
            {6.3f,3.3f,6.0f,2.5f}, // iris dataset sample id=101 (Iris-virginica)
            {5.8f,2.7f,5.1f,1.9f}, // iris dataset sample id=102 (Iris-virginica)
            {7.1f,3.0f,5.9f,2.1f}, // iris dataset sample id=103 (Iris-virginica)
            {6.3f,2.9f,5.6f,1.8f}  // iris dataset sample id=104 (Iris-virginica)
         };
    //--- correct classes for all 10 samples in the batch
       int correct_classes_batch10[10]= {0,0,0,0,1,1,2,2,2,2};
    
    //--- run model
       res=TestSamples(model,input_data_batch10,model_output_classes_id);
    //--- check result
       if(res)
         {
          for(int j=0; j<ArraySize(model_output_classes_id); j++)
            {
             if(model_output_classes_id[j]==correct_classes_batch10[j])
                correct_results++;
             else
               {
                double f1=input_data_batch10[j][0];
                double f2=input_data_batch10[j][1];
                double f3=input_data_batch10[j][2];
                double f4=input_data_batch10[j][3];
                PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch10[j],input_data_batch10[j][0],input_data_batch10[j][1],input_data_batch10[j][2],input_data_batch10[j][3]);
               }
             total_results++;
            }
         }
       else
          return(false);
    
    //--- calculate accuracy
       model_accuracy=correct_results/total_results;
    //---
       return(res);
      }
    //+------------------------------------------------------------------+
    //| Script program start function                                    |
    //+------------------------------------------------------------------+
    int OnStart(void)
      {
       string model_name="HistGradientBoostingClassifier";
    //---
       long model=OnnxCreateFromBuffer(ExtModel,ONNX_DEFAULT);
       if(model==INVALID_HANDLE)
         {
          PrintFormat("model_name=%s OnnxCreate error %d for",model_name,GetLastError());
         }
       else
         {
          //--- test all dataset
          double model_accuracy=0;
          //-- test sample by sample execution for all Iris dataset
          if(TestAllIrisDataset(model,model_name,model_accuracy))
             PrintFormat("model=%s all samples accuracy=%f",model_name,model_accuracy);
          else
             PrintFormat("error in testing model=%s ",model_name);
          //--- test batch execution for several samples
          if(TestBatchExecution(model,model_name,model_accuracy))
             PrintFormat("model=%s batch test accuracy=%f",model_name,model_accuracy);
          else
             PrintFormat("error in testing model=%s ",model_name);
          //--- release model
          OnnxRelease(model);
         }
       return(0);
      }
    //+------------------------------------------------------------------+
    

    Resultado:

    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
    

    A precisão do modelo ONNX exportado no conjunto de dados completo de íris é de 100%, o que corresponde à precisão do original.


    2.24.3. Representação ONNX do modelo Histogram-Based Gradient Boosting Classifier

    Fig.38. Representação ONNX do modelo Histogram-Based Gradient Boosting Classifier em Netron

    Fig.38. Representação ONNX do modelo Histogram-Based Gradient Boosting Classifier em Netron


    2.25. CategoricalNB Classifier

    O CategoricalNB é um algoritmo de classificação baseado no teorema de Bayes. Ele é especificamente projetado para conjuntos de dados com características categóricas e é amplamente utilizado na classificação de texto, detecção de spam e outras aplicações relacionadas a dados discretos.

    Princípios do funcionamento do CategoricalNB:

    1. Classificador bayesiano ingênuo: O CategoricalNB é um dos tipos de classificadores bayesianos ingênuos, baseados no teorema de Bayes. Ele calcula a probabilidade de pertencer a uma certa classe para um conjunto de características, utilizando as probabilidades condicionais de cada característica dado o classe.
    2. Características categóricas: Ao contrário do classificador bayesiano ingênuo gaussiano, que assume características contínuas com distribuição normal, o CategoricalNB é adequado para conjuntos de dados com características categóricas. Ele modela a distribuição probabilística de cada característica por classe.
    3. Suposição de independência: A "ingenuidade" no classificador bayesiano ingênuo vem da suposição de independência entre as características. O CategoricalNB assume que as características são condicionalmente independentes dado o classe. Apesar dessa suposição nem sempre ser mantida na prática, os métodos bayesianos ingênuos podem fornecer bons resultados em muitos conjuntos de dados reais.

    Vantagens do CategoricalNB:

    • Eficiência: O CategoricalNB é computacionalmente eficiente e escala bem para grandes conjuntos de dados. Ele requer uma quantidade mínima de memória e pode fornecer previsões rapidamente.
    • Interpretabilidade: A natureza probabilística do CategoricalNB facilita sua interpretação. Ele pode oferecer insights sobre quais características influenciam a previsão.
    • Processamento de dados categóricos: O CategoricalNB é especificamente projetado para conjuntos de dados com características categóricas. Ele pode processar dados textuais e outros tipos de características discretas de forma eficiente.
    • Desempenho base: Frequentemente serve como um modelo de base forte para tarefas de classificação de texto e pode superar algoritmos mais complexos em conjuntos de dados menores.

    Limitações do CategoricalNB:

    • Suposição de independência: A suposição de independência entre as características pode não ser válida em todos os conjuntos de dados. Se as características forem fortemente dependentes, o desempenho do CategoricalNB pode diminuir.
    • Sensibilidade ao escalonamento das características: O CategoricalNB não requer escalonamento das características, pois trabalha com dados categóricos. No entanto, em alguns casos, a normalização ou codificação de características categóricos de diferentes maneiras pode afetar seu desempenho.
    • Expressividade limitada: O CategoricalNB pode não capturar relações complexas nos dados tão bem quanto algoritmos mais complexos, como modelos de deep learning.
    • Tratamento de dados ausentes: Assume-se que não há valores ausentes no conjunto de dados, e os valores ausentes precisam ser tratados previamente.

    O CategoricalNB é um algoritmo de classificação valioso, especialmente adequado para conjuntos de dados com características categóricas. Sua simplicidade, eficiência e interpretabilidade o tornam uma ferramenta útil para várias tarefas de classificação. Apesar das limitações, como a suposição de independência, ele permanece uma escolha popular para classificação de texto e outras tarefas onde predominam dados discretos. Ao trabalhar com dados categóricos, considerar o CategoricalNB como um modelo de base é frequentemente uma escolha razoável. No entanto, é importante avaliar seu desempenho em comparação com modelos mais complexos, especialmente se houver dependências entre as características.


    2.25.1. Código para criar o modelo CategoricalNB Classifier

    Este código demonstra o processo de treinamento do modelo CategoricalNB Classifier no conjunto de dados Iris, exportação para o formato ONNX e realização de classificação usando o modelo ONNX. Também avalia a precisão tanto do modelo original quanto do modelo 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)
    

    Resultado:

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


    2.25.2. Código no MQL5 para trabalhar com o modelo CategoricalNB Classifier

    //+------------------------------------------------------------------+
    //|                                 Iris_CategoricalNBClassifier.mq5 |
    //|                                  Copyright 2023, MetaQuotes Ltd. |
    //|                                             https://www.mql5.com |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2023, MetaQuotes Ltd."
    #property link      "https://www.mql5.com"
    #property version   "1.00"
    
    #include "iris.mqh"
    #resource "categorical_nb_iris.onnx" as const uchar ExtModel[];
    
    //+------------------------------------------------------------------+
    //| Test IRIS dataset samples                                        |
    //+------------------------------------------------------------------+
    bool TestSamples(long model,float &input_data[][4], int &model_classes_id[])
      {
    //--- check number of input samples
       ulong batch_size=input_data.Range(0);
       if(batch_size==0)
          return(false);
    //--- prepare output array
       ArrayResize(model_classes_id,(int)batch_size);
    //---
       float output_data[];
    //---
       struct Map
         {
          ulong          key[];
          float          value[];
         } output_data_map[];
    //--- check consistency
       bool res=ArrayResize(output_data,(int)batch_size)==batch_size;
    //---
       if(res)
         {
          //--- set input shape
          ulong input_shape[]= {batch_size,input_data.Range(1)};
          OnnxSetInputShape(model,0,input_shape);
          //--- set output shapeы
          ulong output_shape1[]= {batch_size};
          ulong output_shape2[]= {batch_size};
          OnnxSetOutputShape(model,0,output_shape1);
          OnnxSetOutputShape(model,1,output_shape2);
          //--- run the model
          res=OnnxRun(model,0,input_data,output_data,output_data_map);
          //--- postprocessing
          if(res)
            {
             //--- postprocessing of sequence map data
             //--- find class with maximum probability
             ulong output_keys[];
             float output_values[];
             //---
             for(uint n=0; n<output_data_map.Size(); n++)
               {
                int model_class_id=-1;
                int max_idx=-1;
                float max_value=-1;
                //--- copy to arrays
                ArrayCopy(output_keys,output_data_map[n].key);
                ArrayCopy(output_values,output_data_map[n].value);
                //ArrayPrint(output_keys);
                //ArrayPrint(output_values);
                //--- find the key with maximum probability
                for(int k=0; k<ArraySize(output_values); k++)
                  {
                   if(k==0)
                     {
                      max_idx=0;
                      max_value=output_values[max_idx];
                      model_class_id=(int)output_keys[max_idx];
                     }
                   else
                     {
                      if(output_values[k]>max_value)
                        {
                         max_idx=k;
                         max_value=output_values[max_idx];
                         model_class_id=(int)output_keys[max_idx];
                        }
                     }
                  }
                //--- store the result to the output array
                model_classes_id[n]=model_class_id;
                //Print("model_class_id=",model_class_id);
               }
            }
         }
    //---
       return(res);
      }
    
    //+------------------------------------------------------------------+
    //| Test all samples from IRIS dataset (150)                         |
    //| Here we test all samples with batch=1, sample by sample          |
    //+------------------------------------------------------------------+
    bool TestAllIrisDataset(const long model,const string model_name,double &model_accuracy)
      {
       sIRISsample iris_samples[];
    //--- load dataset from file
       PrepareIrisDataset(iris_samples);
    //--- test
       int total_samples=ArraySize(iris_samples);
       if(total_samples==0)
         {
          Print("iris dataset not prepared");
          return(false);
         }
    //--- show dataset
       for(int k=0; k<total_samples; k++)
         {
          //PrintFormat("%d (%.2f,%.2f,%.2f,%.2f) class %d (%s)",iris_samples[k].sample_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3],iris_samples[k].class_id,iris_samples[k].class_name);
         }
    //--- array for output classes
       int model_output_classes_id[];
    //--- check all Iris dataset samples
       int correct_results=0;
       for(int k=0; k<total_samples; k++)
         {
          //--- input array
          float iris_sample_input_data[1][4];
          //--- prepare input data from kth iris sample dataset
          iris_sample_input_data[0][0]=(float)iris_samples[k].features[0];
          iris_sample_input_data[0][1]=(float)iris_samples[k].features[1];
          iris_sample_input_data[0][2]=(float)iris_samples[k].features[2];
          iris_sample_input_data[0][3]=(float)iris_samples[k].features[3];
          //--- run model
          bool res=TestSamples(model,iris_sample_input_data,model_output_classes_id);
          //--- check result
          if(res)
            {
             if(model_output_classes_id[0]==iris_samples[k].class_id)
               {
                correct_results++;
               }
             else
               {
                PrintFormat("model:%s  sample=%d FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f]",model_name,iris_samples[k].sample_id,model_output_classes_id[0],iris_samples[k].class_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3]);
               }
            }
         }
       model_accuracy=1.0*correct_results/total_samples;
    //---
       PrintFormat("model:%s   correct results: %.2f%%",model_name,100*model_accuracy);
    //---
       return(true);
      }
    
    //+------------------------------------------------------------------+
    //| Here we test batch execution of the model                        |
    //+------------------------------------------------------------------+
    bool TestBatchExecution(const long model,const string model_name,double &model_accuracy)
      {
       model_accuracy=0;
    //--- array for output classes
       int model_output_classes_id[];
       int correct_results=0;
       int total_results=0;
       bool res=false;
    
    //--- run batch with 3 samples
       float input_data_batch3[3][4]=
         {
            {5.1f,3.5f,1.4f,0.2f}, // iris dataset sample id=1, Iris-setosa
            {6.3f,2.5f,4.9f,1.5f}, // iris dataset sample id=73, Iris-versicolor
            {6.3f,2.7f,4.9f,1.8f}  // iris dataset sample id=124, Iris-virginica
         };
       int correct_classes_batch3[3]= {0,1,2};
    //--- run model
       res=TestSamples(model,input_data_batch3,model_output_classes_id);
       if(res)
         {
          //--- check result
          for(int j=0; j<ArraySize(model_output_classes_id); j++)
            {
             //--- check result
             if(model_output_classes_id[j]==correct_classes_batch3[j])
                correct_results++;
             else
               {
                PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch3[j],input_data_batch3[j][0],input_data_batch3[j][1],input_data_batch3[j][2],input_data_batch3[j][3]);
               }
             total_results++;
            }
         }
       else
          return(false);
    
    //--- run batch with 10 samples
       float input_data_batch10[10][4]=
         {
            {5.5f,3.5f,1.3f,0.2f}, // iris dataset sample id=37 (Iris-setosa)
            {4.9f,3.1f,1.5f,0.1f}, // iris dataset sample id=38 (Iris-setosa)
            {4.4f,3.0f,1.3f,0.2f}, // iris dataset sample id=39 (Iris-setosa)
            {5.0f,3.3f,1.4f,0.2f}, // iris dataset sample id=50 (Iris-setosa)
            {7.0f,3.2f,4.7f,1.4f}, // iris dataset sample id=51 (Iris-versicolor)
            {6.4f,3.2f,4.5f,1.5f}, // iris dataset sample id=52 (Iris-versicolor)
            {6.3f,3.3f,6.0f,2.5f}, // iris dataset sample id=101 (Iris-virginica)
            {5.8f,2.7f,5.1f,1.9f}, // iris dataset sample id=102 (Iris-virginica)
            {7.1f,3.0f,5.9f,2.1f}, // iris dataset sample id=103 (Iris-virginica)
            {6.3f,2.9f,5.6f,1.8f}  // iris dataset sample id=104 (Iris-virginica)
         };
    //--- correct classes for all 10 samples in the batch
       int correct_classes_batch10[10]= {0,0,0,0,1,1,2,2,2,2};
    
    //--- run model
       res=TestSamples(model,input_data_batch10,model_output_classes_id);
    //--- check result
       if(res)
         {
          for(int j=0; j<ArraySize(model_output_classes_id); j++)
            {
             if(model_output_classes_id[j]==correct_classes_batch10[j])
                correct_results++;
             else
               {
                double f1=input_data_batch10[j][0];
                double f2=input_data_batch10[j][1];
                double f3=input_data_batch10[j][2];
                double f4=input_data_batch10[j][3];
                PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch10[j],input_data_batch10[j][0],input_data_batch10[j][1],input_data_batch10[j][2],input_data_batch10[j][3]);
               }
             total_results++;
            }
         }
       else
          return(false);
    
    //--- calculate accuracy
       model_accuracy=correct_results/total_results;
    //---
       return(res);
      }
    //+------------------------------------------------------------------+
    //| Script program start function                                    |
    //+------------------------------------------------------------------+
    int OnStart(void)
      {
       string model_name="CategoricalNBClassifier";
    //---
       long model=OnnxCreateFromBuffer(ExtModel,ONNX_DEFAULT);
       if(model==INVALID_HANDLE)
         {
          PrintFormat("model_name=%s OnnxCreate error %d for",model_name,GetLastError());
         }
       else
         {
          //--- test all dataset
          double model_accuracy=0;
          //-- test sample by sample execution for all Iris dataset
          if(TestAllIrisDataset(model,model_name,model_accuracy))
             PrintFormat("model=%s all samples accuracy=%f",model_name,model_accuracy);
          else
             PrintFormat("error in testing model=%s ",model_name);
          //--- test batch execution for several samples
          if(TestBatchExecution(model,model_name,model_accuracy))
             PrintFormat("model=%s batch test accuracy=%f",model_name,model_accuracy);
          else
             PrintFormat("error in testing model=%s ",model_name);
          //--- release model
          OnnxRelease(model);
         }
       return(0);
      }
    //+------------------------------------------------------------------+
    
    Resultado:
    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
    

    A precisão do modelo ONNX exportado no iris dataset completo é de 93,33%, o que corresponde à precisão do original.


    2.25.3. Representação ONNX do modelo CategoricalNB Classifier

    Fig.39. Representação ONNX do modelo CategoricalNB Classifier em Netron

    Fig.39. Representação ONNX do modelo CategoricalNB Classifier em Netron



    Nota sobre os modelos ExtraTreeClassifier e ExtraTreesClassifier:

    ExtraTreeClassifier e ExtraTreesClassifier são dois classificadores distintos, e a principal diferença entre eles está na forma como operam:

    ExtraTreeClassifier (Extremely Randomized Trees Classifier):

    • Este classificador também é conhecido como Extremely Randomized Trees ou Extra-Trees.
    • Baseia-se na ideia de árvores de decisão aleatórias.
    • No ExtraTreeClassifier, a escolha do ponto de divisão para cada nó da árvore é feita de maneira aleatória, sem qualquer busca prévia pelo melhor ponto de divisão.
    • Isso torna o classificador menos computacionalmente intensivo do que o Random Forest clássico, pois não requer o cálculo de divisões ótimas para cada nó.
    • No ExtraTreeClassifier, frequentemente são usados limites aleatórios para as características e divisões aleatórias, o que resulta em árvores mais aleatórias.
    • A falta de busca pelas melhores divisões torna o ExtraTreeClassifier mais rápido, mas menos preciso em comparação com o Random Forest.
    ExtraTreesClassifier (Extremely Randomized Trees Classifier):
    • O ExtraTreesClassifier também é um classificador baseado no método de Extremely Randomized Trees.
    • A principal diferença entre o ExtraTreesClassifier e o ExtraTreeClassifier é que o ExtraTreesClassifier realiza divisões aleatórias para selecionar as melhores divisões em cada nó da árvore.
    • Isso significa que o ExtraTreesClassifier aplica um Random Forest com um nível adicional de aleatoriedade na seleção das divisões ótimas.
    • O ExtraTreesClassifier geralmente é mais preciso do que o ExtraTreeClassifier porque realiza divisões aleatórias para encontrar os melhores características para divisão.
    • No entanto, o ExtraTreesClassifier pode ser mais computacionalmente intensivo devido à necessidade de uma busca mais ampla pelas divisões ótimas.
    Assim, a principal diferença entre esses dois classificadores está no nível de aleatoriedade na escolha das divisões. O ExtraTreeClassifier faz uma seleção aleatória para cada nó sem uma busca prévia pelas melhores divisões, enquanto o ExtraTreesClassifier realiza divisões aleatórias com a busca das melhores divisões para cada nó.


    2.26. ExtraTreeClassifier

    O ExtraTreeClassifier, ou Extremely Randomized Trees, é um algoritmo poderoso de aprendizado de máquina usado em tarefas de classificação e regressão. Este algoritmo baseia-se na ideia de árvores de decisão e oferece melhorias em comparação com as florestas aleatórias tradicionais e árvores de decisão.

    Princípios do funcionamento do ExtraTreeClassifier:

    1. Divisão aleatória de nós: O princípio fundamental do ExtraTreeClassifier é que ele escolhe divisões aleatoriamente para cada nó da árvore. Isso difere das árvores de decisão tradicionais, que selecionam o melhor recurso para divisão. O ExtraTreeClassifier faz a divisão sem considerar a melhor divisão, tornando-o mais aleatório e resistente ao sobreajuste.
    2. Agregação dos resultados: Ao construir um ensemble, o ExtraTreeClassifier cria múltiplas árvores aleatórias e agrega seus resultados. Isso é feito para aumentar a capacidade de generalização do modelo e reduzir a variância. O ensemble de árvores ajuda a combater o problema do sobreajuste e aumenta a estabilidade das previsões.
    3. Limites aleatórios: Ao dividir um nó, o ExtraTreeClassifier escolhe limites aleatórios para cada recurso, em vez de valores ótimos definidos. Isso leva a uma maior aleatoriedade e robustez do modelo.

    Vantagens do ExtraTreeClassifier:

    • Resistência ao sobreajuste: Devido à divisão aleatória e à falta de seleção das melhores divisões, o ExtraTreeClassifier geralmente é menos propenso ao sobreajuste em comparação com as árvores de decisão convencionais.
    • Alta velocidade de treinamento: O ExtraTreeClassifier requer menos recursos computacionais para treinamento do que muitos outros algoritmos, como o Random Forest. Isso o torna rápido e eficiente em tarefas com grandes volumes de dados.
    • Versatilidade: O ExtraTreeClassifier pode ser usado tanto para tarefas de classificação quanto de regressão. Isso o torna um algoritmo versátil para diferentes tipos de tarefas.

    Limitações do ExtraTreeClassifier:

    • Aleatoriedade: O uso de divisões aleatórias pode levar a modelos menos precisos em alguns casos. É importante escolher as configurações do algoritmo cuidadosamente.
    • Sensibilidade a valores atípicos: O ExtraTreeClassifier pode ser sensível a valores atípicos nos dados, já que constrói divisões aleatórias. Isso pode levar a previsões instáveis em alguns casos.
    • Menor interpretabilidade: Comparado com árvores de decisão convencionais, o ExtraTreeClassifier é menos interpretável e mais difícil de explicar.

    O ExtraTreeClassifier é um algoritmo poderoso de aprendizado de máquina que possui resistência ao sobreajuste e alta velocidade de treinamento. Ele pode ser útil em várias tarefas de classificação e regressão, especialmente quando os recursos computacionais são limitados. No entanto, é importante considerar o caráter aleatório deste algoritmo e suas limitações, como a sensibilidade a valores atípicos e menor interpretabilidade. Ao usar o ExtraTreeClassifier, é crucial ajustar cuidadosamente seus parâmetros e considerar as características dos dados.


    2.26.1. Código para criar o modelo ExtraTreeClassifier

    Este código demonstra o processo de treinamento do modelo ExtraTreeClassifier no conjunto de dados Iris, exportação para o formato ONNX e realização de classificação usando o modelo ONNX. Também avalia a precisão tanto do modelo original quanto do modelo 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)
    

    Resultado:

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


    2.26.2. Código no MQL5 para trabalhar com o modelo ExtraTreeClassifier

    //+------------------------------------------------------------------+
    //|                                     Iris_ExtraTreeClassifier.mq5 |
    //|                                  Copyright 2023, MetaQuotes Ltd. |
    //|                                             https://www.mql5.com |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2023, MetaQuotes Ltd."
    #property link      "https://www.mql5.com"
    #property version   "1.00"
    
    #include "iris.mqh"
    #resource "extra_tree_iris.onnx" as const uchar ExtModel[];
    
    //+------------------------------------------------------------------+
    //| Test IRIS dataset samples                                        |
    //+------------------------------------------------------------------+
    bool TestSamples(long model,float &input_data[][4], int &model_classes_id[])
      {
    //--- check number of input samples
       ulong batch_size=input_data.Range(0);
       if(batch_size==0)
          return(false);
    //--- prepare output array
       ArrayResize(model_classes_id,(int)batch_size);
    //---
       float output_data[];
    //---
       struct Map
         {
          ulong          key[];
          float          value[];
         } output_data_map[];
    //--- check consistency
       bool res=ArrayResize(output_data,(int)batch_size)==batch_size;
    //---
       if(res)
         {
          //--- set input shape
          ulong input_shape[]= {batch_size,input_data.Range(1)};
          OnnxSetInputShape(model,0,input_shape);
          //--- set output shapeы
          ulong output_shape1[]= {batch_size};
          ulong output_shape2[]= {batch_size};
          OnnxSetOutputShape(model,0,output_shape1);
          OnnxSetOutputShape(model,1,output_shape2);
          //--- run the model
          res=OnnxRun(model,0,input_data,output_data,output_data_map);
          //--- postprocessing
          if(res)
            {
             //--- postprocessing of sequence map data
             //--- find class with maximum probability
             ulong output_keys[];
             float output_values[];
             //---
             for(uint n=0; n<output_data_map.Size(); n++)
               {
                int model_class_id=-1;
                int max_idx=-1;
                float max_value=-1;
                //--- copy to arrays
                ArrayCopy(output_keys,output_data_map[n].key);
                ArrayCopy(output_values,output_data_map[n].value);
                //ArrayPrint(output_keys);
                //ArrayPrint(output_values);
                //--- find the key with maximum probability
                for(int k=0; k<ArraySize(output_values); k++)
                  {
                   if(k==0)
                     {
                      max_idx=0;
                      max_value=output_values[max_idx];
                      model_class_id=(int)output_keys[max_idx];
                     }
                   else
                     {
                      if(output_values[k]>max_value)
                        {
                         max_idx=k;
                         max_value=output_values[max_idx];
                         model_class_id=(int)output_keys[max_idx];
                        }
                     }
                  }
                //--- store the result to the output array
                model_classes_id[n]=model_class_id;
                //Print("model_class_id=",model_class_id);
               }
            }
         }
    //---
       return(res);
      }
    
    //+------------------------------------------------------------------+
    //| Test all samples from IRIS dataset (150)                         |
    //| Here we test all samples with batch=1, sample by sample          |
    //+------------------------------------------------------------------+
    bool TestAllIrisDataset(const long model,const string model_name,double &model_accuracy)
      {
       sIRISsample iris_samples[];
    //--- load dataset from file
       PrepareIrisDataset(iris_samples);
    //--- test
       int total_samples=ArraySize(iris_samples);
       if(total_samples==0)
         {
          Print("iris dataset not prepared");
          return(false);
         }
    //--- show dataset
       for(int k=0; k<total_samples; k++)
         {
          //PrintFormat("%d (%.2f,%.2f,%.2f,%.2f) class %d (%s)",iris_samples[k].sample_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3],iris_samples[k].class_id,iris_samples[k].class_name);
         }
    //--- array for output classes
       int model_output_classes_id[];
    //--- check all Iris dataset samples
       int correct_results=0;
       for(int k=0; k<total_samples; k++)
         {
          //--- input array
          float iris_sample_input_data[1][4];
          //--- prepare input data from kth iris sample dataset
          iris_sample_input_data[0][0]=(float)iris_samples[k].features[0];
          iris_sample_input_data[0][1]=(float)iris_samples[k].features[1];
          iris_sample_input_data[0][2]=(float)iris_samples[k].features[2];
          iris_sample_input_data[0][3]=(float)iris_samples[k].features[3];
          //--- run model
          bool res=TestSamples(model,iris_sample_input_data,model_output_classes_id);
          //--- check result
          if(res)
            {
             if(model_output_classes_id[0]==iris_samples[k].class_id)
               {
                correct_results++;
               }
             else
               {
                PrintFormat("model:%s  sample=%d FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f]",model_name,iris_samples[k].sample_id,model_output_classes_id[0],iris_samples[k].class_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3]);
               }
            }
         }
       model_accuracy=1.0*correct_results/total_samples;
    //---
       PrintFormat("model:%s   correct results: %.2f%%",model_name,100*model_accuracy);
    //---
       return(true);
      }
    
    //+------------------------------------------------------------------+
    //| Here we test batch execution of the model                        |
    //+------------------------------------------------------------------+
    bool TestBatchExecution(const long model,const string model_name,double &model_accuracy)
      {
       model_accuracy=0;
    //--- array for output classes
       int model_output_classes_id[];
       int correct_results=0;
       int total_results=0;
       bool res=false;
    
    //--- run batch with 3 samples
       float input_data_batch3[3][4]=
         {
            {5.1f,3.5f,1.4f,0.2f}, // iris dataset sample id=1, Iris-setosa
            {6.3f,2.5f,4.9f,1.5f}, // iris dataset sample id=73, Iris-versicolor
            {6.3f,2.7f,4.9f,1.8f}  // iris dataset sample id=124, Iris-virginica
         };
       int correct_classes_batch3[3]= {0,1,2};
    //--- run model
       res=TestSamples(model,input_data_batch3,model_output_classes_id);
       if(res)
         {
          //--- check result
          for(int j=0; j<ArraySize(model_output_classes_id); j++)
            {
             //--- check result
             if(model_output_classes_id[j]==correct_classes_batch3[j])
                correct_results++;
             else
               {
                PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch3[j],input_data_batch3[j][0],input_data_batch3[j][1],input_data_batch3[j][2],input_data_batch3[j][3]);
               }
             total_results++;
            }
         }
       else
          return(false);
    
    //--- run batch with 10 samples
       float input_data_batch10[10][4]=
         {
            {5.5f,3.5f,1.3f,0.2f}, // iris dataset sample id=37 (Iris-setosa)
            {4.9f,3.1f,1.5f,0.1f}, // iris dataset sample id=38 (Iris-setosa)
            {4.4f,3.0f,1.3f,0.2f}, // iris dataset sample id=39 (Iris-setosa)
            {5.0f,3.3f,1.4f,0.2f}, // iris dataset sample id=50 (Iris-setosa)
            {7.0f,3.2f,4.7f,1.4f}, // iris dataset sample id=51 (Iris-versicolor)
            {6.4f,3.2f,4.5f,1.5f}, // iris dataset sample id=52 (Iris-versicolor)
            {6.3f,3.3f,6.0f,2.5f}, // iris dataset sample id=101 (Iris-virginica)
            {5.8f,2.7f,5.1f,1.9f}, // iris dataset sample id=102 (Iris-virginica)
            {7.1f,3.0f,5.9f,2.1f}, // iris dataset sample id=103 (Iris-virginica)
            {6.3f,2.9f,5.6f,1.8f}  // iris dataset sample id=104 (Iris-virginica)
         };
    //--- correct classes for all 10 samples in the batch
       int correct_classes_batch10[10]= {0,0,0,0,1,1,2,2,2,2};
    
    //--- run model
       res=TestSamples(model,input_data_batch10,model_output_classes_id);
    //--- check result
       if(res)
         {
          for(int j=0; j<ArraySize(model_output_classes_id); j++)
            {
             if(model_output_classes_id[j]==correct_classes_batch10[j])
                correct_results++;
             else
               {
                double f1=input_data_batch10[j][0];
                double f2=input_data_batch10[j][1];
                double f3=input_data_batch10[j][2];
                double f4=input_data_batch10[j][3];
                PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch10[j],input_data_batch10[j][0],input_data_batch10[j][1],input_data_batch10[j][2],input_data_batch10[j][3]);
               }
             total_results++;
            }
         }
       else
          return(false);
    
    //--- calculate accuracy
       model_accuracy=correct_results/total_results;
    //---
       return(res);
      }
    //+------------------------------------------------------------------+
    //| Script program start function                                    |
    //+------------------------------------------------------------------+
    int OnStart(void)
      {
       string model_name="ExtraTreeClassifier";
    //---
       long model=OnnxCreateFromBuffer(ExtModel,ONNX_DEFAULT);
       if(model==INVALID_HANDLE)
         {
          PrintFormat("model_name=%s OnnxCreate error %d for",model_name,GetLastError());
         }
       else
         {
          //--- test all dataset
          double model_accuracy=0;
          //-- test sample by sample execution for all Iris dataset
          if(TestAllIrisDataset(model,model_name,model_accuracy))
             PrintFormat("model=%s all samples accuracy=%f",model_name,model_accuracy);
          else
             PrintFormat("error in testing model=%s ",model_name);
          //--- test batch execution for several samples
          if(TestBatchExecution(model,model_name,model_accuracy))
             PrintFormat("model=%s batch test accuracy=%f",model_name,model_accuracy);
          else
             PrintFormat("error in testing model=%s ",model_name);
          //--- release model
          OnnxRelease(model);
         }
       return(0);
      }
    //+------------------------------------------------------------------+
    

    Resultado:

    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
    

    A precisão do modelo ONNX exportado no conjunto de dados completo de íris é de 100%, o que corresponde à precisão do original.


    2.26.3. Representação ONNX do modelo ExtraTreeClassifier

    Fig.40. Representação ONNX do modelo ExtraTreeClassifier em Netron

    Fig.40. Representação ONNX do modelo ExtraTreeClassifier em Netron


    2.27. ExtraTreesClassifier

    O ExtraTreesClassifier é um algoritmo poderoso de aprendizado de máquina usado para tarefas de classificação. Este algoritmo é uma extensão e melhoria do Random Forest (floresta aleatória) e oferece uma série de vantagens e desvantagens.

    Princípios do funcionamento do ExtraTreesClassifier:

    1. Bootstrap de amostras: Semelhante ao Random Forest, o ExtraTreesClassifier usa o método bootstrap para criar um conjunto de subamostras do conjunto de dados de treinamento. Isso significa que para cada árvore, uma subamostra aleatória com substituição é criada a partir dos dados originais.
    2. Divisões aleatórias: Ao contrário do Random Forest, onde a melhor característica é selecionado para cada nó da árvore, o ExtraTreesClassifier usa características aleatórias e limiares aleatórios para a divisão dos nós. Isso torna as árvores mais aleatórias e reduz o sobreajuste.
    3. Votação: Após a construção do conjunto de árvores, cada árvore vota na classe de um objeto. O resultado é que a classe com o maior número de votos se torna a classe prevista.

    Vantagens do ExtraTreesClassifier:

    1. Redução de sobreajuste: O uso de divisões e características aleatórias torna o ExtraTreesClassifier menos suscetível ao sobreajuste em comparação com as árvores de decisão tradicionais.
    2. Alta velocidade de treinamento: O ExtraTreesClassifier requer menos recursos computacionais para treinamento em comparação com alguns outros algoritmos, como o gradient boosting.
    3. Resistência a valores atípicos: Graças ao uso de um ensemble de árvores e divisões aleatórias, o ExtraTreesClassifier geralmente é mais resistente a valores atípicos nos dados.

    Limitações do ExtraTreesClassifier:

    • Dificuldade de interpretação: A análise e interpretação do modelo ExtraTreesClassifier podem ser desafiadoras devido ao grande número de divisões e características aleatórias.
    • Parâmetros para ajuste: Apesar de sua eficiência, o ExtraTreesClassifier pode exigir um ajuste cuidadoso dos hiperparâmetros para alcançar o desempenho ótimo.
    • Nem sempre o melhor resultado: Em algumas tarefas, o ExtraTreesClassifier pode ser menos preciso do que outros algoritmos, como o gradient boosting.

    O ExtraTreesClassifier é um algoritmo de classificação poderoso, com resistência ao sobreajuste, alta velocidade de treinamento e robustez a valores atípicos. Ele pode ser uma ferramenta útil na análise de dados e na resolução de tarefas de classificação, especialmente em casos com grandes volumes de dados e a necessidade de uma solução eficiente. No entanto, é importante notar que o algoritmo nem sempre é a melhor escolha, e sua eficácia pode depender da tarefa específica e dos dados.


    2.27.1. Código para criar o modelo ExtraTreesClassifier

    Este código demonstra o processo de treinamento do modelo ExtraTreesClassifier no conjunto de dados Iris, exportação para o formato ONNX e realização de classificação usando o modelo ONNX. Também avalia a precisão tanto do modelo original quanto do modelo 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)
    

    Resultado:

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


    2.27.2. Código no MQL5 para trabalhar com o modelo ExtraTreesClassifier

    //+------------------------------------------------------------------+
    //|                                    Iris_ExtraTreesClassifier.mq5 |
    //|                                  Copyright 2023, MetaQuotes Ltd. |
    //|                                             https://www.mql5.com |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2023, MetaQuotes Ltd."
    #property link      "https://www.mql5.com"
    #property version   "1.00"
    
    #include "iris.mqh"
    #resource "extra_trees_iris.onnx" as const uchar ExtModel[];
    
    //+------------------------------------------------------------------+
    //| Test IRIS dataset samples                                        |
    //+------------------------------------------------------------------+
    bool TestSamples(long model,float &input_data[][4], int &model_classes_id[])
      {
    //--- check number of input samples
       ulong batch_size=input_data.Range(0);
       if(batch_size==0)
          return(false);
    //--- prepare output array
       ArrayResize(model_classes_id,(int)batch_size);
    //---
       float output_data[];
    //---
       struct Map
         {
          ulong          key[];
          float          value[];
         } output_data_map[];
    //--- check consistency
       bool res=ArrayResize(output_data,(int)batch_size)==batch_size;
    //---
       if(res)
         {
          //--- set input shape
          ulong input_shape[]= {batch_size,input_data.Range(1)};
          OnnxSetInputShape(model,0,input_shape);
          //--- set output shapeы
          ulong output_shape1[]= {batch_size};
          ulong output_shape2[]= {batch_size};
          OnnxSetOutputShape(model,0,output_shape1);
          OnnxSetOutputShape(model,1,output_shape2);
          //--- run the model
          res=OnnxRun(model,0,input_data,output_data,output_data_map);
          //--- postprocessing
          if(res)
            {
             //--- postprocessing of sequence map data
             //--- find class with maximum probability
             ulong output_keys[];
             float output_values[];
             //---
             for(uint n=0; n<output_data_map.Size(); n++)
               {
                int model_class_id=-1;
                int max_idx=-1;
                float max_value=-1;
                //--- copy to arrays
                ArrayCopy(output_keys,output_data_map[n].key);
                ArrayCopy(output_values,output_data_map[n].value);
                //ArrayPrint(output_keys);
                //ArrayPrint(output_values);
                //--- find the key with maximum probability
                for(int k=0; k<ArraySize(output_values); k++)
                  {
                   if(k==0)
                     {
                      max_idx=0;
                      max_value=output_values[max_idx];
                      model_class_id=(int)output_keys[max_idx];
                     }
                   else
                     {
                      if(output_values[k]>max_value)
                        {
                         max_idx=k;
                         max_value=output_values[max_idx];
                         model_class_id=(int)output_keys[max_idx];
                        }
                     }
                  }
                //--- store the result to the output array
                model_classes_id[n]=model_class_id;
                //Print("model_class_id=",model_class_id);
               }
            }
         }
    //---
       return(res);
      }
    
    //+------------------------------------------------------------------+
    //| Test all samples from IRIS dataset (150)                         |
    //| Here we test all samples with batch=1, sample by sample          |
    //+------------------------------------------------------------------+
    bool TestAllIrisDataset(const long model,const string model_name,double &model_accuracy)
      {
       sIRISsample iris_samples[];
    //--- load dataset from file
       PrepareIrisDataset(iris_samples);
    //--- test
       int total_samples=ArraySize(iris_samples);
       if(total_samples==0)
         {
          Print("iris dataset not prepared");
          return(false);
         }
    //--- show dataset
       for(int k=0; k<total_samples; k++)
         {
          //PrintFormat("%d (%.2f,%.2f,%.2f,%.2f) class %d (%s)",iris_samples[k].sample_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3],iris_samples[k].class_id,iris_samples[k].class_name);
         }
    //--- array for output classes
       int model_output_classes_id[];
    //--- check all Iris dataset samples
       int correct_results=0;
       for(int k=0; k<total_samples; k++)
         {
          //--- input array
          float iris_sample_input_data[1][4];
          //--- prepare input data from kth iris sample dataset
          iris_sample_input_data[0][0]=(float)iris_samples[k].features[0];
          iris_sample_input_data[0][1]=(float)iris_samples[k].features[1];
          iris_sample_input_data[0][2]=(float)iris_samples[k].features[2];
          iris_sample_input_data[0][3]=(float)iris_samples[k].features[3];
          //--- run model
          bool res=TestSamples(model,iris_sample_input_data,model_output_classes_id);
          //--- check result
          if(res)
            {
             if(model_output_classes_id[0]==iris_samples[k].class_id)
               {
                correct_results++;
               }
             else
               {
                PrintFormat("model:%s  sample=%d FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f]",model_name,iris_samples[k].sample_id,model_output_classes_id[0],iris_samples[k].class_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3]);
               }
            }
         }
       model_accuracy=1.0*correct_results/total_samples;
    //---
       PrintFormat("model:%s   correct results: %.2f%%",model_name,100*model_accuracy);
    //---
       return(true);
      }
    
    //+------------------------------------------------------------------+
    //| Here we test batch execution of the model                        |
    //+------------------------------------------------------------------+
    bool TestBatchExecution(const long model,const string model_name,double &model_accuracy)
      {
       model_accuracy=0;
    //--- array for output classes
       int model_output_classes_id[];
       int correct_results=0;
       int total_results=0;
       bool res=false;
    
    //--- run batch with 3 samples
       float input_data_batch3[3][4]=
         {
            {5.1f,3.5f,1.4f,0.2f}, // iris dataset sample id=1, Iris-setosa
            {6.3f,2.5f,4.9f,1.5f}, // iris dataset sample id=73, Iris-versicolor
            {6.3f,2.7f,4.9f,1.8f}  // iris dataset sample id=124, Iris-virginica
         };
       int correct_classes_batch3[3]= {0,1,2};
    //--- run model
       res=TestSamples(model,input_data_batch3,model_output_classes_id);
       if(res)
         {
          //--- check result
          for(int j=0; j<ArraySize(model_output_classes_id); j++)
            {
             //--- check result
             if(model_output_classes_id[j]==correct_classes_batch3[j])
                correct_results++;
             else
               {
                PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch3[j],input_data_batch3[j][0],input_data_batch3[j][1],input_data_batch3[j][2],input_data_batch3[j][3]);
               }
             total_results++;
            }
         }
       else
          return(false);
    
    //--- run batch with 10 samples
       float input_data_batch10[10][4]=
         {
            {5.5f,3.5f,1.3f,0.2f}, // iris dataset sample id=37 (Iris-setosa)
            {4.9f,3.1f,1.5f,0.1f}, // iris dataset sample id=38 (Iris-setosa)
            {4.4f,3.0f,1.3f,0.2f}, // iris dataset sample id=39 (Iris-setosa)
            {5.0f,3.3f,1.4f,0.2f}, // iris dataset sample id=50 (Iris-setosa)
            {7.0f,3.2f,4.7f,1.4f}, // iris dataset sample id=51 (Iris-versicolor)
            {6.4f,3.2f,4.5f,1.5f}, // iris dataset sample id=52 (Iris-versicolor)
            {6.3f,3.3f,6.0f,2.5f}, // iris dataset sample id=101 (Iris-virginica)
            {5.8f,2.7f,5.1f,1.9f}, // iris dataset sample id=102 (Iris-virginica)
            {7.1f,3.0f,5.9f,2.1f}, // iris dataset sample id=103 (Iris-virginica)
            {6.3f,2.9f,5.6f,1.8f}  // iris dataset sample id=104 (Iris-virginica)
         };
    //--- correct classes for all 10 samples in the batch
       int correct_classes_batch10[10]= {0,0,0,0,1,1,2,2,2,2};
    
    //--- run model
       res=TestSamples(model,input_data_batch10,model_output_classes_id);
    //--- check result
       if(res)
         {
          for(int j=0; j<ArraySize(model_output_classes_id); j++)
            {
             if(model_output_classes_id[j]==correct_classes_batch10[j])
                correct_results++;
             else
               {
                double f1=input_data_batch10[j][0];
                double f2=input_data_batch10[j][1];
                double f3=input_data_batch10[j][2];
                double f4=input_data_batch10[j][3];
                PrintFormat("model:%s  FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f)",model_name,model_output_classes_id[j],correct_classes_batch10[j],input_data_batch10[j][0],input_data_batch10[j][1],input_data_batch10[j][2],input_data_batch10[j][3]);
               }
             total_results++;
            }
         }
       else
          return(false);
    
    //--- calculate accuracy
       model_accuracy=correct_results/total_results;
    //---
       return(res);
      }
    //+------------------------------------------------------------------+
    //| Script program start function                                    |
    //+------------------------------------------------------------------+
    int OnStart(void)
      {
       string model_name="ExtraTreesClassifier";
    //---
       long model=OnnxCreateFromBuffer(ExtModel,ONNX_DEFAULT);
       if(model==INVALID_HANDLE)
         {
          PrintFormat("model_name=%s OnnxCreate error %d for",model_name,GetLastError());
         }
       else
         {
          //--- test all dataset
          double model_accuracy=0;
          //-- test sample by sample execution for all Iris dataset
          if(TestAllIrisDataset(model,model_name,model_accuracy))
             PrintFormat("model=%s all samples accuracy=%f",model_name,model_accuracy);
          else
             PrintFormat("error in testing model=%s ",model_name);
          //--- test batch execution for several samples
          if(TestBatchExecution(model,model_name,model_accuracy))
             PrintFormat("model=%s batch test accuracy=%f",model_name,model_accuracy);
          else
             PrintFormat("error in testing model=%s ",model_name);
          //--- release model
          OnnxRelease(model);
         }
       return(0);
      }
    //+------------------------------------------------------------------+
    

    Resultado:

    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
    

    A precisão do modelo ONNX exportado no conjunto de dados completo de íris é de 100%, o que corresponde à precisão do original.


    2.27.3. Representação ONNX do modelo ExtraTreesClassifier

    Fig.41. Representação ONNX do modelo ExtraTrees Classifier em Netron

    Fig.41. Representação ONNX do modelo ExtraTrees Classifier em Netron


    2.28. Comparação da precisão de todos os modelos

    Vamos agora considerar todos os modelos juntos e comparar a qualidade de seu desempenho. Primeiro, faremos a comparação usando Python, depois carregaremos e executaremos os modelos ONNX salvos no MetaTrader 5.

    2.28.1. Código para calcular todos os modelos e construir o diagrama de comparação de precisão

    O script calcula 27 modelos de classificação do pacote Scikit-learn no conjunto de dados completo de íris de Fisher, exporta os modelos para o formato ONNX, executa-os e compara a precisão dos modelos originais e das versões 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()
    

    Resultado:

    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
    
    O script também apresentará uma imagem com os resultados resumidos para todos os 27 modelos.

    Fig. 42. Comparação da precisão de 27 modelos de classificação e suas versões ONNX para o conjunto de dados Iris

    Fig. 42. Comparação da precisão de 27 modelos de classificação e suas versões ONNX para o conjunto de dados Iris



    Com base nos resultados da avaliação de precisão (accuracy) dos modelos originais e suas versões ONNX, podemos fazer as seguintes conclusões:

    Sete modelos mostraram precisão perfeita (1.0000) tanto na versão original quanto na versão ONNX. Esses modelos incluem:

    1. Random Forest Classifier
    2. Gradient Boosting Classifier
    3. Bagging Classifier
    4. Decision Tree Classifier
    5. Extra Tree Classifier
    6. Extra Trees Classifier
    7. Hist Gradient Boosting Classifier

    As representações ONNX desses modelos também mantêm alta precisão.

    Três modelos - Logistic RegressionCV Classifier, MLP Classifier e Linear Discriminant Analysis Classifier - alcançaram alta precisão nas versões original e ONNX com uma precisão de 0.9800. Esses são modelos que funcionam bem em ambas as representações.

    Vários modelos, incluindo o classificador SVC, o classificador Radius Neighbors, o classificador NuSVC, o classificador K-NN, o classificador LinearSVC, o classificador AdaBoost, o classificador Passive-Aggressive, o classificador Gaussian Naive Bayes e o classificador Multinomial Naive Bayes, apresentaram boa precisão nas versões original e ONNX, com uma precisão de 0,9733, 0,9667 ou 0,9600. Esses modelos também mantêm sua precisão na representação ONNX.

    Modelos como SGD Classifier, Categorical Naive Bayes Classifier, Ridge Classifier, Complement Naive Bayes Classifier, perceptron Classifier e Bernoulli Naive Bayes Classifier têm precisão mais baixa. Eles também lidam bem em manter a precisão em ONNX.

    Todos os modelos considerados mantêm sua precisão ao serem exportados para o formato ONNX, indicando que o ONNX oferece uma maneira eficaz de preservar e recuperar modelos de aprendizado de máquina. No entanto, é importante lembrar que a qualidade do modelo exportado pode depender do algoritmo específico e dos parâmetros do modelo.


    2.28.2. Código no MQL5 para executar todos os modelos ONNX

    O script executa todos os modelos ONNX salvos pelo script 2.28.1 no conjunto de dados completo de íris de Fisher.

    //+------------------------------------------------------------------+
    //|                                          Iris_AllClassifiers.mq5 |
    //|                                  Copyright 2023, MetaQuotes Ltd. |
    //|                                             https://www.mql5.com |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2023, MetaQuotes Ltd."
    #property link      "https://www.mql5.com"
    #property version   "1.00"
    
    #include "iris.mqh"
    
    //+------------------------------------------------------------------+
    //| TestSampleSequenceMapOutput                                      |
    //+------------------------------------------------------------------+
    bool TestSampleSequenceMapOutput(long model,sIRISsample &iris_sample, int &model_class_id)
      {
    //---
       model_class_id=-1;
       float input_data[1][4];
       for(int k=0; k<4; k++)
         {
          input_data[0][k]=(float)iris_sample.features[k];
         }
    //---
       float out1[];
    //---
       struct Map
         {
          ulong          key[];
          float          value[];
         } out2[];
    //---
       bool res=ArrayResize(out1,input_data.Range(0))==input_data.Range(0);
    //---
       if(res)
         {
          ulong input_shape[]= { input_data.Range(0), input_data.Range(1) };
          ulong output_shape[]= { input_data.Range(0) };
          //---
          OnnxSetInputShape(model,0,input_shape);
          OnnxSetOutputShape(model,0,output_shape);
          //---
          res=OnnxRun(model,0,input_data,out1,out2);
          //---
          if(res)
            {
             //--- postprocessing of sequence map data
             //--- find class with maximum probability
             ulong output_keys[];
             float output_values[];
             //---
             model_class_id=-1;
             int max_idx=-1;
             float max_value=-1;
             //---
             for(uint n=0; n<out2.Size(); n++)
               {
                //--- copy to arrays
                ArrayCopy(output_keys,out2[n].key);
                ArrayCopy(output_values,out2[n].value);
                //--- find the key with maximum probability
                for(int k=0; k<ArraySize(output_values); k++)
                  {
                   if(k==0)
                     {
                      max_idx=0;
                      max_value=output_values[max_idx];
                      model_class_id=(int)output_keys[max_idx];
                     }
                   else
                     {
                      if(output_values[k]>max_value)
                        {
                         max_idx=k;
                         max_value=output_values[max_idx];
                         model_class_id=(int)output_keys[max_idx];
                        }
                     }
                  }
               }
            }
         }
    //---
       return(res);
      }
    
    //+------------------------------------------------------------------+
    //| TestSampleTensorOutput                                           |
    //+------------------------------------------------------------------+
    bool TestSampleTensorOutput(long model,sIRISsample &iris_sample, int &model_class_id)
      {
    //---
       model_class_id=-1;
       float input_data[1][4];
       for(int k=0; k<4; k++)
         {
          input_data[0][k]=(float)iris_sample.features[k];
         }
    //---
       ulong input_shape[]= { 1, 4};
       OnnxSetInputShape(model,0,input_shape);
    //---
       int output1[1];
       float output2[1,3];
    //---
       ulong output_shape[]= {1};
       OnnxSetOutputShape(model,0,output_shape);
    //---
       ulong output_shape2[]= {1,3};
       OnnxSetOutputShape(model,1,output_shape2);
    //---
       bool res=OnnxRun(model,0,input_data,output1,output2);
    //--- result for these models in output1[0];
       if(res)
          model_class_id=output1[0];
    //---
       return(res);
      }
    
    //+------------------------------------------------------------------+
    //| Script program start function                                    |
    //+------------------------------------------------------------------+
    int OnStart(void)
      {
       sIRISsample iris_samples[];
    //--- load dataset from file
       PrepareIrisDataset(iris_samples);
    //--- test
       int total_samples=ArraySize(iris_samples);
       if(total_samples==0)
         {
          Print("error in loading iris dataset from iris.csv");
          return(false);
         }
       /*for(int k=0; k<total_samples; k++)
         {
          PrintFormat("%d (%.2f,%.2f,%.2f,%.2f) class %d (%s)",iris_samples[k].sample_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3],iris_samples[k].class_id,iris_samples[k].class_name);
         }*/
    //----
    
       string iris_models[]=
         {
          "random_forest_classifier_iris.onnx",
          "gradient_boosting_classifier_iris.onnx",
          "bagging_classifier_iris.onnx",
          "decision_tree_classifier_iris.onnx",
          "extra_tree_classifier_iris.onnx",
          "extra_trees_classifier_iris.onnx",
          "hist_gradient_boosting_classifier_iris.onnx",
          "logistic_regressioncv_classifier_iris.onnx",
          "mlp_classifier_iris.onnx",
          "linear_discriminant_analysis_classifier_iris.onnx",
          "svc_classifier_iris.onnx",
          "radius_neighbors_classifier_iris.onnx",
          "logistic_regression_classifier_iris.onnx",
          "nusvc_classifier_iris.onnx",
          "k-nn_classifier_iris.onnx",
          "linearsvc_classifier_iris.onnx",
          "adaboost_classifier_iris.onnx",
          "passive-aggressive_classifier_iris.onnx",
          "gaussian_naive_bayes_classifier_iris.onnx",
          "multinomial_naive_bayes_classifier_iris.onnx",
          "sgd_classifier_iris.onnx",
          "categorical_naive_bayes_classifier_iris.onnx",
          "ridge_classifier_iris.onnx",
          "ridge_classifiercv_iris.onnx",
          "complement_naive_bayes_classifier_iris.onnx",
          "perceptron_classifier_iris.onnx",
          "bernoulli_naive_bayes_classifier_iris.onnx"
         };
    
    //--- test all iris dataset sample by sample
       for(int i=0; i<ArraySize(iris_models); i++)
         {
          //--- load ONNX-model
          string model_name="IRIS_models\\"+iris_models[i];
          //---
          long model=OnnxCreate(model_name,0);
          if(model==INVALID_HANDLE)
            {
             PrintFormat("model_name=%s OnnxCreate error %d for",model_name,GetLastError());
            }
          else
            {
             //--- check all samples
             int correct_results=0;
             for(int k=0; k<total_samples; k++)
               {
                int model_class_id=-1;
                //--- select data output processor
                string current_model=iris_models[i];
                if(current_model=="svc_classifier_iris.onnx" || current_model=="linearsvc_classifier_iris.onnx" || current_model=="nusvc_classifier_iris.onnx" || current_model=="ridge_classifier_iris.onnx" || current_model=="ridge_classifiercv_iris.onnx" || current_model=="radius_neighbors_classifier_iris.onnx")
                  {
                   TestSampleTensorOutput(model,iris_samples[k],model_class_id);
                  }
                else
                  {
                   TestSampleSequenceMapOutput(model,iris_samples[k],model_class_id);
                  }
                //---
                if(model_class_id==iris_samples[k].class_id)
                  {
                   correct_results++;
                   //PrintFormat("sample=%d OK [class=%d]",iris_samples[k].sample_id,model_class_id);
                  }
                else
                  {
                   //PrintFormat("model:%s  sample=%d FAILED [class=%d, true class=%d] features=(%.2f,%.2f,%.2f,%.2f]",model_name,iris_samples[k].sample_id,model_class_id,iris_samples[k].class_id,iris_samples[k].features[0],iris_samples[k].features[1],iris_samples[k].features[2],iris_samples[k].features[3]);
                  }
               }
             PrintFormat("%d model:%s   accuracy: %.4f",i+1,model_name,1.0*correct_results/total_samples);
             //--- release model
             OnnxRelease(model);
            }
          //---
         }
       return(0);
      }
    //+------------------------------------------------------------------+

    Resultado:

    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
    

    Comparando com os resultados do script 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

    notamos que a execução de todos os modelos ONNX salvos no MQL5 corresponde totalmente aos resultados 2.28.1.

    Assim, os modelos que examinamos, convertidos para o formato ONNX, mantiveram a precisão de desempenho.

    É digno de nota a precisão perfeita de classificação (accuracy=1.0) para o conjunto de dados Iris em 7 modelos:

    1. Random Forest Classifier;
    2. Gradient Boosting Classifier;
    3. Bagging Classifier;
    4. Decision Tree Classifier;
    5. Extra Tree Classifier;
    6. Extra Trees Classifier;
    7. Histogram Gradient Boosting Classifier.

    Os outros 20 modelos tiveram erros de classificação.

    Se descomentar a linha 208, o script também mostrará amostras do conjunto de dados Iris que foram classificadas incorretamente por cada um dos modelos:

    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. Modelos de classificação do Scikit-Learn que não puderam ser convertidos para ONNX

    Algumas modelos de classificação não puderam ser convertidos para o formato ONNX devido a erros na função convert_sklearn.


    2.29.1. DummyClassifier

    O DummyClassifier é um classificador na biblioteca Scikit-learn, que é usado como um modelo básico simples para tarefas de classificação. É destinado a testes e avaliação do desempenho de outros modelos de classificação mais complexos.

    Como funciona:

    O DummyClassifier opera de maneira muito simples - ele faz previsões aleatórias ou ingênuas, sem levar em conta os dados de entrada. As possíveis estratégias (a estratégia é selecionada através do parâmetro strategy):

    1. most_frequent (Classe mais frequente): Esta estratégia sempre prevê a classe que aparece com mais frequência no conjunto de dados de treinamento. Isso pode ser útil em situações onde as classes estão desbalanceadas e a classe dominante precisa ser prevista.
    2. stratified (Estratificado): Essa estratégia tenta fazer previsões que correspondam à distribuição de classes no conjunto de dados de treinamento. Ela usa um palpite aleatório, mas leva em conta as proporções das classes.
    3. uniform (Distribuição uniforme): Essa estratégia faz previsões aleatórias com igual probabilidade para cada classe. É útil se as classes estão equilibradas e você quer testar como seu modelo se comporta, em média.

    Possibilidades:

    • Simplicidade: O DummyClassifier é útil para verificar quão rapidamente uma linha de base pode ser treinada e qual será o seu desempenho. Pode ser útil para uma avaliação rápida do desempenho de outros classificadores.
    • Uso no pipeline: Você pode usar o DummyClassifier como um modelo de base em um pipeline juntamente com outras transformações e modelos para comparação e teste.

    Limitações:

    • Não considera os dados: O DummyClassifier fará previsões aleatórias ou ingênuas, sem considerar os dados reais. Ele não é capaz de aprender com os dados ou identificar padrões.
    • Não é adequado para tarefas complexas: Este classificador não é destinado a resolver tarefas complexas de classificação e geralmente não oferece bons resultados em tarefas com grandes volumes de dados e padrões complexos.
    • Não informativo: Os resultados obtidos com o DummyClassifier podem ser não informativos e não fornecer informações úteis sobre o desempenho do modelo. Eles são mais úteis para testar e avaliar o código.

    Em geral, o DummyClassifier é uma ferramenta útil para testes iniciais e avaliação de modelos de classificação, mas seu uso é limitado em tarefas complexas e não pode substituir algoritmos de classificação mais avançados.

    2.29.1.1. Código de criação do modelo DummyClassifier

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

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

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

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

    # create a DummyClassifier model with the strategy "most_frequent"
    dummy_classifier = DummyClassifier(strategy="most_frequent")

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    Resultado:

    Python    Accuracy of DummyClassifier model: 0.3333333333333333
    Python    
    Python    Classification Report:
    Python                   precision    recall  f1-score   support
    Python    
    Python               0       0.33      1.00      0.50        50
    Python               1       0.00      0.00      0.00        50
    Python               2       0.00      0.00      0.00        50
    Python    
    Python        accuracy                           0.33       150
    Python       macro avg       0.11      0.33      0.17       150
    Python    weighted avg       0.11      0.33      0.17       150
    Python   

    A modelo foi construído e executado no Scikit-learn, mas erros ocorreram durante a conversão para ONNX.

    Na aba Errors, são exibidas mensagens de erro sobre a conversão do modelo para o formato ONNX:

        onnx_model = convert_sklearn(dummy_classifier, initial_types=initial_type, target_opset=12)    Iris_DummyClassifier.py    44    1
        onnx_model = convert_topology(    convert.py    208    1
        topology.convert_operators(container=container, verbose=verbose)    _topology.py    1532    1
        self.call_shape_calculator(operator)    _topology.py    1348    1
        operator.infer_types()    _topology.py    1163    1
        raise MissingShapeCalculator(    _topology.py    629    1
    skl2onnx.common.exceptions.MissingShapeCalculator: Unable to find a shape calculator for type '<class 'sklearn.dummy.DummyClassifier'>'.    _topology.py    629    1
    It usually means the pipeline being converted contains a    _topology.py    629    1
    transformer or a predictor with no corresponding converter    _topology.py    629    1
    implemented in sklearn-onnx. If the converted is implemented    _topology.py    629    1
    in another library, you need to register    _topology.py    629    1
    the converted so that it can be used by sklearn-onnx (function    _topology.py    629    1
    update_registered_converter). If the model is not yet covered    _topology.py    629    1
    by sklearn-onnx, you may raise an issue to    _topology.py    629    1
    https://github.com/onnx/sklearn-onnx/issues    _topology.py    629    1
    to get the converter implemented or even contribute to the    _topology.py    629    1
    project. If the model is a custom model, a new converter must    _topology.py    629    1
    be implemented. Examples can be found in the gallery.    _topology.py    629    1
    Iris_DummyClassifier.py finished in 2071 ms        19    1

    Assim, o modelo DummyClassifier não pôde ser convertido para ONNX.


    2.29.2. GaussianProcessClassifier

    O GaussianProcessClassifier é um classificador que utiliza um processo gaussiano para tarefas de classificação. Ele pertence à família de modelos que usam processos gaussianos e pode ser útil em tarefas onde uma estimativa probabilística das classes é necessária.

    Como funciona:

    1. O GaussianProcessClassifier usa um processo gaussiano para modelar o mapeamento do espaço de características para o espaço de estimativas probabilísticas de classes.
    2. Ele constrói um modelo probabilístico para cada classe, estimando a probabilidade de um ponto pertencer a cada classe.
    3. Na classificação, ele escolhe a classe com a maior probabilidade para um determinado ponto.

    Possibilidades:

    • Classificação probabilística: O GaussianProcessClassifier fornece estimativas probabilísticas das classes, o que pode ser útil, por exemplo, para avaliar a incerteza do modelo.
    • Adaptabilidade: Este classificador é capaz de se adaptar aos dados e atualizar suas previsões com base em novas observações.
    • Calibração: O modelo pode ser calibrado usando o método calibrate para melhorar as estimativas probabilísticas.

    Limitações:

    • Complexidade computacional: O GaussianProcessClassifier pode ser computacionalmente custoso para grandes volumes de dados e/ou alta dimensionalidade do espaço de características.
    • Não adequado para grandes amostras: Devido à complexidade computacional, este classificador pode ser ineficiente para treinar em grandes amostras.
    • Dificuldade de interpretação: Os processos gaussianos podem ser complexos para interpretação e compreensão, especialmente para usuários sem experiência em estatística bayesiana.

    O GaussianProcessClassifier é útil em tarefas onde a estimativa probabilística de classes é importante e quando os custos computacionais são gerenciáveis. Caso contrário, para tarefas de classificação em grandes dados ou com estruturas de dados simples, podem ser mais apropriados algoritmos de classificação.

    2.29.2.1. Código de criação do modelo GaussianProcessClassifier

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

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

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

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

    # create a GaussianProcessClassifier model with an RBF kernel
    kernel = 1.0 * RBF(1.0)
    gpc_model = GaussianProcessClassifier(kernel=kernel)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    Resultado:

    Python    Accuracy of GaussianProcessClassifier model: 0.9866666666666667
    Python    
    Python    Classification Report:
    Python                   precision    recall  f1-score   support
    Python    
    Python               0       1.00      1.00      1.00        50
    Python               1       0.98      0.98      0.98        50
    Python               2       0.98      0.98      0.98        50
    Python    
    Python        accuracy                           0.99       150
    Python       macro avg       0.99      0.99      0.99       150
    Python    weighted avg       0.99      0.99      0.99       150
    Python   

    Erros na aba Errors:

        onnx_model = convert_sklearn(gpc_model, initial_types=initial_type, target_opset=12)    Iris_GaussianProcessClassifier.py    46    1
        onnx_model = convert_topology(    convert.py    208    1
        topology.convert_operators(container=container, verbose=verbose)    _topology.py    1532    1
        self.call_converter(operator, container, verbose=verbose)    _topology.py    1349    1
        conv(self.scopes[0], operator, container)    _topology.py    1132    1
        return self._fct(*args)    _registration.py    27    1
        raise NotImplementedError("Only binary classification is iplemented.")    gaussian_process.py    247    1
    NotImplementedError: Only binary classification is iplemented.    gaussian_process.py    247    1
    Iris_GaussianProcessClassifier.py finished in 4004 ms        9    1

    Assim, também não foi possível converter o modelo GaussianProcessClassifier para ONNX.


    2.29.3. LabelPropagation Classifier

    LabelPropagation é um método de aprendizado semi-supervisionado usado para tarefas de classificação. A ideia principal deste método é propagar rótulos (classes) de objetos rotulados para objetos não rotulados em uma estrutura de dados em grafo.

    Processo de trabalho do LabelPropagation:

    1. Começa com a construção de um grafo, onde os nós representam objetos de dados e as arestas entre nós refletem a semelhança ou proximidade entre os objetos.
    2. Colocação inicial dos rótulos: Objetos rotulados recebem seus rótulos, enquanto objetos não rotulados começam com algum rótulo indefinido.
    3. Os rótulos são propagados pelo grafo: Os rótulos de objetos rotulados são propagados para objetos não rotulados, considerando a semelhança entre os objetos. Essa semelhança pode ser definida de várias maneiras, por exemplo, com base nos vizinhos mais próximos no grafo.
    4. Processo iterativo: Os rótulos podem mudar ao longo de várias iterações, onde cada iteração atualiza os rótulos em objetos não rotulados com base nos rótulos atuais e na semelhança entre os objetos.
    5. Estabilização: O processo continua até que os rótulos se estabilizem ou seja atendido um certo critério de parada.

    Vantagens do LabelPropagation:

    • Usa informações de dados não rotulados: LabelPropagation permite usar informações entre objetos não rotulados para melhorar a qualidade da classificação. Isso é especialmente útil quando há dados rotulados insuficientes.
    • Resistência ao ruído: O método lida bem com dados que contêm ruído, porque considera a semelhança entre objetos e não depende apenas da rotulação.

    Limitações do LabelPropagation:

    • Dependência da escolha do grafo: A qualidade da classificação pelo LabelPropagation pode depender significativamente da escolha do grafo e da maneira como a semelhança entre os objetos é definida. Uma escolha incorreta de parâmetros pode levar a resultados ruins.
    • Complexidade computacional: Dependendo do tamanho e complexidade dos dados, assim como dos parâmetros do método, o LabelPropagation pode exigir grandes recursos computacionais.
    • Possibilidade de sobreajuste: Se o grafo contiver muitas arestas ruidosas ou rotulações incorretas, o método pode sofrer de sobreajuste.
    • Não sempre converge: Em casos raros, o LabelPropagation pode não convergir para rótulos estáveis, o que pode exigir limitação no número de iterações ou ajustes de outras configurações.

    LabelPropagation é um método poderoso, mas requer um ajuste cuidadoso dos parâmetros e análise da estrutura de grafo dos dados para alcançar bons resultados.

    2.29.3.1. Código de criação do modelo LabelPropagationClassifier

    # Iris_LabelPropagationClassifier.py

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

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

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

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

    # create a LabelPropagation model
    lp_model = LabelPropagation()

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    Resultado:

    Python    Accuracy of LabelPropagation model: 1.0
    Python    
    Python    Classification Report:
    Python                   precision    recall  f1-score   support
    Python    
    Python               0       1.00      1.00      1.00        50
    Python               1       1.00      1.00      1.00        50
    Python               2       1.00      1.00      1.00        50
    Python    
    Python        accuracy                           1.00       150
    Python       macro avg       1.00      1.00      1.00       150
    Python    weighted avg       1.00      1.00      1.00       150
    Python   

    O modelo foi construído, porém, ao converter para o formato ONNX, ocorreram erros.

    Erros na aba Errors:

        onnx_model = convert_sklearn(lp_model, initial_types=initial_type, target_opset=12)    Iris_LabelPropagation.py    44    1
        onnx_model = convert_topology(    convert.py    208    1
        topology.convert_operators(container=container, verbose=verbose)    _topology.py    1532    1
        self.call_shape_calculator(operator)    _topology.py    1348    1
        operator.infer_types()    _topology.py    1163    1
        raise MissingShapeCalculator(    _topology.py    629    1
    skl2onnx.common.exceptions.MissingShapeCalculator: Unable to find a shape calculator for type '<class 'sklearn.semi_supervised._label_propagation.LabelPropagation'>'.    _topology.py    629    1
    It usually means the pipeline being converted contains a    _topology.py    629    1
    transformer or a predictor with no corresponding converter    _topology.py    629    1
    implemented in sklearn-onnx. If the converted is implemented    _topology.py    629    1
    in another library, you need to register    _topology.py    629    1
    the converted so that it can be used by sklearn-onnx (function    _topology.py    629    1
    update_registered_converter). If the model is not yet covered    _topology.py    629    1
    by sklearn-onnx, you may raise an issue to    _topology.py    629    1
    https://github.com/onnx/sklearn-onnx/issues    _topology.py    629    1
    to get the converter implemented or even contribute to the    _topology.py    629    1
    project. If the model is a custom model, a new converter must    _topology.py    629    1
    be implemented. Examples can be found in the gallery.    _topology.py    629    1
    Iris_LabelPropagation.py finished in 2064 ms        19    1

    Assim, não foi possível converter o modelo LabelPropagation Classifier para ONNX.


    2.29.4. LabelSpreading Classifier

    LabelSpreading é um método de aprendizado semi-supervisionado usado para tarefas de classificação. Ele se baseia na ideia de propagar rótulos (classes) de objetos rotulados para objetos não rotulados em uma estrutura de dados em grafo, similar ao LabelPropagation. No entanto, o LabelSpreading inclui estabilização e regularização adicionais do processo de propagação de rótulos.

    Processo de trabalho do LabelSpreading:

    1. Começa com a construção de um grafo, onde os nós representam objetos de dados e as arestas entre nós refletem a semelhança ou proximidade entre os objetos.
    2. Colocação inicial dos rótulos: Objetos rotulados recebem seus rótulos, enquanto objetos não rotulados começam com algum rótulo indefinido.
    3. Os rótulos são propagados pelo grafo: Os rótulos de objetos rotulados são propagados para objetos não rotulados, considerando a semelhança entre os objetos.
    4. Regularização e Estabilização: O LabelSpreading inclui uma regularização, que ajuda a estabilizar o processo de propagação de rótulos e reduzir o sobreajuste. Isso é alcançado levando em consideração não apenas a semelhança entre objetos, mas também a discrepância entre os rótulos dos objetos vizinhos.
    5. Processo iterativo: Os rótulos podem mudar ao longo de várias iterações, onde cada iteração atualiza os rótulos em objetos não rotulados com base nos rótulos atuais e na regularização.
    6. Estabilização: O processo continua até que os rótulos se estabilizem ou seja atendido um certo critério de parada.

    Vantagens do LabelSpreading:

    • Usa informações de dados não rotulados: O LabelSpreading permite usar informações entre objetos não rotulados para melhorar a qualidade da classificação.
    • Regularização: A presença de regularização no LabelSpreading ajuda a reduzir o sobreajuste e torna o processo de propagação de rótulos mais estável.

    Limitações do LabelSpreading:

    • Dependência da escolha do grafo: Assim como no LabelPropagation, a qualidade da classificação pelo LabelSpreading pode depender significativamente da escolha do grafo e dos parâmetros do método.
    • Complexidade computacional: Dependendo do tamanho e complexidade dos dados, bem como dos parâmetros do método, o LabelSpreading pode exigir grandes recursos computacionais.
    • Não sempre converge: Em casos raros, o LabelSpreading pode não convergir para rótulos estáveis, o que pode exigir limitação no número de iterações ou ajustes de outras configurações.

    O LabelSpreading é um método que também requer um ajuste cuidadoso e pode ser uma ferramenta poderosa para o uso de dados não rotulados em tarefas de classificação.

    2.29.4.1. Código de criação do modelo LabelSpreadingClassifier

    # Iris_LabelSpreadingClassifier.py
    # The code demonstrates the process of training LabelSpreading Classifier model on the Iris dataset, exporting it to ONNX format, and making predictions using the ONNX model.
    # It also evaluates the accuracy of both the original model and the ONNX model.
    # Copyright 2023, MetaQuotes Ltd.


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

    # get the script path
    script_path = sys.argv[0]
    last_index = script_path.rfind("\\") + 1
    data_path = script_path[0:last_index]

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

    # create a LabelSpreading model
    ls_model = LabelSpreading()

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    Resultado:

    Python    Accuracy of LabelSpreading model: 1.0
    Python    
    Python    Classification Report:
    Python                   precision    recall  f1-score   support
    Python    
    Python               0       1.00      1.00      1.00        50
    Python               1       1.00      1.00      1.00        50
    Python               2       1.00      1.00      1.00        50
    Python    
    Python        accuracy                           1.00       150
    Python       macro avg       1.00      1.00      1.00       150
    Python    weighted avg       1.00      1.00      1.00       150
    Python   

    Na aba Errors, são exibidas informações sobre erros de conversão para ONNX.

        onnx_model = convert_sklearn(ls_model, initial_types=initial_type, target_opset=12)    Iris_LabelSpreading.py    45    1
        onnx_model = convert_topology(    convert.py    208    1
        topology.convert_operators(container=container, verbose=verbose)    _topology.py    1532    1
        self.call_shape_calculator(operator)    _topology.py    1348    1
        operator.infer_types()    _topology.py    1163    1
        raise MissingShapeCalculator(    _topology.py    629    1
    skl2onnx.common.exceptions.MissingShapeCalculator: Unable to find a shape calculator for type '<class 'sklearn.semi_supervised._label_propagation.LabelSpreading'>'.    _topology.py    629    1
    It usually means the pipeline being converted contains a    _topology.py    629    1
    transformer or a predictor with no corresponding converter    _topology.py    629    1
    implemented in sklearn-onnx. If the converted is implemented    _topology.py    629    1
    in another library, you need to register    _topology.py    629    1
    the converted so that it can be used by sklearn-onnx (function    _topology.py    629    1
    update_registered_converter). If the model is not yet covered    _topology.py    629    1
    by sklearn-onnx, you may raise an issue to    _topology.py    629    1
    https://github.com/onnx/sklearn-onnx/issues    _topology.py    629    1
    to get the converter implemented or even contribute to the    _topology.py    629    1
    project. If the model is a custom model, a new converter must    _topology.py    629    1
    be implemented. Examples can be found in the gallery.    _topology.py    629    1
    Iris_LabelSpreading.py finished in 2032 ms        19    1

    Não foi possível converter o modelo LabelPropagation Classifier para ONNX.


    2.29.5. NearestCentroid Classifier

    O NearestCentroid é um método de classificação baseado na ideia de determinar o centroide para cada classe e classificar os objetos com base no centroide mais próximo. Este método é adequado para tarefas com múltiplas classes e funciona bem em conjuntos de dados com classes linearmente separáveis.

    Processo de trabalho do NearestCentroid:

    1. Para cada classe, calcula-se o centroide, que representa o valor médio das características de todos os objetos pertencentes a essa classe. Isso pode ser feito calculando a média de cada característica para os objetos de uma dada classe.
    2. Na classificação de um novo objeto, calcula-se o seu centroide mais próximo entre os centroides de todas as classes.
    3. O novo objeto é atribuído à classe cujo centroide está mais próximo dele no espaço métrico.

    Vantagens do NearestCentroid:

    • Simplicidade e velocidade: O NearestCentroid é um método de baixa complexidade computacional e opera rapidamente em grandes conjuntos de dados.
    • Adequado para classes linearmente separáveis: O método é eficaz em tarefas onde as classes são linearmente separáveis ou próximas disso.
    • Eficiência em tarefas multiclasse: O NearestCentroid é adequado para tarefas com múltiplas classes e pode ser usado como um classificador base em ensembles.

    Limitações do NearestCentroid:

    • Sensibilidade a valores atípicos: O método NearestCentroid é sensível a valores atípicos nos dados, pois o centroide pode ser significativamente distorcido pela presença de valores atípicos.
    • Deslocamento espacial: Se as classes nos dados têm diferentes dispersões e formas, o método NearestCentroid pode ser menos eficaz.
    • Necessidade de suposição sobre as médias: O método pressupõe que as classes têm aproximadamente os mesmos valores médios das características, o que pode não ser sempre o caso nos dados reais.
    • Inadequado para tarefas não lineares: O NearestCentroid não é adequado para tarefas com fronteiras de decisão não lineares entre as classes.

    O NearestCentroid é um método de classificação simples e interpretável que pode ser útil em certos cenários, especialmente quando as classes são linearmente separáveis e não há valores atípicos nos dados.

    2.29.5.1.Código de criação do modelo NearestCentroid

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

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

    # get the script path
    script_path = sys.argv[0]
    last_index = script_path.rfind("\\") + 1
    data_path = script_path[0:last_index]

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

    # create a NearestCentroid model
    nc_model = NearestCentroid()

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    Resultado:

    Python    Accuracy of NearestCentroid model: 0.9266666666666666
    Python    
    Python    Classification Report:
    Python                   precision    recall  f1-score   support
    Python    
    Python               0       1.00      1.00      1.00        50
    Python               1       0.87      0.92      0.89        50
    Python               2       0.91      0.86      0.89        50
    Python    
    Python        accuracy                           0.93       150
    Python       macro avg       0.93      0.93      0.93       150
    Python    weighted avg       0.93      0.93      0.93       150
    Python   

    Erros na aba Errors:

        onnx_model = convert_sklearn(nc_model, initial_types=initial_type, target_opset=12)    Iris_NearestCentroid.py    45    1
        onnx_model = convert_topology(    convert.py    208    1
        topology.convert_operators(container=container, verbose=verbose)    _topology.py    1532    1
        self.call_shape_calculator(operator)    _topology.py    1348    1
        operator.infer_types()    _topology.py    1163    1
        raise MissingShapeCalculator(    _topology.py    629    1
    skl2onnx.common.exceptions.MissingShapeCalculator: Unable to find a shape calculator for type '<class 'sklearn.neighbors._nearest_centroid.NearestCentroid'>'.    _topology.py    629    1
    It usually means the pipeline being converted contains a    _topology.py    629    1
    transformer or a predictor with no corresponding converter    _topology.py    629    1
    implemented in sklearn-onnx. If the converted is implemented    _topology.py    629    1
    in another library, you need to register    _topology.py    629    1
    the converted so that it can be used by sklearn-onnx (function    _topology.py    629    1
    update_registered_converter). If the model is not yet covered    _topology.py    629    1
    by sklearn-onnx, you may raise an issue to    _topology.py    629    1
    https://github.com/onnx/sklearn-onnx/issues    _topology.py    629    1
    to get the converter implemented or even contribute to the    _topology.py    629    1
    project. If the model is a custom model, a new converter must    _topology.py    629    1
    be implemented. Examples can be found in the gallery.    _topology.py    629    1
    Iris_NearestCentroid.py finished in 2131 ms        19    1
    Também não foi possível converter o modelo NearestCentroid Classifier para ONNX.


    2.29.6. Quadratic Discriminant Analysis Classifier

    A Análise Discriminante Quadrática (QDA) é um método de classificação que utiliza um modelo probabilístico para separar dados em classes. É uma generalização da análise discriminante linear (LDA) e permite considerar a covariância das características dentro de cada classe. A ideia principal do QDA é modelar a distribuição das características para cada classe e, em seguida, usar essa distribuição para classificar novos objetos.

    Processo de trabalho do QDA:

    1. Para cada classe, são calculados parâmetros de distribuição, como a média e a matriz de covariância das características. Esses parâmetros são estimados com base nos dados de treinamento para cada classe.
    2. Com os parâmetros obtidos, pode-se calcular as densidades de probabilidade para cada classe usando a distribuição normal multivariada (ou função de distribuição quadrática).
    3. Na classificação de um novo objeto, são calculados os valores de densidade de probabilidade para cada classe, e o objeto é atribuído à classe com a maior probabilidade.

    Vantagens da Análise Discriminante Quadrática (QDA):

    • Considera a covariância das características: O QDA é mais flexível que o LDA, pois permite matrizes de covariância diferentes para diferentes classes, tornando-o mais adaptável a diversas estruturas de dados.
    • Adequado para limites de decisão não lineares: O QDA é capaz de modelar limites de decisão complexos e não lineares entre classes.
    • Resistência a dados desbalanceados: O QDA pode lidar bem com tarefas onde as classes estão desbalanceadas.

    Limitações da Análise Discriminante Quadrática (QDA):

    • Complexidade computacional: O QDA requer a estimativa de parâmetros para cada classe, incluindo matrizes de covariância, o que pode ser computacionalmente custoso em grandes conjuntos de dados.
    • Poucos dados: O QDA pode ser menos eficaz quando há poucos dados, e a estimativa de parâmetros se torna menos precisa.
    • Suposição de distribuição normal: O QDA supõe que os dados seguem uma distribuição normal, o que pode não ser verdadeiro para alguns tipos de dados.
    • Possibilidade de sobreajuste: Com uma quantidade insuficiente de dados de treinamento ou quando há uma forte covariância entre as características, o QDA pode enfrentar problemas de sobreajuste.

    A Análise Discriminante Quadrática (QDA) é um método de classificação poderoso, adequado para vários tipos de dados e capaz de considerar a covariância das características dentro das classes. No entanto, também possui limitações que devem ser consideradas ao utilizá-lo.

    2.29.6.1.Código de criação do modelo Quadratic Discriminant Analysis

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

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

    # get the script path
    script_path = sys.argv[0]
    last_index = script_path.rfind("\\") + 1
    data_path = script_path[0:last_index]

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

    # create a QuadraticDiscriminantAnalysis model
    qda_model = QuadraticDiscriminantAnalysis()

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    Resultado:

    Python    Accuracy of Quadratic Discriminant Analysis model: 0.98
    Python    
    Python    Classification Report:
    Python                   precision    recall  f1-score   support
    Python    
    Python               0       1.00      1.00      1.00        50
    Python               1       0.98      0.96      0.97        50
    Python               2       0.96      0.98      0.97        50
    Python    
    Python        accuracy                           0.98       150
    Python       macro avg       0.98      0.98      0.98       150
    Python    weighted avg       0.98      0.98      0.98       150
    Python    
    Python    Model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\qda_iris.onnx

    Desta vez, o modelo foi salvo em ONNX, mas ao executá-lo na aba Errors, erros são exibidos:

        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:00 INVALID_GRAPH : Load model from C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\qda_iris.onnx failed:This is an invalid mode    onnxruntime_inference_collection.py    424    1
    Iris_QuadraticDiscriminantAnalysisClassifier.py finished in 2063 ms        5    1

    A conversão do modelo Análise Discriminante Quadrática Classifier para ONNX ocorreu com erro.


    Considerações finais

    Um estudo de 33 modelos de classificação disponíveis na biblioteca Scikit-learn versão 1.2.2 foi realizado usando o conjunto de dados Iris.

    1. Deste conjunto, 6 modelos enfrentaram dificuldades na conversão para o formato ONNX:

    1. DummyClassifier: classificador dummy (Dummy Classifier);
    2. GaussianProcessClassifier:  classificador de processos gaussianos (Gaussian Process Classifier);
    3. LabelPropagation : classificador de propagação de rótulos (Label Propagation Classifier);
    4. LabelSpreading : classificador de disseminação de rótulos (Label Spreading Classifier);
    5. NearestCentroid: classificador de centroide mais próximo (Nearest Centroid Classifier);
    6. QuadraticDiscriminantAnalysis: classificador de análise discriminante quadrática (Quadratic Discriminant Analysis Classifier).

    Aparentemente, esses modelos são mais complexos em termos de sua estrutura e/ou lógica, e sua adaptação para execução no formato ONNX pode requerer esforços adicionais. Também é possível que eles usem estruturas de dados ou algoritmos específicos que não são totalmente suportados ou adequados para o formato ONNX.

    2. Os outros 27 modelos foram convertidos com sucesso para o formato ONNX e demonstraram manter sua precisão, destacando a eficácia do ONNX como meio de preservar e restaurar modelos de aprendizado de máquina. Isso possibilita transferir facilmente modelos entre diferentes ambientes e utilizá-los em várias aplicações, mantendo a qualidade de seu desempenho.

    Lista completa dos modelos convertidos com sucesso para o formato ONNX:

    1. SVC: classificador de vetores de suporte (Support Vector Classifier);
    2. LinearSVC: classificador de vetores de suporte linear (Linear Support Vector Classifier);
    3. NuSVC: classificador de vetores de suporte nu (Nu Support Vector Classifier);
    4. AdaBoostClassifier: classificador com boosting adaptativo (Adaptive Boosting Classifier);
    5. BaggingClassifier: classificador com agregação bootstrap (Bootstrap Aggregating Classifier);
    6. BernoulliNB: naive bayes bernoulli (Bernoulli Naive Bayes);
    7. CategoricalNB: naive bayes categórico (Categorical Naive Bayes);
    8. ComplementNB: naive bayes complementar (Complement Naive Bayes);
    9. DecisionTreeClassifier: classificador de árvore de decisão (Decision Tree Classifier);
    10. ExtraTreeClassifier: classificador de árvore extra (Extra Tree Classifier);
    11. ExtraTreesClassifier: classificador de árvores extras (Extra Trees Classifier);
    12. GaussianNB: naive bayes gaussiano (Gaussian Naive Bayes);
    13. GradientBoostingClassifier: classificador de boosting gradiente (Gradient Boosting Classifier);
    14. HistGradientBoostingClassifier: classificador de boosting gradiente baseado em histograma (Histogram-Based Gradient Boosting Classifier);
    15. classificador k-vizinhos mais próximos (KNeighborsClassifier);
    16. LinearDiscriminantAnalysis: análise discriminante linear (Linear Discriminant Analysis);
    17. LogisticRegression: classificador de regressão logística (Logistic Regression Classifier);
    18. LogisticRegressionCV: classificador de regressão logística com validação cruzada (Logistic Regression Classifier with Cross-Validation);
    19. MLPClassifier: classificador de perceptron de múltiplas camadas (Multi-Layer perceptron Classifier);
    20. MultinomialNB: naive bayes multinomial (Multinomial Naive Bayes);
    21. PassiveAggressiveClassifier: classificador passivo-agressivo (Passive-Aggressive Classifier);
    22. Perceptron: perceptron (Perceptron);
    23. RadiusNeighborsClassifier: classificador de vizinhos por raio (Radius Neighbors Classifier);
    24. RandomForestClassifier: classificador de floresta aleatória (Random Forest Classifier);
    25. RidgeClassifier: classificador ridge (Ridge Classifier);
    26. RidgeClassifierCV: classificador ridge com validação cruzada (Ridge Classifier with Cross-Validation);
    27. SGDClassifier: classificador de descida de gradiente estocástico (Stochastic Gradient Descent Classifier).

    3. Além disso, durante a pesquisa, foram destacados modelos que demonstraram eficácia notável na tarefa de classificação do conjunto de dados Iris. Os modelos de classificação, como o classificador Random Forest, o classificador Gradient Boosting, o classificador Bagging, o classificador Decision Tree, o classificador Extra Tree, o classificador Extra Trees e o classificador Hist Gradient Boosting, obtiveram uma precisão perfeita nas previsões. Isso significa que elas são capazes de determinar com alta precisão a qual classe cada amostra de íris no conjunto de dados pertence.

    Esses resultados podem ser particularmente úteis ao escolher o melhor modelo para uma tarefa específica de classificação. Modelos que alcançaram precisão perfeita nos dados do Iris podem ser a escolha ideal se sua tarefa estiver relacionada à análise ou classificação de dados semelhantes.

    Assim, a pesquisa realizada destaca a importância da escolha correta do modelo para uma tarefa específica e permite identificar as opções mais promissoras para futuras pesquisas e aplicação em tarefas práticas.


    Considerações finais

    O artigo analisou 33 modelos de classificação usando o conjunto de dados Iris com a biblioteca Scikit-learn versão 1.2.2.

    Dentre todos os modelos considerados, 6 mostraram-se desafiadores para conversão no formato ONNX. Esses modelos incluem o Dummy Classifier, Gaussian Process Classifier, Label Propagation Classifier, Label Spreading Classifier, Nearest Centroid Classifier, e o Quadratic Discriminant Analysis Classifier. Provavelmente, suas estruturas ou lógicas complexas requerem adaptação adicional para uma conversão bem-sucedida para o formato ONNX.

    Os outros 27 modelos foram convertidos com sucesso para o formato ONNX e demonstraram manter sua precisão. Isso confirma que o ONNX é um meio eficaz para preservar e restaurar modelos de aprendizado de máquina, garantindo sua portabilidade e a manutenção da qualidade de seu desempenho.

    É importante notar que os modelos de classificação, como o classificador Random Forest, o classificador Gradient Boosting, o classificador Bagging, o classificador Decision Tree, o classificador Extra Tree, o classificador Extra Trees e o classificador Hist Gradient Boosting, obtiveram uma precisão perfeita nas previsões. Esses modelos podem ser particularmente atraentes para tarefas de classificação onde a alta precisão é crucial.

    Este estudo ressalta a importância da escolha adequada do modelo para tarefas específicas e demonstra as vantagens de usar o ONNX para preservar e aplicar modelos de aprendizado de máquina em tarefas de classificação.

    Todos os scripts do artigo também estão disponíveis em um projeto público no MQL5\Shared Projects\Scikit.Classification.ONNX.

    Traduzido do russo pela MetaQuotes Ltd.
    Artigo original: https://www.mql5.com/ru/articles/13451

    A sazonalidade no mercado de moedas e suas possibilidades de uso A sazonalidade no mercado de moedas e suas possibilidades de uso
    Todo indivíduo moderno está familiarizado com o conceito de sazonalidade, por exemplo, todos nós estamos acostumados com o aumento dos preços de vegetais frescos no inverno ou o aumento do preço dos combustíveis durante fortes geadas, mas poucos sabem que existem padrões semelhantes no mercado de moedas.
    Redes neurais de maneira fácil (Parte 59): dicotomia do controle (DoC) Redes neurais de maneira fácil (Parte 59): dicotomia do controle (DoC)
    No artigo anterior, nos familiarizamos com o transformador de decisões. Porém, o complexo ambiente estocástico do mercado de moedas não permitiu revelar totalmente o potencial do método apresentado. Hoje, quero apresentar a vocês um algoritmo focado em melhorar o desempenho dos algoritmos em ambientes estocásticos.
    Desenvolvendo um sistema de Replay (Parte 44): Projeto do Chart Trade (III) Desenvolvendo um sistema de Replay (Parte 44): Projeto do Chart Trade (III)
    No artigo anterior, expliquei como você pode manipular os dados do template a fim de usá-los em um OBJ_CHART. Mas lá apenas introduzi a questão, mas sem entrar em muitos detalhes, já que naquela versão o trabalho foi feito de uma maneira bem simplificada. No entanto, ela foi feita daquela forma, justamente para facilitar a explicação do conteúdo. Pois apesar de parecer simples fazer certas coisas, algumas não são tão evidentes, e sem compreender a parte mais simples e básica, você não irá de fato entender o que estou fazendo.
    Fatorando Matrizes — O Básico Fatorando Matrizes — O Básico
    Como o intuito aqui é ser didático. Vou manter a coisa no seu padrão mais simples. Ou seja, iremos implementar apenas e somente o que será preciso. A multiplicação de matrizes. E você verá que isto será o suficiente para simular a multiplicação de uma matriz por um escalar. A grande dificuldade que muita gente tem em implementar um código usando fatoração de matrizes, é que diferente de uma fatoração escalar, onde em quase todos os casos a ordem dos fatores não altera o resultado. Quando se usa matrizes, a coisa não é bem assim.