English Русский Español Português Français Italiano Türkçe
preview
Scikit-learn 라이브러리의 회귀 모델과 이 모델을 ONNX로 내보내기

Scikit-learn 라이브러리의 회귀 모델과 이 모델을 ONNX로 내보내기

MetaTrader 5통합 | 19 9월 2024, 15:55
284 0
MetaQuotes
MetaQuotes

ONNX(오픈 신경망 교환)은 머신러닝 모델을 설명하고 교환하기 위한 형식으로서 서로 다른 머신러닝 프레임워크 간에 모델을 전송할 수 있는 기능을 제공합니다. 딥 러닝과 신경망에서는 float32와 같은 데이터 유형이 자주 사용됩니다. 일반적으로 딥러닝 모델 학습에 적합한 정확도와 효율성을 제공하기 때문에 널리 적용됩니다.

일부 고전적인 머신러닝 모델은 ONNX 연산자로 표현하기 어렵습니다. 따라서 ONNX에서 이를 구현하기 위해 연산자 (ai.onnx.ml)가 추가로 도입되었습니다. ONNX 사양에 따르면 이 집합의 키 연산자 )는 다양한 유형의 입력 데이터(tensor(float), tensor(double), tensor(int64), tensor(int32))를 받을 수 있지만 항상 출력시에는 tensor(float) 유형을 반환한다는 점에 주목할 필요가 있습니다. 이러한 연산자를 매개변수화 할때도 부동 소수점 수를 사용하여 수행 되므로 특히 원래 모델의 매개변수를 정의하는 데 배정밀도 수를 사용한 경우 계산의 정확도가 제한될 수 있습니다.

이로 인해 ONNX에서 데이터를 변환하고 처리하는 과정에서 모델을 변환하거나 다른 데이터 유형을 사용할 때 정확도가 떨어질 수 있습니다. 나중에 살펴보겠지만 일부 모델은 이러한 제한을 우회하고 ONNX 모델의 완전한 휴대성을 보장하여 정확도를 잃지 않고 이중 정밀도로 작업할 수 있습니다. 특히 데이터 표현의 정확성이 중요한 경우에는 ONNX에서 모델과 그 표현으로 작업할 때 이러한 특성을 고려하는 것이 중요합니다.

Scikit-learn은 파이썬 커뮤니티에서 가장 인기 있고 널리 사용되는 머신 러닝 라이브러리 중 하나이며 다양한 알고리즘, 사용자 친화적인 인터페이스, 훌륭한 설명서를 제공합니다. 이전 문서인 "Scikit-learn 라이브러리의 분류 모델 및 ONNX로 내보내기"는 분류 모델에 대해 다루었습니다.

이 글에서 우리는 Scikit-learn 패키지의 회귀 모델의 적용에 대해 살펴보고 테스트 데이터 세트에 대해 배정밀도로 파라미터를 계산하고 이를 부동 소수점 및 배정밀도를 위한 ONNX 형식으로 변환하고 이렇게 얻은 모델을 MQL5의 프로그램에서 사용하는 방법에 대해 알아볼 것입니다. 또한 부동 소수점 및 배정밀도 정확도에 대해 원본 모델과 ONNX 버전의 정확도를 비교합니다. 또한 회귀 모델의 내부 구조와 작동을 더 잘 이해할 수 있도록 회귀 모델의 ONNX 표현을 살펴볼 것입니다.


내용



귀찮으시다면 기고해 주세요.

런타임 개발자 포럼에서 사용자 중 한 명이 "[ONNXRuntimeError] 오류를 보고했습니다: 9 : NOT_IMPLEMENTED : ONNX 런타임을 통해 모델을 실행할 때 'LinearRegressor:LinearRegressor(1)' 노드에 대한 구현을 찾을 수 없습니다.

안녕하세요 선형 회귀 모델을 추론하려고 할 때 이 오류가 발생합니다. 이 문제를 해결하도록 도와주세요.

"not_implemented : LinearRegressor:LinearRegressor(1) 노드에 대한 구현을 찾을 수 없습니다." ONNX 런타임 개발자 포럼의 오류

"NOT_IMPLEMENTED : ONNX 런타임 개발자 포럼에서 "노드 LinearRegressor:LinearRegressor(1)"에 대한 구현을 찾을 수 없습니다 라는 에러.

개발자의 답변:

float64가 아닌 float32에 대해서만 구현했기 때문입니다. 하지만 귀하의 모델에는 float64가 필요합니다.

참조:
hgithub.com/microsoft/onnxruntime/blob/master/onnxruntime/core/providers/cpu/ml/linearregressor.cc#L16https://github.com/microsoft/onnxruntime/blob/master/onnxruntime/core/providers/cpu/ml/linearregressor.cc#L16

귀찮으시다면 기고해 주세요.

사용자의 ONNX 모델에서 연산자는 double(float64) 데이터 유형으로 호출되며 ONNX 런타임이 배정밀도 LinearRegressor() 연산자를 지원하지 않기 때문에 오류 메시지가 발생합니다.

연산자의 사양에 따르면 double 입력 데이터 유형(T: tensor(float), tensor(double), tensor(int64), tensor(int32)이 가능하지만 개발자는 의도적으로 이를 구현하지 않기로 결정했습니다.

그 이유는 출력에 항상 Y: tensor(float) 값이 반환되기 때문입니다. 또한 계산 매개변수는 실수입니다(계수: 실수 목록, 인터셉트: 실수 목록).

따라서 계산이 배정밀도로 수행될 때 이 연산자는 정밀도를 부동 소수점으로 감소시키므로 배정밀도 계산에서 필요할지 의심스럽습니다.


ai.onnx.ml.LinearRegressor 연산자 설명

ai.onnx.ml.LinearRegressor 연산자 설명


따라서 매개변수 및 출력 값의 정밀도를 float으로 낮추면 double(float64) 수로 완전히 작동할 수 없게 됩니다. 아마도 이러한 이유로 ONNX 런타임 개발자는 double 유형에 대한 구현을 자제하기로 결정했을 것입니다.

'double 지원 추가' 메서드는 개발자가 코드 주석(노란색으로 강조 표시)을 통해 시연했습니다.

ONNX 런타임에서는 LinearRegressor 클래스(https://github.com/microsoft/onnxruntime/blob/main/onnxruntime/core/providers/cpu/ml/linearregressor.h)를 사용하여 계산을 수행합니다.

연산자의 매개변수인 coefficients_ 및 intercepts_는 std::vector<float>로 저장됩니다:

#pragma once

#include "core/common/common.h"
#include "core/framework/op_kernel.h"
#include "core/util/math_cpuonly.h"
#include "ml_common.h"

namespace onnxruntime {
namespace ml {

class LinearRegressor final : public OpKernel {
 public:
  LinearRegressor(const OpKernelInfo& info);
  Status Compute(OpKernelContext* context) const override;

 private:
  int64_t num_targets_;
  std::vector<float> coefficients_;
  std::vector<float> intercepts_;
  bool use_intercepts_;
  POST_EVAL_TRANSFORM post_transform_;
};

}  // namespace ml
}  // namespace onnxruntime

LinearRegressor 연산자 구현 (https://github.com/microsoft/onnxruntime/blob/main/onnxruntime/core/providers/cpu/ml/linearregressor.cc)
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

#include "core/providers/cpu/ml/linearregressor.h"
#include "core/common/narrow.h"
#include "core/providers/cpu/math/gemm.h"

namespace onnxruntime {
namespace ml {

ONNX_CPU_OPERATOR_ML_KERNEL(
    LinearRegressor,
    1,
    // KernelDefBuilder().TypeConstraint("T", std::vector<MLDataType>{
    //                                            DataTypeImpl::GetTensorType<float>(),
    //                                            DataTypeImpl::GetTensorType<double>()}),
    KernelDefBuilder().TypeConstraint("T", DataTypeImpl::GetTensorType<float>()),
    LinearRegressor);

LinearRegressor::LinearRegressor(const OpKernelInfo& info)
    : OpKernel(info),
      intercepts_(info.GetAttrsOrDefault<float>("intercepts")),
      post_transform_(MakeTransform(info.GetAttrOrDefault<std::string>("post_transform", "NONE"))) {
  ORT_ENFORCE(info.GetAttr<int64_t>("targets", &num_targets_).IsOK());
  ORT_ENFORCE(info.GetAttrs<float>("coefficients", coefficients_).IsOK());

  // use the intercepts_ if they're valid
  use_intercepts_ = intercepts_.size() == static_cast<size_t>(num_targets_);
}

// Use GEMM for the calculations, with broadcasting of intercepts
// https://github.com/onnx/onnx/blob/main/docs/Operators.md#Gemm
//
// X: [num_batches, num_features]
// coefficients_: [num_targets, num_features]
// intercepts_: optional [num_targets].
// Output: X * coefficients_^T + intercepts_: [num_batches, num_targets]
template <typename T>
static Status ComputeImpl(const Tensor& input, ptrdiff_t num_batches, ptrdiff_t num_features, ptrdiff_t num_targets,
                          const std::vector<float>& coefficients,
                          const std::vector<float>* intercepts, Tensor& output,
                          POST_EVAL_TRANSFORM post_transform,
                          concurrency::ThreadPool* threadpool) {
  const T* input_data = input.Data<T>();
  T* output_data = output.MutableData<T>();

  if (intercepts != nullptr) {
    TensorShape intercepts_shape({num_targets});
    onnxruntime::Gemm<T>::ComputeGemm(CBLAS_TRANSPOSE::CblasNoTrans, CBLAS_TRANSPOSE::CblasTrans,
                                      num_batches, num_targets, num_features,
                                      1.f, input_data, coefficients.data(), 1.f,
                                      intercepts->data(), &intercepts_shape,
                                      output_data,
                                      threadpool);
  } else {
    onnxruntime::Gemm<T>::ComputeGemm(CBLAS_TRANSPOSE::CblasNoTrans, CBLAS_TRANSPOSE::CblasTrans,
                                      num_batches, num_targets, num_features,
                                      1.f, input_data, coefficients.data(), 1.f,
                                      nullptr, nullptr,
                                      output_data,
                                      threadpool);
  }

  if (post_transform != POST_EVAL_TRANSFORM::NONE) {
    ml::batched_update_scores_inplace(gsl::make_span(output_data, SafeInt<size_t>(num_batches) * num_targets),
                                      num_batches, num_targets, post_transform, -1, false, threadpool);
  }
  return Status::OK();
}

Status LinearRegressor::Compute(OpKernelContext* ctx) const {
  Status status = Status::OK();

  const auto& X = *ctx->Input<Tensor>(0);
  const auto& input_shape = X.Shape();

  if (input_shape.NumDimensions() > 2) {
    return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT, "Input shape had more than 2 dimension. Dims=",
                           input_shape.NumDimensions());
  }

  ptrdiff_t num_batches = input_shape.NumDimensions() <= 1 ? 1 : narrow<ptrdiff_t>(input_shape[0]);
  ptrdiff_t num_features = input_shape.NumDimensions() <= 1 ? narrow<ptrdiff_t>(input_shape.Size())
                                                            : narrow<ptrdiff_t>(input_shape[1]);
  Tensor& Y = *ctx->Output(0, {num_batches, num_targets_});
  concurrency::ThreadPool* tp = ctx->GetOperatorThreadPool();

  auto element_type = X.GetElementType();

  switch (element_type) {
    case ONNX_NAMESPACE::TensorProto_DataType_FLOAT: {
      status = ComputeImpl<float>(X, num_batches, num_features, narrow<ptrdiff_t>(num_targets_), coefficients_,
                                  use_intercepts_ ? &intercepts_ : nullptr,
                                  Y, post_transform_, tp);

      break;
    }
    case ONNX_NAMESPACE::TensorProto_DataType_DOUBLE: {
      // TODO: Add support for 'double' to the scoring functions in ml_common.h
      // once that is done we can just call ComputeImpl<double>...
      // Alternatively we could cast the input to float.
    }
    default:
      status = ORT_MAKE_STATUS(ONNXRUNTIME, FAIL, "Unsupported data type of ", element_type);
  }

  return status;
}

}  // namespace ml
}  // namespace onnxruntime

입력 값으로 double 숫자를 사용하고 float 매개 변수로 연산자의 계산을 수행하는 옵션이 있다는 것이 밝혀졌습니다. 또 다른 가능성은 입력 데이터의 정밀도를 float으로 낮추는 것입니다. 그러나 이러한 옵션 중 어느 것도 적절한 솔루션으로 간주될 수 없습니다.

ai.onnx.ml.LinearRegressor 연산자의 사양은 매개변수와 출력 값이 float 유형으로 제한되어 있기 때문에 double 숫자로 전체 연산을 할 수 있는 기능이 제한되어 있습니다.

다른 ONNX ML 운영업체에서도 비슷한 상황이 발생합니다. ai.onnx.ml.SVMRegressorai.onnx.ml.TreeEnsembleRegressor

그 결과 배정밀도의 ONNX 모델 실행을 사용하는 모든 개발자는 이와 같은 사양의 한계에 직면하게 됩니다. 해결책은 ONNX 사양을 확장하는 것(또는 매개변수와 출력값이 두 배인 LinearRegressor64, SVMRegressor64, TreeEnsembleRegressor64와 같은 유사한 연산자를 추가하는 것)이 될 것입니다. 하지만 현재 이 문제는 아직 해결되지 않은 상태입니다.

ONNX 변환기에 따라 많은 것이 달라지게 됩니다. double로 계산되는 모델의 경우 이러한 연산자를 사용하지 않는 것이 바람직할 수 있습니다(항상 가능한 것은 아니지만). 이 경우에는 ONNX로 변환기가 사용자의 모델에서 최적으로 작동하지 않았습니다.

나중에 살펴보겠지만 sklearn-onnx 변환기는 LinearRegressor의 제한을 우회하여 ONNX double 모델의 경우 ONNX 연산자 MatMul()Add() 를 대신 사용합니다. 이 메서드 덕분에 Scikit-learn 라이브러리의 수많은 회귀 모델이 원래 double 모델의 정확성을 유지하면서 double로 계산된 ONNX 모델로 성공적으로 변환됩니다.


    1. 테스트 데이터 세트

    예제를 실행하려면 파이썬(3.10.8 버전 사용), 추가 라이브러리(pip install -U scikit-learn numpy matplotlib onnx onnxruntime skl2onnx)를 설치하고 메타에디터의 메뉴(도구->옵션->컴파일러->파이썬)에서 파이썬의 경로를 지정해야 합니다.

    테스트 데이터셋으로는 y = 4X + 10sin(X*0.5) 함수로 생성된 값을 사용하겠습니다.

    이러한 함수의 그래프를 표시하려면 메타에디터를 열고 RegressionData.py라는 파일을 만든 다음 스크립트 텍스트를 복사하고 "컴파일" 버튼을 클릭하여 실행합니다.

    테스트 데이터 집합을 표시하는 스크립트

    # RegressionData.py
    # The code plots the synthetic data, used for all regression models
    # Copyright 2023, MetaQuotes Ltd.
    # https://mql5.com
    
    # import necessary libraries
    import numpy as np
    import matplotlib.pyplot as plt
    
    # generate synthetic data for regression
    X = np.arange(0,100,1).reshape(-1,1)
    y = 4*X + 10*np.sin(X*0.5)
    
    # set the figure size
    plt.figure(figsize=(8,5))
    
    # plot the initial data for regression
    plt.scatter(X, y, label='Regression Data', marker='o')
    plt.xlabel('X')
    plt.ylabel('y')
    plt.legend()
    plt.title('Regression data')
    plt.show()

    결과적으로 함수 그래프가 표시되며 우리는 이를 사용하여 회귀 메서드를 테스트할 것입니다.

    그림 1. 회귀 모델 테스트 함수

    그림 1. 회귀 모델 테스트용 함수


    2. 회귀 모델

    회귀 작업을 하는 목표는 새로운 데이터의 수치 값을 예측하기 위해 피처와 대상 변수 간의 관계를 가장 잘 설명하는 수학적 함수 또는 모델을 찾는 것입니다. 이를 통해 우리는 예측을 하고 솔루션을 최적화하고 데이터를 기반으로 정보에 입각한 의사 결정을 내릴 수 있습니다.

    이제 scikit-learn 패키지의 주요 회귀 모델에 대해 살펴보도록 하겠습니다.

    2.0. Scikit-learn 회귀 모델 목록

    사용 가능한 scikit-learn 회귀 모델 목록을 표시하려면 스크립트를 사용하면 됩니다:

    # ScikitLearnRegressors.py
    # The script lists all the regression algorithms available inb 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 regression models
    from sklearn.utils import all_estimators
    
    regressors = all_estimators(type_filter='regressor')
    for index, (name, RegressorClass) in enumerate(regressors, start=1):
        print(f"Regressor {index}: {name}")

    출력:

    Python 버전은 3.10.8입니다.
    scikit-learn 버전은 1.3.2입니다.
    Regressor 1: ARDRegression
    Regressor 2: AdaBoostRegressor
    Regressor 3: BaggingRegressor
    Regressor 4: BayesianRidge
    Regressor 5: CCA
    Regressor 6: DecisionTreeRegressor
    Regressor 7: DummyRegressor
    Regressor 8: ElasticNet
    Regressor 9: ElasticNetCV
    Regressor 10: ExtraTreeRegressor
    Regressor 11: ExtraTreesRegressor
    Regressor 12: GammaRegressor
    Regressor 13: GaussianProcessRegressor
    Regressor 14: GradientBoostingRegressor
    Regressor 15: HistGradientBoostingRegressor
    Regressor 16: HuberRegressor
    Regressor 17: IsotonicRegression
    Regressor 18: KNeighborsRegressor
    Regressor 19: KernelRidge
    Regressor 20: Lars
    Regressor 21: LarsCV
    Regressor 22: Lasso
    Regressor 23: LassoCV
    Regressor 24: LassoLars
    Regressor 25: LassoLarsCV
    Regressor 26: LassoLarsIC
    Regressor 27: LinearRegression
    Regressor 28: LinearSVR
    Regressor 29: MLPRegressor
    Regressor 30: MultiOutputRegressor
    Regressor 31: MultiTaskElasticNet
    Regressor 32: MultiTaskElasticNetCV
    Regressor 33: MultiTaskLasso
    Regressor 34: MultiTaskLassoCV

    Regressor 35: NuSVR
    Regressor 36: OrthogonalMatchingPursuit
    Regressor 37: OrthogonalMatchingPursuitCV
    Regressor 38: PLSCanonical
    Regressor 39: PLSRegression
    Regressor 40: PassiveAggressiveRegressor
    Regressor 41: PoissonRegressor
    Regressor 42: QuantileRegressor
    Regressor 43: RANSACRegressor
    Regressor 44: RadiusNeighborsRegressor
    Regressor 45: RandomForestRegressor
    Regressor 46: RegressorChain
    Regressor 47: Ridge
    Regressor 48: RidgeCV
    Regressor 49: SGDRegressor
    Regressor 50: SVR
    Regressor 51: StackingRegressor
    Regressor 52: TheilSenRegressor
    Regressor 53: TransformedTargetRegressor
    Regressor 54: TweedieRegressor
    Regressor 55: VotingRegressor

    이 회귀 변수 목록의 편의성을 위해 회귀 ㅂㄴ수들은 다른 색상으로 강조 표시되어 있습니다. 기본 회귀 모델이 필요한 모델은 회색으로 강조 표시되고 다른 모델은 독립적으로 사용할 수 있습니다. ONNX 형식으로 성공적으로 내보낸 모델은 녹색으로 표시되고 현재 버전의 scikit-learn 1.2.2에서 변환하는 동안 오류가 발생한 모델은 빨간색으로 표시됩니다. 테스트 작업에 적합하지 않은 메서드는 파란색으로 강조 표시됩니다.

    회귀 품질 분석은 실제 값과 예측 값의 함수인 회귀 메트릭을 사용합니다. MQL5 언어에서는 "회귀 메트릭을 사용하여 ONNX 모델 평가하기"라는 문서에 자세히 설명된 여러 가지 메트릭을 사용할 수 있습니다.

    이 글에서는 세 가지 메트릭을 사용하여 서로 다른 모델의 품질을 비교합니다:

    1. 결정 계수 R-제곱(R2);
    2. 평균 절대 오류(MAE);
    3. 평균 제곱 오차(MSE).


    2.1. ONNX 모델 float 및 double로 변환하는 Scikit 학습 회귀 모델

    이 섹션에서는 부동 소수점 및 배정밀도 모두에서 ONNX 형식으로 성공적으로 변환된 회귀 모델을 소개합니다.

    이후 다룰 회귀 모델은 다음의 형식입니다:

    1. 모델 설명, 작동 원리, 장점 및 제한 사항
    2. 모델을 생성하고 float 및 double 형식의 ONNX 파일로 내보내고 파이썬의 ONNX 런타임을 사용하여 얻은 모델을 실행하기 위한 파이썬 스크립트입니다. sklearn.metrics를 사용하여 계산된 R^2, MAE, MSE와 같은 메트릭은 원본 및 ONNX 모델의 품질을 평가하는 데 사용됩니다.
    3. ONNX 런타임을 통해 ONNX 모델(float 및 double)을 실행하기 위한 MQL5 스크립트로, RegressionMetric()을 사용하여 메트릭을 계산합니다.
    4. 부동 소수점 및 배정밀도용 Netron의 ONNX 모델 표현.


      2.1.1. sklearn.linear_model.ARDRegression

      ARDRegression (자동 관련성 결정 회귀)는 모델 학습 과정에서 피처의 중요도(관련성)를 자동으로 결정하고 가중치를 설정하면서 회귀 문제를 해결하기 위해 고안된 회귀 메서드입니다.

      ARDRegression을 사용하면 회귀 모델을 구축하는 데 가장 중요한 변수만 감지하고 사용할 수 있으므로 많은 변수를 다룰 때 유용할 수 있습니다.

      ARDRegression의 작동 원리:

      1. 선형 회귀: ARDRegression은 독립 변수(피처)와 대상 변수 간의 선형 관계를 가정하는 선형 회귀를 기반으로 합니다.
      2. 자동 피처 중요도 결정: ARDRegression의 주요 피처는 대상 변수를 예측하는 데 가장 중요한 변수를 자동으로 결정한다는 점입니다. 이는 가중치에 사전 분포(정규화)를 도입하여 모델이 중요도가 낮은 변수에 대해 자동으로 0이란 가중치를 설정하도록 함으로써 달성할 수 있습니다.
      3. 사후 확률 추정: ARDRegression은 각 변수에 대한 사후 확률을 계산하여 그 중요성을 결정할 수 있습니다. 사후 확률이 높은 변수는 관련성이 높은 것으로 간주되어 0이 아닌 가중치를 받고 사후 확률이 낮은 변수는 0의 가중치를 받습니다.
      4. 차원 감소: 따라서 ARDRegression는 중요하지 않은 변수를 제거하여 데이터 차원을 줄일 수 있습니다.

      ARDRegression의 장점:

      • 중요한 변수의 자동 결정: 이 메서드는 가장 중요한 메서드만 자동으로 식별하여 사용하므로 잠재적으로 모델의 성능을 향상시키고 과적합 되는 위험을 줄일 수 있습니다.
      • 다중 선형성에 대한 복원력: ARDRegression은 변수의 상관관계가 높은 경우에도 다중 공선형성을 잘 처리합니다.

      ARDRegression의 한계:

      • 사전 배포를 선택해야 합니다: 적절한 사전 배포를 선택하려면 실험이 필요할 수 있습니다.
      • 계산의 복잡성: 특히 대규모 데이터 세트의 경우 ARDRegression 훈련은 계산 비용이 많이 들 수 있습니다.

      ARDRegression은 자동으로 변수의 중요도를 결정하고 사후 확률을 기반으로 가중치를 설정하는 회귀 메서드입니다. 이 메서드는 회귀 모델을 구축할 때 중요한 변수만 고려해야 할 때와 데이터 차원을 줄여야 할 때 유용합니다.


      2.1.1.1. ARDRegression 모델을 생성하고 float 및 double용 ONNX로 내보내기 위한 코드

      이 코드는 sklearn.linear_model.ARDRegression 모델을 생성하고 합성 데이터로 학습시키고 모델을 ONNX 형식으로 저장하고 float 및 double 입력 데이터를 모두 사용하여 예측을 수행합니다. 또한 원본 모델과 ONNX로 내보낸 모델 모두의 정확도를 평가합니다.

      # ARDRegression.py
      # 이 코드는 ARDRegressor 모델을 학습하고 이를 ONNX 형식(float 및 double 모두)으로 내보내고 ONNX 모델을 사용하여 예측하는 과정을 보여 줍니다.
      # Copyright 2023, MetaQuotes Ltd.
      # https://www.mql5.com


      # 소수점 이하 자릿수를 비교학 위한 함수
      def compare_decimal_places(value1, value2):
      # 두 값을 모두 문자열로 변환
          str_value1 = str(value1)
          str_value2 = str(value2)

      # 문자열에서 소수점 위치 찾기
          dot_position1 = str_value1.find(".")
          dot_position2 = str_value2.find(".")

      # 값 중 소수점이 없는 경우 0을 반환합니다.
          만약 dot_position1 == --1 또는 dot_position2 == --1:
              return 0

          # 소수점 이하 자릿수 계산
          decimal_places1 = len(str_value1) - dot_position1 - 1
          decimal_places2 = len(str_value2) - dot_position2 - 1

          # 소수점 이하 두 자리 수 중 최소값 찾기
          min_decimal_places = min(decimal_places1, decimal_places2)

          # 소수점 이하 자릿수가 일치하도록 카운트를 초기화
          matching_count = 0

          # 소수점 이하 문자 비교
          for i in range(1, min_decimal_places + 1):
              if str_value1[dot_position1 + i] == str_value2[dot_position2 + i]:
                  matching_count += 1
              else:
                  break

          return matching_count

      # 필요한 라이브러리 가져오기
      import numpy as np
      import matplotlib.pyplot as plt
      from sklearn.linear_model import ARDRegression
      from sklearn.metrics import r2_score,mean_absolute_error,mean_squared_error
      import onnx
      import onnxruntime as ort
      from skl2onnx import convert_sklearn
      from skl2onnx.common.data_types import FloatTensorType
      from skl2onnx.common.data_types import DoubleTensorType
      from sys import argv

      # 모델 저장 경로를 정의합니다.
      data_path = argv[0]
      last_index = data_path.rfind("\\")+ 1
      data_path = data_path[0:last_index]

      # 회귀를 위한 합성 데이터 생성
      X = np.arange(0,100,1).reshape(-1,1)
      y = 4*X+ 10*np.sin(X*0.5)

      model_name="ARDRegression"
      onnx_model_filename = data_path + "ard_regression"

      # ARDRegression 모델 만들기
      regression_model = ARDRegression()

      # 데이터에 모델 맞추기
      regression_model.fit(X, y.ravel())

      # 전체 데이터 집합에 대한 값 예측
      y_pred = regression_model.predict(X)

      # 모델의 성능 평가
      r2 = r2_score(y, y_pred)
      MSE = MEAN_SQUARE_ERROR(Y, Y_PED)
      MAE = MEAN_ABSOLUTE_ERROR(Y, Y_PED)

      print("\n"+model_name+" Original model (double)")
      print("R-squared (Coefficient of determination):", r2)
      print("Mean Absolute Error:", mae)
      print("평균 제곱 오차:", mse)

      # ONNX 모델로 변환 (float)
      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.

      initial_type_float = [('float_input', FloatTensorType([None, X.shape[1]])]]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_float = convert_sklearn(regression_model, initial_types=initial_type_float, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_float.onnx"
      onnx.save_model(onnx_model_float, onnx_filename)

      print("\n"+model_name+" ONNX model (float)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.
      initial_type_float = X.astype(np.float32)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_float = onnx_session.run([output_name], {input_name: initial_type_float})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_float = r2_score(y, y_pred_onnx_float)
      mse_onnx_float = mean_squared_error(y, y_pred_onnx_float)
      mae_onnx_float = mean_absolute_error(y, y_pred_onnx_float)

      print("R-squared (Coefficient of determination)", r2_onnx_float)
      print("Mean Absolute Error:", mae_onnx_float)
      print("Mean Squared Error:", mse_onnx_float)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_float))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_float))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_float))
      print("float ONNX model precision: ",compare_decimal_places(mae, mae_onnx_float))

      # 그림 크기 설정
      plt.figure(figsize=(8, 5))
      # 원본 데이터와 회귀 데이터를 플로팅합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with float ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_float.png')

      # ONNX 모델로 변환 (double)
      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.
      initial_type_double = [('double_input', DoubleTensorType([None, X.shape[1]]))]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_double = convert_sklearn(regression_model, initial_types=initial_type_double, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_double.onnx"
      onnx.save_model(onnx_model_double, onnx_filename)

      print("\n"+model_name+" ONNX model (double)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.
      initial_type_double = X.astype(np.float64)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_double = onnx_session.run([output_name], {input_name: initial_type_double})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_double = r2_score(y, y_pred_onnx_double)
      mse_onnx_double = mean_squared_error(y, y_pred_onnx_double)
      mae_onnx_double = mean_absolute_error(y, y_pred_onnx_double)

      print("R-squared (Coefficient of determination)", r2_onnx_double)
      print("Mean Absolute Error:", mae_onnx_double)
      print("Mean Squared Error:", mse_onnx_double)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_double))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_double))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_double))
      print("double ONNX model precision: ",compare_decimal_places(mae, mae_onnx_double))

      # 그림 크기 설정
      plt.figure(figsize=(8, 5))
      # 원본 데이터와 회귀선을 그림으로 표시합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with double ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_double.png')

      이 스크립트는 sklearn.linear_model.ARDRegression 모델을 생성하고 학습한 다음(원래 모델은 double로 간주됨), float 및 double용 ONNX로 모델을 내보내고(ard_regression_float.onnxard_regression_double.onnx) 그 연산 정확도를 비교합니다.

      또한 float 및 double에 대한 ONNX 모델의 결과를 시각적으로 평가할 수 있도록 ARDRegression_plot_float.png 및 ARDRegression_plot_double.png 파일을 생성합니다(그림 2-3).

      Fig.2. Results of the ARDRegression.py (float)

      그림 2. ARDRegression.py의 결과(float)


      Fig.3. Results of the ARDRegression.py (double)

      그림 3. ARDRegression.py의 결과(double)

      시각적으로 float 및 double용ONNX 모델은 동일하게 보입니다(그림 2-3), 자세한 정보는 저널 탭에서 확인할 수 있습니다:

      Python  ARDRegression Original model (double)
      Python  R-squared (Coefficient of determination): 0.9962382628120845
      Python  Mean Absolute Error: 6.347568012853758
      Python  Mean Squared Error: 49.77815934891289
      Python  
      Python  ARDRegression ONNX model (float)
      Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\ard_regression_float.onnx
      Python  Information about input tensors in ONNX:
      Python  1. Name: float_input, Data Type: tensor(float), Shape: [None, 1]
      Python  Information about output tensors in ONNX:
      Python  1. Name: variable, Data Type: tensor(float), Shape: [None, 1]
      Python  R-squared (Coefficient of determination) 0.9962382627587808
      Python  Mean Absolute Error: 6.347568283744705
      Python  Mean Squared Error: 49.778160054267204
      Python  R^2 matching decimal places:  9
      Python  MAE matching decimal places:  6
      Python  ONNX: MSE matching decimal places:  4
      Python  float ONNX model precision:  6
      Python  
      Python  ARDRegression ONNX model (double)
      Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\ard_regression_double.onnx
      Python  Information about input tensors in ONNX:
      Python  1. Name: double_input, Data Type: tensor(double), Shape: [None, 1]
      Python  Information about output tensors in ONNX:
      Python  1. Name: variable, Data Type: tensor(double), Shape: [None, 1]
      Python  R-squared (Coefficient of determination) 0.9962382628120845
      Python  Mean Absolute Error: 6.347568012853758
      Python  Mean Squared Error: 49.77815934891289
      Python  R^2 matching decimal places:  16
      Python  MAE matching decimal places:  15
      Python  MSE matching decimal places:  14
      Python  double ONNX model precision:  15

      이 예제에서는 원본 모델을 double로 고려한 다음 각각 float 및 double에 대해 ONNX 모델 ard_regression_float.onnxard_regression_double.onnx로 내보냈습니다.

      모델의 정확도를 평균 절대 오차(MAE)로 평가할 경우 float용 ONNX 모델의 정확도는 소수점 이하 6자리까지 double을 사용한 ONNX 모델은 소수점 이하 15자리까지 정확도를 유지하여 원래 모델의 정밀도와 비슷하게 유지되는 것으로 나타났습니다.

      ONNX 모델의 속성은 MetaEditor에서 볼 수 있습니다(그림 4-5).


      Fig.4. ard_regression_float.onnx ONNX-model in MetaEditor

      그림 4. MetaEditor의 ard_regression_float.onnx ONNX 모델



      그림 5. MetaEditor의 ard_regression_double.onnx ONNX 모델

      그림 5. MetaEditor의 ard_regression_double.onnx ONNX 모델


      실수형과 이중 ONNX 모델을 비교하면 이 경우 ARDRegression을 위한 ONNX 모델의 계산이 다르게 수행된다는 것을 알 수 있습니다. 실수형에는 ONNX-ML의 LinearRegressor() 연산자가 사용되는 반면 이중 숫자에는 ONNX 연산자 , Add()Reshape() 가 사용됩니다.

      ONNX에서 모델을 구현하는 것은 변환기에 따라 다릅니다; ONNX로 내보내기 예제에서는 skl2onnx 라이브러리의 skl2onnx.convert_sklearn() 함수가 사용됩니다.


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

      이 코드는 저장된 ard_regression_float.onnxard_regression_double.onnx ONNX 모델을 실행하고 MQL5에서 회귀 메트릭을 사용하는 것을 보여줍니다.

      //+------------------------------------------------------------------+
      //|                                                ARDRegression.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"
      
      #define   ModelName          "ARDRegression"
      #define   ONNXFilenameFloat  "ard_regression_float.onnx"
      #define   ONNXFilenameDouble "ard_regression_double.onnx"
      
      #resource ONNXFilenameFloat  as const uchar ExtModelFloat[];
      #resource ONNXFilenameDouble as const uchar ExtModelDouble[];
      
      #define   TestFloatModel  1
      #define   TestDoubleModel 2
      
      //+------------------------------------------------------------------+
      //| Calculate regression using float values                          |
      //+------------------------------------------------------------------+
      bool RunModelFloat(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         float input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=(float)input_vector[k];
      //--- prepare output tensor
         float output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      //+------------------------------------------------------------------+
      //| Calculate regression using double values                         |
      //+------------------------------------------------------------------+
      bool RunModelDouble(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         double input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=input_vector[k];
      //--- prepare output tensor
         double output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      
      //+------------------------------------------------------------------+
      //| Generate synthetic data                                          |
      //+------------------------------------------------------------------+
      bool GenerateData(const int n,vector &x,vector &y)
        {
         if(n<=0)
            return(false);
      //--- prepare arrays
         x.Resize(n);
         y.Resize(n);
      //---
         for(int i=0; i<n; i++)
           {
            x[i]=(double)1.0*i;
            y[i]=(double)(4*x[i] + 10*sin(x[i]*0.5));
           }
      //---
         return(true);
        }
      
      //+------------------------------------------------------------------+
      //| TestRegressionModel                                              |
      //+------------------------------------------------------------------+
      bool TestRegressionModel(const string model_name,const int model_type)
        {
      //---
         long  model=INVALID_HANDLE;
         ulong flags=ONNX_DEFAULT;
      
         if(model_type==TestFloatModel)
           {
            PrintFormat("\nTesting ONNX float: %s (%s)",model_name,ONNXFilenameFloat);
            model=OnnxCreateFromBuffer(ExtModelFloat,flags);
           }
         else
            if(model_type==TestDoubleModel)
              {
               PrintFormat("\nTesting ONNX double: %s (%s)",model_name,ONNXFilenameDouble);
               model=OnnxCreateFromBuffer(ExtModelDouble,flags);
              }
            else
              {
               PrintFormat("Model type is not incorrect.");
               return(false);
              }
      //--- check
         if(model==INVALID_HANDLE)
           {
            PrintFormat("model_name=%s OnnxCreate error %d",model_name,GetLastError());
            return(false);
           }
      //---
         vector x_values= {};
         vector y_true= {};
         vector y_predicted= {};
      //---
         int n=100;
         GenerateData(n,x_values,y_true);
      //---
         bool run_result=false;
         if(model_type==TestFloatModel)
           {
            run_result=RunModelFloat(model,x_values,y_predicted);
           }
         else
            if(model_type==TestDoubleModel)
              {
               run_result=RunModelDouble(model,x_values,y_predicted);
              }
      //---
         if(run_result)
           {
            PrintFormat("MQL5:   R-Squared (Coefficient of determination): %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_R2));
            PrintFormat("MQL5:   Mean Absolute Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MAE));
            PrintFormat("MQL5:   Mean Squared Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MSE));
           }
         else
            PrintFormat("Error %d",GetLastError());
      //--- release model
         OnnxRelease(model);
      //---
         return(true);
        }
      //+------------------------------------------------------------------+
      //| Script program start function                                    |
      //+------------------------------------------------------------------+
      int OnStart(void)
        {
      //--- test ONNX regression model for float
         TestRegressionModel(ModelName,TestFloatModel);
      //--- test ONNX regression model for double
         TestRegressionModel(ModelName,TestDoubleModel);
      //---
         return(0);
        }
      //+------------------------------------------------------------------+

      출력:

      ARDRegression (EURUSD,H1)       Testing ONNX float: ARDRegression (ard_regression_float.onnx)
      ARDRegression (EURUSD,H1)       MQL5:   R-Squared (Coefficient of determination): 0.9962382627587808
      ARDRegression (EURUSD,H1)       MQL5:   Mean Absolute Error: 6.3475682837447049
      ARDRegression (EURUSD,H1)       MQL5:   Mean Squared Error: 49.7781600542671896
      ARDRegression (EURUSD,H1)       
      ARDRegression (EURUSD,H1)       Testing ONNX double: ARDRegression (ard_regression_double.onnx)
      ARDRegression (EURUSD,H1)       MQL5:   R-Squared (Coefficient of determination): 0.9962382628120845
      ARDRegression (EURUSD,H1)       MQL5:   Mean Absolute Error: 6.3475680128537597
      ARDRegression (EURUSD,H1)       MQL5:   Mean Squared Error: 49.7781593489128795
      

      파이썬에서 원래 double 모델과 비교:

      Testing ONNX float: ARDRegression (ard_regression_float.onnx)
      Python  Mean Absolute Error: 6.347568012853758
      MQL5:   Mean Absolute Error: 6.3475682837447049
             
      Testing ONNX double: ARDRegression (ard_regression_double.onnx)
      Python  Mean Absolute Error: 6.347568012853758
      MQL5:   Mean Absolute Error: 6.3475680128537597
      

      ONNX float MAE의 정확도: 소수점 이하 6자리, 정확도 ONNX double MAE: 소수점 이하 14자리.


      2.1.1.3. 모델 ard_regression_float.onnx 및 ard_regression_double.onnx의 ONNX 표현은 다음과 같습니다.

      Netron (웹 버전)은 모델을 시각화하고 계산 그래프를 분석하는 도구로, ONNX(Open Neural Network Exchange) 형식의 모델에 사용할 수 있습니다.

      Netron은 모델 그래프와 그 아키텍처를 명확하고 인터랙티브한 형태로 표시하여 ONNX를 사용하여 만든 모델을 포함한 딥 러닝 모델의 구조와 매개변수를 탐색할 수 있습니다.

      Netron의 주요 기능은 다음과 같습니다:

      • 그래프 시각화: Netron은 모델의 아키텍처를 그래프로 표시하여 여러분이 레이어, 작업 및 이들 간의 연결을 확인할 수 있게 합니다. 여러분은 모델 내의 구조와 데이터 흐름을 쉽게 이해할 수 있을 것입니다.
      • 대화형 탐색: 그래프에서 노드를 선택하여 각 연산자 및 해당 매개변수에 대한 추가 정보를 얻을 수 있습니다.
      • 다양한 형식 지원: 네트론은 ONNX, 텐서플로우, 파이토치, 코어ML 등 다양한 딥러닝 모델 형식을 지원합니다.
      • 매개변수 분석 기능: 모델의 매개변수와 가중치를 볼 수 있어 모델의 여러 부분에서 사용된 값을 이해하는 데 유용합니다.

      Netron은 모델의 시각화 및 분석을 단순화하는 것을 통해 복잡한 신경망의 이해와 디버깅을 도와줍니다. 이는 머신러닝 및 딥러닝 분야의 개발자와 연구자들에게 도움이 되는 기능입니다.

      이 도구를 사용하면 모델을 빠르게 검사하고 구조와 매개변수를 탐색하여 심층 신경망 작업을 쉽게 수행될 수 있도록 합니다.

      네트론에 대한 자세한 내용은 관련 문서를 참조하세요: Netron을 사용한 신경망Netron을 사용한 Keras 신경망.

      Netron 관련 동영상::





      ard_regression_float.onnx 모델은 그림 6에 나와 있습니다:

      그림 6. Netron에서 ard_regression_float.onnx 모델의 ONNX 표현

      그림 6. 네트론의 ard_regression_float.onnx 모델에 대한 ONNX 표현


      ai.onnx.ml LinearRegressor() ONNX 연산자는 ONNX 표준의 일부로 회귀 작업에 대한 모델을 설명합니다. 이 연산자는 입력 피처를 기반으로 숫자(연속형) 값을 예측하는 회귀에 사용됩니다.

      가중치, 바이어스 등 모델 파라미터와 입력 피처를 입력으로 받아 선형 회귀를 실행합니다. 선형 회귀는 각 입력 피처에 대한 매개변수(가중치)를 추정하고 이러한 피처와 가중치의 선형 조합을 수행하여 예측을 생성합니다.

      이 오퍼레이터는 다음 단계를 수행합니다:

      1. 입력 피처와 함께 모델의 가중치 및 편향성을 가져옵니다.
      2. 입력 데이터의 각 예에 대해 해당 피처와 가중치의 선형 조합을 수행합니다.
      3. 결과 값에 바이어스를 추가합니다.

      그 결과 회귀 작업에서 대상 변수를 예측할 수 있습니다.

      LinearRegressor() 매개변수는 그림 7에 나와 있습니다.

      그림 7. Netron에서 ard_regression_float.onnx 모델의 LinearRegressor() 연산자 속성

      그림 7. 네트론에서 ard_regression_float.onnx 모델의 LinearRegressor() 연산자 속성


      ard_regression_double.onnx ONNX 모델은 그림 8에 나와 있습니다:

      그림 8. Netron에서 ard_regression_double.onnx 모델의 ONNX 표현

      그림 8. 네트론에서 ard_regression_double.onnx 모델에 대한 ONNX 표현


      MatMul(), Add(), titlehttps://github.com/onnx/onnx/blob/main/docs/Operators.md#ReshapetitleReshape() ONNX 연산자의 매개변수는 그림 9-11에 나와 있습니다.

      그림 9: Netron의 ard_regression_double.onnx 모델에서 MatMul 연산자의 속성

      그림 9. 네트론의 ard_regression_double.onnx 모델에서 MatMul 연산자의 속성


      MatMul (행렬 곱셈) ONNX 연산자는 두 행렬의 곱셈을 수행하며

      두 개의 입력, 즉 두 개의 행렬을 받아 행렬의 곱을 반환합니다.

      만약 두 행렬 A와 B가 있는 경우 Matmul(A, B)의 결과는 행렬 C이며, 여기서 각 요소 C[i][j]는 행렬 A의 행 i에 있는 요소와 행렬 B의 열 j에 있는 요소의 곱의 합으로 계산됩니다.


      그림 10: Netron의 ard_regression_double.onnx 모델에서 Add 연산자의 속성

      그림 10. 네트론 ard_regression_double.onnx 모델에서 추가 연산자의 속성


      Add() ONNX 연산자는 같은 모양의 텐서 또는 배열 두 개를 요소 단위로 더하는 작업을 수행하며

      두 개의 입력을 받아 결과 텐서의 각 요소는 입력 텐서의 해당 요소의 합과 같은 결과를 반환합니다.


      그림 11. Netron의 ard_regression_double.onnx 모델에서 Reshape 연산자의 속성

      그림 11. 네트론의 ard_regression_double.onnx 모델에서 Reshape 연산자의 속성


      Reshape(-1,1) ONNX 연산자는 입력 데이터의 모양(또는 차원)을 수정하는 데 사용됩니다. 이 연산자에서 차원에 대한 값 -1은 데이터 일관성을 보장하기 위해 다른 차원을 기준으로 해당 차원의 크기가 자동으로 계산되어야 한다는 것을 나타냅니다.

      두 번째 차원의 값 1은 도형 변환 후 각 요소가 하나의 하위 차원을 갖도록 지정합니다.


      2.1.2. sklearn.linear_model.BayesianRidge

      BayesianRidge는 베이지안 접근 방식을 사용하여 모델 매개 변수를 추정하는 회귀 메서드입니다. 이 메서드를 사용하면 파라미터의 사전 분포를 모델링하고 데이터를 고려하여 업데이트하여 파라미터의 사후 분포를 얻을 수 있습니다.

      BayesianRidge는 하나 또는 여러 개의 독립 변수를 기반으로 종속 변수를 예측하도록 설계된 베이지안 회귀 메서드입니다.

      BayesianRidge의 작동 원리:

      1. 매개변수의 사전 배포: 모델 파라미터의 사전 분포를 정의하는 것으로 시작합니다. 이 분포는 데이터를 고려하기 전에 모델 매개변수에 대한 사전 지식 또는 가정을 나타냅니다. BayesianRidge의 경우 가우스 형태의 사전 분포가 사용됩니다.
      2. 매개변수 분포 업데이트하기: 사전 매개변수 분포가 설정되면 데이터를 기반으로 업데이트됩니다. 이는 데이터를 고려하여 매개변수의 사후 분포를 계산하는 베이지안 이론을 사용하여 수행됩니다. 필수적인 측면은 후행 분포의 형태에 영향을 미치는 하이퍼패러미터의 추정입니다.
      3. 예측: 매개변수의 사후 분포를 추정하고 나면 새로운 관측값에 대한 예측을 할 수 있습니다. 이렇게 하면 단일 포인트 값이 아닌 예측 분포가 생성되므로 예측의 불확실성을 고려할 수 있습니다.

      BayesianRidge의 장점:

      • 불확실성 고려: BayesianRidge는 모델 매개변수 및 예측의 불확실성을 설명합니다. 포인트 예측 대신 신뢰 구간이 제공됩니다.
      • 정규화: 베이지안 회귀 메서드는 모델 정규화에 유용하며 과적합을 방지하는 데 도움이 될 수 있습니다.
      • 자동 피처 선택: BayesianRidge는 중요하지 않은 피처의 가중치를 줄여 피처의 중요도를 자동으로 결정할 수 있습니다.

      BayesianRidge의 한계:

      • 계산의 복잡성: 이 메서드를 사용하려면 매개변수를 추정하고 사후 분포를 계산하는 데 계산 리소스가 필요합니다.
      • 높은 추상화 수준: 베이지안 리지를 이해하고 사용하려면 베이지안 통계에 대한 더 깊은 이해가 필요할 수 있으며
      • 항상 최선의 선택은 아닙니다: 특히 제한된 데이터를 다루는 경우 특정 회귀 작업에서는 BayesianRidge가 가장 적합한 메서드가 아닐 수도 있습니다.

      BayesianRidge는 매개변수 및 예측의 불확실성이 중요한 회귀 작업과 모델 정규화가 필요한 경우에 유용합니다.

      2.1.2.1. BayesianRidge 모델을 생성하고 float 및 double용 ONNX로 내보내기 위한 코드

      이 코드는 sklearn.linear_model.BayesianRidge 모델을 생성하고 합성 데이터로 학습시키고 모델을 ONNX 형식으로 저장하고 플로트 및 더블 입력 데이터를 모두 사용하여 예측을 수행합니다. 또한 원본 모델과 ONNX로 내보낸 모델 모두의 정확도를 평가합니다.

      # BayesianRidge.py
      # 이 코드는 BayesianRidge 모델을 학습하고, 이를 ONNX 형식(float 및 double 모두)으로 내보내고 ONNX 모델을 사용하여 예측하는 과정을 보여 줍니다.
      # Copyright 2023, MetaQuotes Ltd.
      # https://www.mql5.com


      # 소수점 이하 자릿수를 비교학 위한 함수
      def compare_decimal_places(value1, value2):
      # 두 값을 모두 문자열로 변환
          str_value1 = str(value1)
          str_value2 = str(value2)

      # 문자열에서 소수점 위치 찾기
          dot_position1 = str_value1.find(".")
          dot_position2 = str_value2.find(".")

      # 값 중 소수점이 없는 경우 0을 반환합니다.
          if dot_position1 == -1 or dot_position2 == -1:
              return 0

          # 소수점 이하 자릿수 계산
          decimal_places1 = len(str_value1) - dot_position1 - 1
          decimal_places2 = len(str_value2) - dot_position2 - 1

          # 소수점 이하 두 자리 수 중 최소값 찾기
          min_decimal_places = min(decimal_places1, decimal_places2)

          # 소수점 이하 자릿수가 일치하도록 카운트를 초기화
          matching_count = 0

          # 소수점 이하 문자 비교
          for i in range(1, min_decimal_places + 1):
              if str_value1[dot_position1 + i] == str_value2[dot_position2 + i]:
                  matching_count += 1
              else:
                  break

          return matching_count

      # 필요한 라이브러리 가져오기
      import numpy as np
      import matplotlib.pyplot as plt
      from sklearn.linear_model import BayesianRidge
      from sklearn.metrics import r2_score,mean_absolute_error,mean_squared_error
      import onnx
      import onnxruntime as ort
      from skl2onnx import convert_sklearn
      from skl2onnx.common.data_types import FloatTensorType
      from skl2onnx.common.data_types import DoubleTensorType
      from sys import argv

      # 모델 저장 경로를 정의합니다.
      data_path = argv[0]
      last_index = data_path.rfind("\\")+ 1
      data_path = data_path[0:last_index]

      # 회귀를 위한 합성 데이터 생성
      X = np.arange(0,100,1).reshape(-1,1)
      y = 4*X+ 10*np.sin(X*0.5)

      model_name = "BayesianRidge"
      onnx_model_filename = data_path + "bayesian_ridge"

      # BayesianRidge 회귀 모델 만들기
      regression_model = BayesianRidge()

      # 데이터에 모델 맞추기
      regression_model.fit(X, y.ravel())

      # 전체 데이터 집합에 대한 값 예측
      y_pred = regression_model.predict(X)

      # 모델의 성능 평가
      r2 = r2_score(y, y_pred)
      MSE = MEAN_SQUARE_ERROR(Y, Y_PED)
      MAE = MEAN_ABSOLUTE_ERROR(Y, Y_PED)

      print("\n"+model_name+" Original model (double)")
      print("R-squared (Coefficient of determination):", r2)
      print("Mean Absolute Error:", mae)
      print("Mean Squared Error:", mse)

      # ONNX 모델로 변환 (float)
      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.
      initial_type_float = [('float_input', FloatTensorType([None, X.shape[1]])]]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_float = convert_sklearn(regression_model, initial_types=initial_type_float, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_float.onnx"
      onnx.save_model(onnx_model_float, onnx_filename)

      print("\n"+model_name+" ONNX model (float)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.
      initial_type_float = X.astype(np.float32)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_float = onnx_session.run([output_name], {input_name: initial_type_float})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_float = r2_score(y, y_pred_onnx_float)
      mse_onnx_float = mean_squared_error(y, y_pred_onnx_float)
      mae_onnx_float = mean_absolute_error(y, y_pred_onnx_float)
      print("R-squared (Coefficient of determination)", r2_onnx_float)
      print("Mean Absolute Error:", mae_onnx_float)
      print("Mean Squared Error:", mse_onnx_float)
      print("R^2 matching decimal places: ", compare_decimal_places(r2, r2_onnx_float))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_float))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_float))
      print("float ONNX model precision: ",compare_decimal_places(mae, mae_onnx_float))

      # 그림 크기 설정
      plt.figure(figsize=(8,5))
      # 원본 데이터와 회귀 데이터를 플로팅합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with float ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_float.png')


      # ONNX 모델로 변환 (double)
      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.
      initial_type_double = [('double_input', DoubleTensorType([None, X.shape[1]]))]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_double = convert_sklearn(regression_model, initial_types=initial_type_double, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_double.onnx"
      onnx.save_model(onnx_model_double, onnx_filename)

      print("\n"+model_name+" ONNX model (double)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.
      initial_type_double = X.astype(np.float64)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_double = onnx_session.run([output_name], {input_name: initial_type_double})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_double = r2_score(y, y_pred_onnx_double)
      mse_onnx_double = mean_squared_error(y, y_pred_onnx_double)
      mae_onnx_double = mean_absolute_error(y, y_pred_onnx_double)
      print("R-squared (Coefficient of determination)", r2_onnx_double)
      print("Mean Absolute Error:", mae_onnx_double)
      print("Mean Squared Error:", mse_onnx_double)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_double))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_double))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_double))
      print("double ONNX model precision: ",compare_decimal_places(mae, mae_onnx_double))

      # 그림 크기 설정
      plt.figure(figsize=(8,5))
      # 원본 데이터와 회귀선을 그림으로 표시합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with double ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_double.png')

      출력:

      Python  BayesianRidge Original model (double)
      Python  R-squared (Coefficient of determination): 0.9962382628120845
      Python  Mean Absolute Error: 6.347568012853758
      Python  Mean Squared Error: 49.77815934891288
      Python  
      Python  BayesianRidge ONNX model (float)
      Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\bayesian_ridge_float.onnx
      Python  Information about input tensors in ONNX:
      Python  1. Name: float_input, Data Type: tensor(float), Shape: [None, 1]
      Python  Information about output tensors in ONNX:
      Python  1. Name: variable, Data Type: tensor(float), Shape: [None, 1]
      Python  R-squared (Coefficient of determination) 0.9962382627587808
      Python  Mean Absolute Error: 6.347568283744705
      Python  Mean Squared Error: 49.778160054267204
      Python  R^2 matching decimal places:  9
      Python  MAE matching decimal places:  6
      Python  MSE matching decimal places:  4
      Python  float ONNX model precision:  6
      Python  
      Python  BayesianRidge ONNX model (double)
      Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\bayesian_ridge_double.onnx
      Python  Information about input tensors in ONNX:
      Python  1. Name: double_input, Data Type: tensor(double), Shape: [None, 1]
      Python  Information about output tensors in ONNX:
      Python  1. Name: variable, Data Type: tensor(double), Shape: [None, 1]
      Python  R-squared (Coefficient of determination) 0.9962382628120845
      Python  Mean Absolute Error: 6.347568012853758
      Python  Mean Squared Error: 49.77815934891288
      Python  R^2 matching decimal places:  16
      Python  MAE matching decimal places:  15
      Python  MSE matching decimal places:  14
      Python  double ONNX model precision:  15
      

      Fig.12. BayesianRidge.py의 결과(float ONNX)

      그림 12. BayesianRidge.py의 결과(float ONNX)


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

      이 코드는 저장된 bayesian_ridge_float.onnxbayesian_ridge_double.onnx ONNX 모델을 실행하고 MQL5에서 회귀 메트릭을 사용하는 것을 보여 줍니다.

      //+------------------------------------------------------------------+
      //|                                                BayesianRidge.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"
      
      #define   ModelName          "BayesianRidge"
      #define   ONNXFilenameFloat  "bayesian_ridge_float.onnx"
      #define   ONNXFilenameDouble "bayesian_ridge_double.onnx"
      
      #resource ONNXFilenameFloat  as const uchar ExtModelFloat[];
      #resource ONNXFilenameDouble as const uchar ExtModelDouble[];
      
      #define   TestFloatModel  1
      #define   TestDoubleModel 2
      
      //+------------------------------------------------------------------+
      //| Calculate regression using float values                          |
      //+------------------------------------------------------------------+
      bool RunModelFloat(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         float input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=(float)input_vector[k];
      //--- prepare output tensor
         float output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      //+------------------------------------------------------------------+
      //| Calculate regression using double values                         |
      //+------------------------------------------------------------------+
      bool RunModelDouble(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         double input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=input_vector[k];
      //--- prepare output tensor
         double output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      
      //+------------------------------------------------------------------+
      //| Generate synthetic data                                          |
      //+------------------------------------------------------------------+
      bool GenerateData(const int n,vector &x,vector &y)
        {
         if(n<=0)
            return(false);
      //--- prepare arrays
         x.Resize(n);
         y.Resize(n);
      //---
         for(int i=0; i<n; i++)
           {
            x[i]=(double)1.0*i;
            y[i]=(double)(4*x[i] + 10*sin(x[i]*0.5));
           }
      //---
         return(true);
        }
      
      //+------------------------------------------------------------------+
      //| TestRegressionModel                                              |
      //+------------------------------------------------------------------+
      bool TestRegressionModel(const string model_name,const int model_type)
        {
      //---
         long  model=INVALID_HANDLE;
         ulong flags=ONNX_DEFAULT;
      
         if(model_type==TestFloatModel)
           {
            PrintFormat("\nTesting ONNX float: %s (%s)",model_name,ONNXFilenameFloat);
            model=OnnxCreateFromBuffer(ExtModelFloat,flags);
           }
         else
            if(model_type==TestDoubleModel)
              {
               PrintFormat("\nTesting ONNX double: %s (%s)",model_name,ONNXFilenameDouble);
               model=OnnxCreateFromBuffer(ExtModelDouble,flags);
              }
            else
              {
               PrintFormat("Model type is not incorrect.");
               return(false);
              }
      //--- check
         if(model==INVALID_HANDLE)
           {
            PrintFormat("model_name=%s OnnxCreate error %d",model_name,GetLastError());
            return(false);
           }
      //---
         vector x_values= {};
         vector y_true= {};
         vector y_predicted= {};
      //---
         int n=100;
         GenerateData(n,x_values,y_true);
      //---
         bool run_result=false;
         if(model_type==TestFloatModel)
           {
            run_result=RunModelFloat(model,x_values,y_predicted);
           }
         else
            if(model_type==TestDoubleModel)
              {
               run_result=RunModelDouble(model,x_values,y_predicted);
              }
      //---
         if(run_result)
           {
            PrintFormat("MQL5:   R-Squared (Coefficient of determination): %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_R2));
            PrintFormat("MQL5:   Mean Absolute Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MAE));
            PrintFormat("MQL5:   Mean Squared Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MSE));
           }
         else
            PrintFormat("Error %d",GetLastError());
      //--- release model
         OnnxRelease(model);
      //---
         return(true);
        }
      //+------------------------------------------------------------------+
      //| Script program start function                                    |
      //+------------------------------------------------------------------+
      int OnStart(void)
        {
      //--- test ONNX regression model for float
         TestRegressionModel(ModelName,TestFloatModel);
      //--- test ONNX regression model for double
         TestRegressionModel(ModelName,TestDoubleModel);
      //---
         return(0);
        }
      //+------------------------------------------------------------------+

      출력:

      BayesianRidge (EURUSD,H1)       Testing ONNX float: BayesianRidge (bayesian_ridge_float.onnx)
      BayesianRidge (EURUSD,H1)       MQL5:   R-Squared (Coefficient of determination): 0.9962382627587808
      BayesianRidge (EURUSD,H1)       MQL5:   Mean Absolute Error: 6.3475682837447049
      BayesianRidge (EURUSD,H1)       MQL5:   Mean Squared Error: 49.7781600542671896
      BayesianRidge (EURUSD,H1)       
      BayesianRidge (EURUSD,H1)       Testing ONNX double: BayesianRidge (bayesian_ridge_double.onnx)
      BayesianRidge (EURUSD,H1)       MQL5:   R-Squared (Coefficient of determination): 0.9962382628120845
      BayesianRidge (EURUSD,H1)       MQL5:   Mean Absolute Error: 6.3475680128537624
      BayesianRidge (EURUSD,H1)       MQL5:   Mean Squared Error: 49.7781593489128866
      

      파이썬에서 원래 double 모델과 비교:

      Testing ONNX float: BayesianRidge (bayesian_ridge_float.onnx)
      Python  Mean Absolute Error: 6.347568012853758
      MQL5:   Mean Absolute Error: 6.3475682837447049
      
      Testing ONNX double: BayesianRidge (bayesian_ridge_double.onnx)
      Python  Mean Absolute Error: 6.347568012853758
      MQL5:   Mean Absolute Error: 6.3475680128537624
      

      ONNX float MAE의 정확도: 소수점 이하 6자리, 정확도 ONNX double MAE: 소수점 이하 13자리.


      2.1.2.3. bayesian_ridge_float.onnx and bayesian_ridge_double.onnx의 ONNX 표현

      그림 13: Netron에서 bayesian_ridge_float.onnx의 ONNX 표현

      그림 13. 네트론에서 bayesian_ridge_float.onnx의 ONNX 표현



      그림 14. 넷트론에서 베이시안_릿지_더블.onnx의 ONNX 표현

      그림 14. 네트론에서 bayesian_ridge_double.onnx의 ONNX 표현


      ElasticNet 및 ElasticNetCV 메서드에 대한 참고 사항

      회귀 모델, 특히 선형 회귀를 정규화하는 데 사용되는 두 가지 관련 머신 러닝 메서드로는 ElasticNet과 ElasticNetCV가 있습니다. 이들은 서로 공통의 기능을 공유하지만 사용 및 적용 방식이 다릅니다.

          ElasticNet(엘라스틱 넷 회귀):

      • 작동 원리: ElasticNet은 Lasso(L1 정규화)와 Ridge(L2 정규화)를 결합한 회귀 메서드입니다. 하나는 올가미처럼 계수의 절대값이 큰 경우 모델에 불이익을 주고 다른 하나는 릿지처럼 계수의 제곱이 큰 경우 모델에 불이익을 주는 두 가지 정규화 구성 요소를 손실 함수에 추가합니다.
      • ElasticNet은 일반적으로 데이터에 다중 상관관계가 있는 경우(피처의 상관관계가 높은 경우)와 차원 축소 및 계수 값 제어가 필요한 경우에 사용됩니다.

          ElasticNetCV(Elastic Net 교차 검증):

      • 작동 원리: ElasticNetCV는 교차 검증을 사용하여 최적의 하이퍼파라미터 알파(L1과 L2 정규화 사이의 혼합 계수)와 람다(정규화 강도)를 자동으로 선택하는 ElasticNet의 확장 기능입니다. 다양한 알파 및 람다 값을 반복하여 교차하여 유효성 검사에서 가장 우수한 성능을 발휘하는 조합을 선택합니다.
      • 장점: ElasticNetCV는 교차 검증을 기반으로 모델 매개변수를 자동으로 조정하므로 수동으로 조정할 필요 없이 최적의 하이퍼파라미터 값을 선택할 수 있습니다. 이렇게 하면 더 편리하게 사용할 수 있고 모델의 과적합을 방지할 수 있습니다.

      따라서 ElasticNet과 ElasticNetCV의 가장 큰 차이점은 ElasticNet은 데이터에 적용되는 회귀 메서드인 반면 ElasticNetCV는 교차 검증을 사용하여 ElasticNet 모델에 대한 최적의 하이퍼파라미터 값을 자동으로 찾아주는 도구라는 점입니다. 최적의 모델 매개변수를 찾고 튜닝 프로세스를 더욱 자동화해야 할 때 ElasticNetCV가 유용합니다.


      2.1.3. sklearn.linear_model.ElasticNet

      ElasticNet은 L1(올가미)과 L2(릿지) 정규화의 조합을 나타내는 회귀 메서드입니다.

      이 메서드는 일련의 피처을 기반으로 대상 변수의 수치 값을 예측하는 회귀 분석에 사용됩니다. ElasticNet은 과적합을 제어하고 모델 계수에 대한 L1 및 L2 페널티를 모두 고려합니다.

      ElasticNet의 작동 원리:

      1. 입력 데이터: 피처(독립 변수)와 대상 변수의 해당 값이 있는 원본 데이터 세트에서 시작합니다.
      2. 목적 함수: ElasticNet은 평균 제곱 오차(MSE)와 두 개의 정규화라는 두 가지 구성 요소를 포함하는 손실 함수를 최소화합니다: L1(올가미) 및 L2(능선). 즉 목적 함수는 다음과 같습니다:
        Objective Function = MSE + α * L1 + β * L2
        여기서 α와 β는 각각 L1 및 L2 정규화의 가중치를 제어하는 하이퍼파라미터입니다.
      3. 최적의 α와 β 찾기: 교차 검증 메서드는 일반적으로 α와 β의 최적 값을 찾는 데 사용됩니다. 이를 통해 과적합을 줄이는 것과 필수 피처를 보존하는 것 사이에서 균형을 이루는 값을 선택할 수 있습니다.
      4. 모델 교육: ElasticNet은 목적 함수를 최소화하여 최적의 α와 β를 고려하며 모델을 학습시킵니다.
      5. 예측: 모델이 학습된 후에는 ElasticNet을 사용해 새로운 데이터의 목표 변수 값을 예측할 수 있습니다.

      ElasticNet의 장점:

      • 피처 선택 능력: ElasticNet은 중요하지 않은 피처에 대해서는 가중치를 0으로 설정하여 가장 중요한 피처를 자동으로 선택할 수 있습니다(Lasso와 유사).
      • 과적합 제어: ElasticNet을 사용하면 L1 및 L2 정규화로 인한 과적합을 제어할 수 있습니다.
      • 다연속성 다루기: 이 메서드는 다중 선형성(피처 간의 높은 상관관계)이 존재하는 경우 L2 정규화를 통해 다중 선형 피처의 영향을 줄일 수 있으므로 유용합니다.

      ElasticNet의 한계:

      • 하이퍼파라미터 α와 β의 조정이 필요한데 이는 간단한 작업이 아닐 수 있습니다.
      • 매개변수 선택에 따라 ElasticNet은 너무 적거나 너무 많은 피처를 유지하게 되고 이에 따라 모델의 품질에 영향을 미칠 수 있습니다.

      ElasticNet은 피처 선택과 과적합 제어가 중요한 작업에서 유용할 수 있는 강력한 회귀 메서드입니다.


      2.1.3.1. ElasticNet 모델을 생성하고 float 및 double 용의 ONNX로 내보내는 코드

      이 코드는 sklearn.linear_model.ElasticNet 모델을 생성하고 합성 데이터로 학습시키고 모델을 ONNX 형식으로 저장하고 플로트 및 더블 입력 데이터를 모두 사용하여 예측을 수행합니다. 또한 원본 모델과 ONNX로 내보낸 모델 모두의 정확도를 평가합니다.

      # ElasticNet.py
      # 이 코드는 ElasticNet 모델을 학습하고 이를 ONNX 형식(float 및 double)으로 내보내고 ONNX 모델을 사용하여 예측하는 과정을 보여줍니다.
      # Copyright 2023, MetaQuotes Ltd.
      # https://www.mql5.com


      # 소수점 이하 자릿수를 비교학 위한 함수
      def compare_decimal_places(value1, value2):
      # 두 값을 모두 문자열로 변환
          str_value1 = str(value1)
          str_value2 = str(value2)

      # 문자열에서 소수점 위치 찾기
          dot_position1 = str_value1.find(".")
          dot_position2 = str_value2.find(".")

      # 값 중 소수점이 없는 경우 0을 반환합니다.
          if dot_position1 == -1 or dot_position2 == -1:
              return 0

          # 소수점 이하 자릿수 계산
          decimal_places1 = len(str_value1) - dot_position1 - 1
          decimal_places2 = len(str_value2) - dot_position2 - 1

          # 소수점 이하 두 자리 수 중 최소값 찾기
          min_decimal_places = min(decimal_places1, decimal_places2)

          # 소수점 이하 자릿수가 일치하도록 카운트를 초기화
          matching_count = 0

          # 소수점 이하 문자 비교
          for i in range(1, min_decimal_places + 1):
              if str_value1[dot_position1 + i] == str_value2[dot_position2 + i]:
                  matching_count += 1
              else:
                  break

          return matching_count

      # 필요한 라이브러리 가져오기
      import numpy as np
      import matplotlib.pyplot as plt
      from sklearn.linear_model import ElasticNet
      from sklearn.metrics import r2_score,mean_absolute_error,mean_squared_error
      import onnx
      import onnxruntime as ort
      from skl2onnx import convert_sklearn
      from skl2onnx.common.data_types import FloatTensorType
      from skl2onnx.common.data_types import DoubleTensorType
      from sys import argv

      # 모델 저장 경로를 정의합니다.
      data_path = argv[0]
      last_index = data_path.rfind("\\")+ 1
      data_path = data_path[0:last_index]

      # 회귀를 위한 합성 데이터 생성
      X = np.arange(0,100,1).reshape(-1,1)
      y = 4*X + 10*np.sin(X*0.5)

      model_name = "ElasticNet"
      onnx_model_filename = data_path + "elastic_net"

      # ElasticNet 모델 생성
      regression_model = ElasticNet()

      # 데이터에 모델 맞추기
      regression_model.fit(X,y)

      # 전체 데이터 집합에 대한 값 예측
      y_pred = regression_model.predict(X)

      # 모델의 성능 평가
      r2 = r2_score(y, y_pred)
      MSE = MEAN_SQUARE_ERROR(Y, Y_PED)
      MAE = MEAN_ABSOLUTE_ERROR(Y, Y_PED)

      print("\n"+model_name+" Original model (double)")
      print("R-squared (Coefficient of determination):", r2)
      print("Mean Absolute Error:", mae)
      print("Mean Squared Error:", mse)

      # ONNX 모델로 변환 (float)
      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.

      initial_type_float = [('float_input', FloatTensorType([None, X.shape[1]])]]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_float = convert_sklearn(regression_model, initial_types=initial_type_float, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_float.onnx"
      onnx.save_model(onnx_model_float, onnx_filename)

      print("\n"+model_name+" ONNX model (float)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.
      initial_type_float = X.astype(np.float32)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_float = onnx_session.run([output_name], {input_name: initial_type_float})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_float = r2_score(y, y_pred_onnx_float)
      mse_onnx_float = mean_squared_error(y, y_pred_onnx_float)
      mae_onnx_float = mean_absolute_error(y, y_pred_onnx_float)
      print("R-squared (Coefficient of determination)", r2_onnx_float)
      print("Mean Absolute Error:", mae_onnx_float)
      print("Mean Squared Error:", mse_onnx_float)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_float))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_float))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_float))
      print("float ONNX model precision: ",compare_decimal_places(mae, mae_onnx_float))

      # 그림 크기 설정
      plt.figure(figsize=(8,5))
      # 원본 데이터와 회귀선을 그림으로 표시합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with float ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_float.png')

      # ONNX 모델로 변환 (double)
      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.

      initial_type_double = [('double_input', DoubleTensorType([None, X.shape[1]]))]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_double = convert_sklearn(regression_model, initial_types=initial_type_double, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_double.onnx"
      onnx.save_model(onnx_model_double, onnx_filename)

      print("\n"+model_name+" ONNX model (double)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.
      initial_type_double = X.astype(np.float64)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_double = onnx_session.run([output_name], {input_name: initial_type_double})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_double = r2_score(y, y_pred_onnx_double)
      mse_onnx_double = mean_squared_error(y, y_pred_onnx_double)
      mae_onnx_double = mean_absolute_error(y, y_pred_onnx_double)
      print("R-squared (Coefficient of determination)", r2_onnx_double)
      print("Mean Absolute Error:", mae_onnx_double)
      print("Mean Squared Error:", mse_onnx_double)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_double))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_double))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_double))
      print("double ONNX model precision: ",compare_decimal_places(mae, mae_onnx_double))

      # 그림 크기 설정
      plt.figure(figsize=(8,5))
      # 원본 데이터와 회귀선을 그림으로 표시합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with double ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_double.png')

      출력:

      Python  ElasticNet Original model (double)
      Python  R-squared (Coefficient of determination): 0.9962377031744798
      Python  Mean Absolute Error: 6.344394662876524
      Python  Mean Squared Error: 49.78556489812415
      Python  
      Python  ElasticNet ONNX model (float)
      Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\elastic_net_float.onnx
      Python  Information about input tensors in ONNX:
      Python  1. Name: float_input, Data Type: tensor(float), Shape: [None, 1]
      Python  Information about output tensors in ONNX:
      Python  1. Name: variable, Data Type: tensor(float), Shape: [None, 1]
      Python  R-squared (Coefficient of determination) 0.9962377032416807
      Python  Mean Absolute Error: 6.344395027824294
      Python  Mean Squared Error: 49.78556400887057
      Python  R^2 matching decimal places:  9
      Python  MAE matching decimal places:  5
      Python  MSE matching decimal places:  6
      Python  float ONNX model precision:  5
      Python  
      Python  ElasticNet ONNX model (double)
      Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\elastic_net_double.onnx
      Python  Information about input tensors in ONNX:
      Python  1. Name: double_input, Data Type: tensor(double), Shape: [None, 1]
      Python  Information about output tensors in ONNX:
      Python  1. Name: variable, Data Type: tensor(double), Shape: [None, 1]
      Python  R-squared (Coefficient of determination) 0.9962377031744798
      Python  Mean Absolute Error: 6.344394662876524
      Python  Mean Squared Error: 49.78556489812415
      Python  R^2 matching decimal places:  16
      Python  MAE matching decimal places:  15
      Python  MSE matching decimal places:  14
      Python  double ONNX model precision:  15
      

      그림 15. ElasticNet.py (float ONNX) 결과

      그림 15. ElasticNet.py의 결과(float ONNX)


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

      이 코드는 저장된 elastic_net_double.onnxelastic_net_float.onnx 모델을 실행하고 MQL5에서 회귀 메트릭을 사용하는 것을 보여줍니다.

      //+------------------------------------------------------------------+
      //|                                                   ElasticNet.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"
      
      #define   ModelName          "ElasticNet"
      #define   ONNXFilenameFloat  "elastic_net_float.onnx"
      #define   ONNXFilenameDouble "elastic_net_double.onnx"
      
      #resource ONNXFilenameFloat  as const uchar ExtModelFloat[];
      #resource ONNXFilenameDouble as const uchar ExtModelDouble[];
      
      #define   TestFloatModel  1
      #define   TestDoubleModel 2
      
      //+------------------------------------------------------------------+
      //| Calculate regression using float values                          |
      //+------------------------------------------------------------------+
      bool RunModelFloat(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         float input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=(float)input_vector[k];
      //--- prepare output tensor
         float output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      //+------------------------------------------------------------------+
      //| Calculate regression using double values                         |
      //+------------------------------------------------------------------+
      bool RunModelDouble(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         double input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=input_vector[k];
      //--- prepare output tensor
         double output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      
      //+------------------------------------------------------------------+
      //| Generate synthetic data                                          |
      //+------------------------------------------------------------------+
      bool GenerateData(const int n,vector &x,vector &y)
        {
         if(n<=0)
            return(false);
      //--- prepare arrays
         x.Resize(n);
         y.Resize(n);
      //---
         for(int i=0; i<n; i++)
           {
            x[i]=(double)1.0*i;
            y[i]=(double)(4*x[i] + 10*sin(x[i]*0.5));
           }
      //---
         return(true);
        }
      
      //+------------------------------------------------------------------+
      //| TestRegressionModel                                              |
      //+------------------------------------------------------------------+
      bool TestRegressionModel(const string model_name,const int model_type)
        {
      //---
         long  model=INVALID_HANDLE;
         ulong flags=ONNX_DEFAULT;
      
         if(model_type==TestFloatModel)
           {
            PrintFormat("\nTesting ONNX float: %s (%s)",model_name,ONNXFilenameFloat);
            model=OnnxCreateFromBuffer(ExtModelFloat,flags);
           }
         else
            if(model_type==TestDoubleModel)
              {
               PrintFormat("\nTesting ONNX double: %s (%s)",model_name,ONNXFilenameDouble);
               model=OnnxCreateFromBuffer(ExtModelDouble,flags);
              }
            else
              {
               PrintFormat("Model type is not incorrect.");
               return(false);
              }
      //--- check
         if(model==INVALID_HANDLE)
           {
            PrintFormat("model_name=%s OnnxCreate error %d",model_name,GetLastError());
            return(false);
           }
      //---
         vector x_values= {};
         vector y_true= {};
         vector y_predicted= {};
      //---
         int n=100;
         GenerateData(n,x_values,y_true);
      //---
         bool run_result=false;
         if(model_type==TestFloatModel)
           {
            run_result=RunModelFloat(model,x_values,y_predicted);
           }
         else
            if(model_type==TestDoubleModel)
              {
               run_result=RunModelDouble(model,x_values,y_predicted);
              }
      //---
         if(run_result)
           {
            PrintFormat("MQL5:   R-Squared (Coefficient of determination): %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_R2));
            PrintFormat("MQL5:   Mean Absolute Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MAE));
            PrintFormat("MQL5:   Mean Squared Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MSE));
           }
         else
            PrintFormat("Error %d",GetLastError());
      //--- release model
         OnnxRelease(model);
      //---
         return(true);
        }
      //+------------------------------------------------------------------+
      //| Script program start function                                    |
      //+------------------------------------------------------------------+
      int OnStart(void)
        {
      //--- test ONNX regression model for float
         TestRegressionModel(ModelName,TestFloatModel);
      //--- test ONNX regression model for double
         TestRegressionModel(ModelName,TestDoubleModel);
      //---
         return(0);
        }
      //+------------------------------------------------------------------+

      출력:

      ElasticNet (EURUSD,H1)  Testing ONNX float: ElasticNet (elastic_net_float.onnx)
      ElasticNet (EURUSD,H1)  MQL5:   R-Squared (Coefficient of determination): 0.9962377032416807
      ElasticNet (EURUSD,H1)  MQL5:   Mean Absolute Error: 6.3443950278242944
      ElasticNet (EURUSD,H1)  MQL5:   Mean Squared Error: 49.7855640088705869
      ElasticNet (EURUSD,H1)  
      ElasticNet (EURUSD,H1)  Testing ONNX double: ElasticNet (elastic_net_double.onnx)
      ElasticNet (EURUSD,H1)  MQL5:   R-Squared (Coefficient of determination): 0.9962377031744798
      ElasticNet (EURUSD,H1)  MQL5:   Mean Absolute Error: 6.3443946628765220
      ElasticNet (EURUSD,H1)  MQL5:   Mean Squared Error: 49.7855648981241217
      

      파이썬에서 원래 double 모델과 비교:

      Testing ONNX float: ElasticNet (elastic_net_float.onnx)
      Python  Mean Absolute Error: 6.344394662876524
      MQL5:   Mean Absolute Error: 6.3443950278242944
        
      Testing ONNX double: ElasticNet (elastic_net_double.onnx)
      Python  Mean Absolute Error: 6.344394662876524
      MQL5:   Mean Absolute Error: 6.3443946628765220
      

      ONNX float MAE의 정확도: 소수점 이하 5자리, 정확도 ONNX 더블 MAE: 소수점 이하 14자리.


      2.1.3.3. elastic_net_float.onnx 및 elastic_net_double.onnx의 ONNX 표현


      Fig.16. Netron에서 elastic_net_float.onnx의 ONNX 표현

      그림 16. 네트론에서 elastic_net_float.onnx의 ONNX 표현



      Fig.17. Netron에서 elastic_net_double.onnx의 ONNX 표현

      그림 17. 네트론에서 elastic_net_double.onnx의 ONNX 표현


      2.1.4. sklearn.linear_model.ElasticNetCV

      ElasticNetCV은 교차 검증을 사용하여 하이퍼파라미터 α 및 β(L1 및 L2 정규화)의 최적 값을 자동으로 선택하도록 설계된 ElasticNet 메서드의 확장입니다.

      이를 통해 수동으로 매개변수를 조정할 필요 없이 ElasticNet 모델에 가장 적합한 정규화 조합을 찾을 수 있습니다.

      ElasticNetCV의 작동 원리:

      1. 입력 데이터: 피처(독립 변수)와 그에 해당하는 목표 변수 값이 포함된 원본 데이터 집합으로 시작합니다.
      2. α 및 β 범위 정의하기: 사용자는 최적화 중에 고려할 α와 β의 값의 범위를 지정합니다. 이러한 값은 일반적으로 로그 스케일로 선택됩니다.
      3. 데이터 분할: 데이터 세트는 교차 검증을 위해 여러 번 분할됩니다. 각 폴드는 테스트 데이터세트로 사용되고 다른 폴드는 학습에 사용됩니다.
      4. 교차 유효성 검사: 지정된 범위 내에서 α와 β의 각 조합에 대해 교차 유효성 검사가 수행됩니다. 학습 데이터에 대해 ElasticNet 모델을 학습한 다음 테스트 데이터에 대해 평가합니다.
      5. 성능 평가: 교차 검증에서 테스트 데이터 세트의 평균 오류는 각 α 및 β 조합에 대해 계산됩니다.
      6. 최적의 매개변수 선택: 교차 검증 중에 얻은 최소 평균 오류에 해당하는 α 및 β의 값이 결정됩니다.
      7. 최적의 매개변수로 모델 학습: 발견된 α와 β의 최적 값을 사용하여 ElasticNetCV 모델을 학습합니다.
      8. 예측: 학습 후에는 이 모델을 사용하여 새로운 데이터의 목표 변수 값을 예측할 수 있습니다.

      ElasticNetCV의 장점:

      • 자동 하이퍼파라미터 선택: ElasticNetCV는 α와 β의 최적 값을 자동으로 찾아내어 모델 튜닝을 간소화합니다.
      • 과적합 방지: 교차 검증은 일반화 능력이 우수한 모델을 선택하는 데 도움이 됩니다.
      • 노이즈 견고성: 이 메서드은 데이터 노이즈에 강하며 노이즈를 고려하면서 최적의 정규화 조합을 식별할 수 있습니다.

      ElasticNetCV의 한계:

      • 계산의 복잡성: 매개변수가 커다란 범위에 걸쳐 있는 교차 검증을 수행하려면 시간이 많이 소요될 수 있습니다.
      • 최적의 매개변수는 범위 선택에 알려 있습니다: α 및 β 범위의 선택에 따라 결과가 달라질 수 있으므로 이 범위를 신중하게 조정하는 것이 중요합니다.

      ElasticNetCV는 ElasticNet 모델에서 정규화를 자동으로 조정하고 성능을 향상시키기 위한 강력한 도구입니다.


      2.1.4.1. ElasticNetCV 모델을 생성하고 float 및 double 용의 ONNX로 내보내는 코드

      이 코드는 sklearn.linear_model.ElasticNetCV 모델을 생성하고 합성 데이터로 학습시키고 모델을 ONNX 형식으로 저장하고 플로트 및 더블 입력 데이터를 모두 사용하여 예측을 수행합니다. 또한 원본 모델과 ONNX로 내보낸 모델 모두의 정확도를 평가합니다.

      # ElasticNetCV.py
      # 이 코드는 ElasticNetCV 모델을 학습하고 이를 ONNX 형식(float 및 double)으로 내보내고 ONNX 모델을 사용하여 예측하는 프로세스를 보여 줍니다.
      # Copyright 2023, MetaQuotes Ltd.
      # https://www.mql5.com


      # 소수점 이하 자릿수를 비교학 위한 함수
      def compare_decimal_places(value1, value2):
      # 두 값을 모두 문자열로 변환
          str_value1 = str(value1)
          str_value2 = str(value2)

      # 문자열에서 소수점 위치 찾기
          dot_position1 = str_value1.find(".")
          dot_position2 = str_value2.find(".")

      # 값 중 소수점이 없는 경우 0을 반환합니다.
          if dot_position1 == -1 or dot_position2 == -1:
              return 0

          # 소수점 이하 자릿수 계산
          decimal_places1 = len(str_value1) - dot_position1 - 1
          decimal_places2 = len(str_value2) - dot_position2 - 1

          # 소수점 이하 두 자리 수 중 최소값 찾기
          min_decimal_places = min(decimal_places1, decimal_places2)

          # 소수점 이하 자릿수가 일치하도록 카운트를 초기화
          matching_count = 0

          # 소수점 이하 문자 비교
          for i in range(1, min_decimal_places + 1):
              if str_value1[dot_position1 + i] == str_value2[dot_position2 + i]:
                  matching_count += 1
              else:
                  break

          return matching_count

      # 필요한 라이브러리 가져오기
      import numpy as np
      import matplotlib.pyplot as plt
      from sklearn.linear_model import ElasticNetCV
      from sklearn.metrics import r2_score,mean_absolute_error,mean_squared_error
      import onnx
      import onnxruntime as ort
      from skl2onnx import convert_sklearn
      from skl2onnx.common.data_types import FloatTensorType
      from skl2onnx.common.data_types import DoubleTensorType
      from sys import argv

      # 모델 저장 경로를 정의합니다.
      data_path = argv[0]
      last_index = data_path.rfind("\\")+ 1
      data_path = data_path[0:last_index]

      # 회귀를 위한 합성 데이터 생성
      X = np.arange(0,100,1).reshape(-1,1)
      y = 4*X+ 10*np.sin(X*0.5)

      model_name = "ElasticNetCV"
      onnx_model_filename = data_path + "elastic_net_cv"

      # ElasticNetCV 모델 생성하기
      regression_model = ElasticNetCV()

      # 데이터에 모델 맞추기
      regression_model.fit(X, y.ravel())

      # 전체 데이터 집합에 대한 값 예측
      y_pred = regression_model.predict(X)

      # 모델의 성능 평가
      r2 = r2_score(y, y_pred)
      MSE = MEAN_SQUARE_ERROR(Y, Y_PED)
      MAE = MEAN_ABSOLUTE_ERROR(Y, Y_PED)

      print("\n"+model_name+" Original model (double)")
      print("R-squared (Coefficient of determination):", r2)
      print("Mean Absolute Error:", mae)
      print("Mean Squared Error:", mse)

      # ONNX 모델로 변환 (float)
      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.

      initial_type_float = [('float_input', FloatTensorType([None, X.shape[1]])]]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_float = convert_sklearn(regression_model, initial_types=initial_type_float, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_float.onnx"
      onnx.save_model(onnx_model_float, onnx_filename)

      print("\n"+model_name+" ONNX model (float)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.
      initial_type_float = X.astype(np.float32)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_float = onnx_session.run([output_name], {input_name: initial_type_float})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_float = r2_score(y, y_pred_onnx_float)
      mse_onnx_float = mean_squared_error(y, y_pred_onnx_float)
      mae_onnx_float = mean_absolute_error(y, y_pred_onnx_float)
      print("R-squared (Coefficient of determination)", r2_onnx_float)
      print("Mean Absolute Error:", mae_onnx_float)
      print("Mean Squared Error:", mse_onnx_float)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_float))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_float))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_float))
      print("float ONNX model precision: ",compare_decimal_places(mae, mae_onnx_float))

      # 그림 크기 설정
      plt.figure(figsize=(8, 5))
      # 원본 데이터와 회귀선을 그림으로 표시합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with float ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_float.png')

      # ONNX 모델로 변환 (double)
      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.

      initial_type_double = [('double_input', DoubleTensorType([None, X.shape[1]]))]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_double = convert_sklearn(regression_model, initial_types=initial_type_double, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_double.onnx"
      onnx.save_model(onnx_model_double, onnx_filename)

      print("\n"+model_name+" ONNX model (double)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.
      initial_type_double = X.astype(np.float64)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_double = onnx_session.run([output_name], {input_name: initial_type_double})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_double = r2_score(y, y_pred_onnx_double)
      mse_onnx_double = mean_squared_error(y, y_pred_onnx_double)
      mae_onnx_double = mean_absolute_error(y, y_pred_onnx_double)
      print("R-squared (Coefficient of determination)", r2_onnx_double)
      print("Mean Absolute Error:", mae_onnx_double)
      print("Mean Squared Error:", mse_onnx_double)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_double))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_double))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_double))
      print("double ONNX model precision: ",compare_decimal_places(mae, mae_onnx_double))

      # 그림 크기 설정
      plt.figure(figsize=(8,5))
      # 원본 데이터와 회귀선을 그림으로 표시합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with double ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_double.png')

      출력:

      Python  ElasticNetCV Original model (double)
      Python  R-squared (Coefficient of determination): 0.9962137763338385
      Python  Mean Absolute Error: 6.334487104423225
      Python  Mean Squared Error: 50.10218299945999
      Python  
      Python  ElasticNetCV ONNX model (float)
      Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\elastic_net_cv_float.onnx
      Python  Information about input tensors in ONNX:
      Python  1. Name: float_input, Data Type: tensor(float), Shape: [None, 1]
      Python  Information about output tensors in ONNX:
      Python  1. Name: variable, Data Type: tensor(float), Shape: [None, 1]
      Python  R-squared (Coefficient of determination) 0.9962137770260989
      Python  Mean Absolute Error: 6.334486542922601
      Python  Mean Squared Error: 50.10217383894468
      Python  R^2 matching decimal places:  8
      Python  MAE matching decimal places:  5
      Python  MSE matching decimal places:  4
      Python  float ONNX model precision:  5
      Python  
      Python  ElasticNetCV ONNX model (double)
      Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\elastic_net_cv_double.onnx
      Python  Information about input tensors in ONNX:
      Python  1. Name: double_input, Data Type: tensor(double), Shape: [None, 1]
      Python  Information about output tensors in ONNX:
      Python  1. Name: variable, Data Type: tensor(double), Shape: [None, 1]
      Python  R-squared (Coefficient of determination) 0.9962137763338385
      Python  Mean Absolute Error: 6.334487104423225
      Python  Mean Squared Error: 50.10218299945999
      Python  R^2 matching decimal places:  16
      Python  MAE matching decimal places:  15
      Python  MSE matching decimal places:  14
      Python  double ONNX model precision:  15
      

      < 결과그림 18. ElasticNetCV.py (float ONNX) 결과

      그림 18. ElasticNetCV.py의 결과(float ONNX)


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

      이 코드는 저장된 elastic_net_cv_float.onnxelastic_net_cv_double.onnx ONNX 모델을 실행하고 MQL5에서 회귀 메트릭을 사용하는 것을 시연합니다.

      //+------------------------------------------------------------------+
      //|                                                 ElasticNetCV.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"
      
      #define   ModelName          "ElasticNetCV"
      #define   ONNXFilenameFloat  "elastic_net_cv_float.onnx"
      #define   ONNXFilenameDouble "elastic_net_cv_double.onnx"
      
      #resource ONNXFilenameFloat  as const uchar ExtModelFloat[];
      #resource ONNXFilenameDouble as const uchar ExtModelDouble[];
      
      #define   TestFloatModel  1
      #define   TestDoubleModel 2
      
      //+------------------------------------------------------------------+
      //| Calculate regression using float values                          |
      //+------------------------------------------------------------------+
      bool RunModelFloat(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         float input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=(float)input_vector[k];
      //--- prepare output tensor
         float output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      //+------------------------------------------------------------------+
      //| Calculate regression using double values                         |
      //+------------------------------------------------------------------+
      bool RunModelDouble(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         double input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=input_vector[k];
      //--- prepare output tensor
         double output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      
      //+------------------------------------------------------------------+
      //| Generate synthetic data                                          |
      //+------------------------------------------------------------------+
      bool GenerateData(const int n,vector &x,vector &y)
        {
         if(n<=0)
            return(false);
      //--- prepare arrays
         x.Resize(n);
         y.Resize(n);
      //---
         for(int i=0; i<n; i++)
           {
            x[i]=(double)1.0*i;
            y[i]=(double)(4*x[i] + 10*sin(x[i]*0.5));
           }
      //---
         return(true);
        }
      
      //+------------------------------------------------------------------+
      //| TestRegressionModel                                              |
      //+------------------------------------------------------------------+
      bool TestRegressionModel(const string model_name,const int model_type)
        {
      //---
         long  model=INVALID_HANDLE;
         ulong flags=ONNX_DEFAULT;
      
         if(model_type==TestFloatModel)
           {
            PrintFormat("\nTesting ONNX float: %s (%s)",model_name,ONNXFilenameFloat);
            model=OnnxCreateFromBuffer(ExtModelFloat,flags);
           }
         else
            if(model_type==TestDoubleModel)
              {
               PrintFormat("\nTesting ONNX double: %s (%s)",model_name,ONNXFilenameDouble);
               model=OnnxCreateFromBuffer(ExtModelDouble,flags);
              }
            else
              {
               PrintFormat("Model type is not incorrect.");
               return(false);
              }
      //--- check
         if(model==INVALID_HANDLE)
           {
            PrintFormat("model_name=%s OnnxCreate error %d",model_name,GetLastError());
            return(false);
           }
      //---
         vector x_values= {};
         vector y_true= {};
         vector y_predicted= {};
      //---
         int n=100;
         GenerateData(n,x_values,y_true);
      //---
         bool run_result=false;
         if(model_type==TestFloatModel)
           {
            run_result=RunModelFloat(model,x_values,y_predicted);
           }
         else
            if(model_type==TestDoubleModel)
              {
               run_result=RunModelDouble(model,x_values,y_predicted);
              }
      //---
         if(run_result)
           {
            PrintFormat("MQL5:   R-Squared (Coefficient of determination): %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_R2));
            PrintFormat("MQL5:   Mean Absolute Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MAE));
            PrintFormat("MQL5:   Mean Squared Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MSE));
           }
         else
            PrintFormat("Error %d",GetLastError());
      //--- release model
         OnnxRelease(model);
      //---
         return(true);
        }
      //+------------------------------------------------------------------+
      //| Script program start function                                    |
      //+------------------------------------------------------------------+
      int OnStart(void)
        {
      //--- test ONNX regression model for float
         TestRegressionModel(ModelName,TestFloatModel);
      //--- test ONNX regression model for double
         TestRegressionModel(ModelName,TestDoubleModel);
      //---
         return(0);
        }
      //+------------------------------------------------------------------+

      출력:

      ElasticNetCV (EURUSD,H1)        Testing ONNX float: ElasticNetCV (elastic_net_cv_float.onnx)
      ElasticNetCV (EURUSD,H1)        MQL5:   R-Squared (Coefficient of determination): 0.9962137770260989
      ElasticNetCV (EURUSD,H1)        MQL5:   Mean Absolute Error: 6.3344865429226038
      ElasticNetCV (EURUSD,H1)        MQL5:   Mean Squared Error: 50.1021738389446938
      ElasticNetCV (EURUSD,H1)        
      ElasticNetCV (EURUSD,H1)        Testing ONNX double: ElasticNetCV (elastic_net_cv_double.onnx)
      ElasticNetCV (EURUSD,H1)        MQL5:   R-Squared (Coefficient of determination): 0.9962137763338385
      ElasticNetCV (EURUSD,H1)        MQL5:   Mean Absolute Error: 6.3344871044232205
      ElasticNetCV (EURUSD,H1)        MQL5:   Mean Squared Error: 50.1021829994599983
      

      파이썬에서 원래 double 모델과 비교:

      Testing ONNX float: ElasticNetCV (elastic_net_cv_float.onnx)
      Python  Mean Absolute Error: 6.334487104423225
      MQL5:   Mean Absolute Error: 6.3344865429226038
      
      Testing ONNX double: ElasticNetCV (elastic_net_cv_double.onnx)
      Python  Mean Absolute Error: 6.334487104423225
      MQL5:   Mean Absolute Error: 6.3344871044232205

      ONNX float MAE의 정확도: 소수점 이하 5자리, 정확도 ONNX 더블 MAE: 소수점 이하 14자리.


      2.1.4.3. elastic_net_cv_float.onnx 및 elastic_net_cv_double.onnx의 ONNX 표현

      Fig.19. 넷트론에서 elastic_net_cv_float.onnx의 ONNX 표현

      그림 19. 네트론에서 elastic_net_cv_float.onnx의 ONNX 표현


      Fig.20. 네트론에서 elastic_net_cv_double.onnx의 ONNX 표현

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



      2.1.5. sklearn.linear_model.HuberRegressor

      HuberRegressor - 회귀 작업에 사용되는 머신 러닝 메서드입니다. 최소자승법(OLS)을 수정한 것으로 데이터의 이상값에 강하도록 설계되었습니다.


      오차의 제곱을 최소화하는 OLS와 달리 HuberRegressor는 제곱 오차와 절대 오차의 조합을 최소화합니다. 이를 통해 데이터에 이상값이 있는 경우 메서드가 더욱 강력하게 작동할 수 있습니다.

      HuberRegressor의 작동 원리:

      1. 입력 데이터: 피처(독립 변수()와 그에 해당하는 목표 변수 값이 있는 원본 데이터 세트에서 시작합니다.
      2. 후버 손실 함수: HuberRegressor는 작은 오류에 대한 이차적 손실 함수와 큰 오류에 대한 선형적 손실 함수를 결합한 Huber 손실 함수를 활용합니다. 이렇게 하면 이상값에 대해 메서드의 복원력이 높아집니다.
      3. 모델 교육: 모델은 후버 손실 함수를 사용하여 데이터에 대해 학습합니다. 학습하는 동안 각 피처의 가중치(계수)와 편향성을 조정합니다.
      4. 예측: 학습 후에는 이 모델을 사용하여 새로운 데이터의 목표 변수 값을 예측할 수 있습니다.

      HuberRegressor의 장점:

      • 이상값에 대한 견고성: HuberRegressor는 OLS에 비해 데이터의 이상값에 더 강력하므로 데이터에 비정상적인 값이 포함될 수 있는 작업에 유용합니다.
      • 오류 추정: 후버 손실 함수는 모델 결과를 분석하는 데 유용한 예측 오류를 추정하는 데 기여합니다.
      • 정규화 수준: HuberRegressor는 또한 일정 수준의 정규화를 통합하여 과적합을 줄일 수 있습니다.

      HuberRegressor의 한계:

      • 이상값이 없는 경우 OLS만큼 정확하지 않습니다: 데이터에 이상값이 없는 경우 OLS가 더 정확한 결과를 제공할 수 있습니다.
      • 매개변수 조정: HuberRegressor에는 선형 손실 함수로 전환하기 위해 "큰" 것으로 간주되는 임계값을 정의하는 매개변수가 있습니다. 이 매개변수는 조정이 필요합니다.

      HuberRegressor는 데이터에 이상값이 포함될 수 있는 회귀 작업에서 유용하며 이러한 이상값에 강한 모델이 필요합니다.


      2.1.5.1. HuberRegressor 모델을 생성하고 float 및 double용 ONNX로 내보내기 위한 코드

      이 코드는 sklearn.linear_model.HuberRegressor 모델을 생성하고 합성 데이터로 학습시키고 모델을 ONNX 형식으로 저장하고 플로트 및 더블 입력 데이터를 모두 사용하여 예측을 수행합니다. 또한 원본 모델과 ONNX로 내보낸 모델 모두의 정확도를 평가합니다.

      # HuberRegressor.py
      # 이 코드는 HuberRegressor 모델을 학습하고 이를 ONNX 형식(float 및 double 모두)으로 내보내고 ONNX 모델을 사용하여 예측하는 과정을 보여 줍니다.
      # Copyright 2023, MetaQuotes Ltd.
      # https://www.mql5.com

      # 소수점 이하 자릿수를 비교학 위한 함수
      def compare_decimal_places(value1, value2):
      # 두 값을 모두 문자열로 변환
          str_value1 = str(value1)
          str_value2 = str(value2)

      # 문자열에서 소수점 위치 찾기
          dot_position1 = str_value1.find(".")
          dot_position2 = str_value2.find(".")

      # 값 중 소수점이 없는 경우 0을 반환합니다.
          if dot_position1 == -1 or dot_position2 == -1:
              return 0

          # 소수점 이하 자릿수 계산
          decimal_places1 = len(str_value1) - dot_position1 - 1
          decimal_places2 = len(str_value2) - dot_position2 - 1

          # 소수점 이하 두 자리 수 중 최소값 찾기
          min_decimal_places = min(decimal_places1, decimal_places2)

          # 소수점 이하 자릿수가 일치하도록 카운트를 초기화
          matching_count = 0

          # 소수점 이하 문자 비교
          for i in range(1, min_decimal_places + 1):
              if str_value1[dot_position1 + i] == str_value2[dot_position2 + i]:
                  matching_count += 1
              else:
                  break

          return matching_count

      # 필요한 라이브러리 가져오기
      import numpy as np
      import matplotlib.pyplot as plt
      from sklearn.linear_model import HuberRegressor
      from sklearn.metrics import r2_score,mean_absolute_error,mean_squared_error
      import onnx
      import onnxruntime as ort
      from skl2onnx import convert_sklearn
      from skl2onnx.common.data_types import FloatTensorType
      from skl2onnx.common.data_types import DoubleTensorType
      from sys import argv

      # 모델 저장 경로를 정의합니다.
      data_path = argv[0]
      last_index = data_path.rfind("\\")+ 1
      data_path = data_path[0:last_index]

      # 회귀를 위한 합성 데이터 생성
      X = np.arange(0,100,1).reshape(-1,1)
      y = 4*X+ 10*np.sin(X*0.5)

      model_name = "HuberRegressor"
      onnx_model_filename = data_path + "huber_regressor"

      # 후버 회귀 모델 만들기
      huber_regressor_model = HuberRegressor()

      # 데이터에 모델 맞추기
      huber_regressor_model.fit(X, y.ravel())

      # 전체 데이터 집합에 대한 값 예측
      y_pred = huber_regressor_model.predict(X)

      # 모델의 성능 평가
      r2 = r2_score(y, y_pred)
      MSE = MEAN_SQUARE_ERROR(Y, Y_PED)
      MAE = MEAN_ABSOLUTE_ERROR(Y, Y_PED)

      print("\n"+model_name+" Original model (double)")
      print("R-squared (Coefficient of determination):", r2)
      print("Mean Absolute Error:", mae)
      print("Mean Squared Error:", mse)

      # ONNX 모델로 변환 (float)
      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.

      initial_type_float = [('float_input', FloatTensorType([None, X.shape[1]])]]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_float = convert_sklearn(huber_regressor_model, initial_types=initial_type_float, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_float.onnx"
      onnx.save_model(onnx_model_float, onnx_filename)

      print("\n"+model_name+" ONNX model (float)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.
      initial_type_float = X.astype(np.float32)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_float = onnx_session.run([output_name], {input_name: initial_type_float})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_float = r2_score(y, y_pred_onnx_float)
      mse_onnx_float = mean_squared_error(y, y_pred_onnx_float)
      mae_onnx_float = mean_absolute_error(y, y_pred_onnx_float)
      print("R-squared (Coefficient of determination)", r2_onnx_float)
      print("Mean Absolute Error:", mae_onnx_float)
      print("Mean Squared Error:", mse_onnx_float)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_float))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_float))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_float))
      print("float ONNX model precision: ",compare_decimal_places(mae, mae_onnx_float))

      # 그림 크기 설정
      plt.figure(figsize=(8,5))
      # 원본 데이터와 회귀선을 그림으로 표시합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with float ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_float.png')

      # ONNX 모델로 변환 (double)
      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.

      initial_type_double = [('double_input', DoubleTensorType([None, X.shape[1]]))]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_double = convert_sklearn(huber_regressor_model, initial_types=initial_type_double, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_double.onnx"
      onnx.save_model(onnx_model_double, onnx_filename)

      print("\n"+model_name+" ONNX model (double)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.
      initial_type_double = X.astype(np.float64)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_double = onnx_session.run([output_name], {input_name: initial_type_double})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_double = r2_score(y, y_pred_onnx_double)
      mse_onnx_double = mean_squared_error(y, y_pred_onnx_double)
      mae_onnx_double = mean_absolute_error(y, y_pred_onnx_double)
      print("R-squared (Coefficient of determination)", r2_onnx_double)
      print("Mean Absolute Error:", mae_onnx_double)
      print("Mean Squared Error:", mse_onnx_double)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_double))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_double))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_double))
      print("double ONNX model precision: ",compare_decimal_places(mae, mae_onnx_double))

      # 그림 크기 설정
      plt.figure(figsize=(8,5))
      # 원본 데이터와 회귀선을 그림으로 표시합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with double ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_double.png')

      출력:

      Python  HuberRegressor Original model (double)
      Python  R-squared (Coefficient of determination): 0.9962363935647066
      Python  Mean Absolute Error: 6.341633708569641
      Python  Mean Squared Error: 49.80289464784336
      Python  
      Python  HuberRegressor ONNX model (float)
      Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\huber_regressor_float.onnx
      Python  Information about input tensors in ONNX:
      Python  1. Name: float_input, Data Type: tensor(float), Shape: [None, 1]
      Python  Information about output tensors in ONNX:
      Python  1. Name: variable, Data Type: tensor(float), Shape: [None, 1]
      Python  R-squared (Coefficient of determination) 0.9962363944236795
      Python  Mean Absolute Error: 6.341633300252807
      Python  Mean Squared Error: 49.80288328126165
      Python  R^2 matching decimal places:  8
      Python  MAE matching decimal places:  6
      Python  ONNX: MSE matching decimal places:  4
      Python  float ONNX model precision:  6
      Python  
      Python  HuberRegressor ONNX model (double)
      Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\huber_regressor_double.onnx
      Python  Information about input tensors in ONNX:
      Python  1. Name: double_input, Data Type: tensor(double), Shape: [None, 1]
      Python  Information about output tensors in ONNX:
      Python  1. Name: variable, Data Type: tensor(double), Shape: [None, 1]
      Python  R-squared (Coefficient of determination) 0.9962363935647066
      Python  Mean Absolute Error: 6.341633708569641
      Python  Mean Squared Error: 49.80289464784336
      Python  R^2 matching decimal places:  16
      Python  MAE matching decimal places:  15
      Python  MSE matching decimal places:  14
      Python  double ONNX model precision:  15
      

      Fig.21. HuberRegressor.py의 결과 (float ONNX)

      그림 21. HuberRegressor.py(float ONNX)의 결과


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

      이 코드는 저장된 huber_regressor_float.onnx huber_regressor_double.onnx ONNX 모델을 실행하고 MQL5에서 회귀 메트릭을 사용하는 예를 보여 줍니다.

      //+------------------------------------------------------------------+
      //|                                               HuberRegressor.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"
      
      #define   ModelName          "HuberRegressor"
      #define   ONNXFilenameFloat  "huber_regressor_float.onnx"
      #define   ONNXFilenameDouble "huber_regressor_double.onnx"
      
      #resource ONNXFilenameFloat  as const uchar ExtModelFloat[];
      #resource ONNXFilenameDouble as const uchar ExtModelDouble[];
      
      #define   TestFloatModel  1
      #define   TestDoubleModel 2
      
      //+------------------------------------------------------------------+
      //| Calculate regression using float values                          |
      //+------------------------------------------------------------------+
      bool RunModelFloat(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         float input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=(float)input_vector[k];
      //--- prepare output tensor
         float output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      //+------------------------------------------------------------------+
      //| Calculate regression using double values                         |
      //+------------------------------------------------------------------+
      bool RunModelDouble(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         double input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=input_vector[k];
      //--- prepare output tensor
         double output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      
      //+------------------------------------------------------------------+
      //| Generate synthetic data                                          |
      //+------------------------------------------------------------------+
      bool GenerateData(const int n,vector &x,vector &y)
        {
         if(n<=0)
            return(false);
      //--- prepare arrays
         x.Resize(n);
         y.Resize(n);
      //---
         for(int i=0; i<n; i++)
           {
            x[i]=(double)1.0*i;
            y[i]=(double)(4*x[i] + 10*sin(x[i]*0.5));
           }
      //---
         return(true);
        }
      
      //+------------------------------------------------------------------+
      //| TestRegressionModel                                              |
      //+------------------------------------------------------------------+
      bool TestRegressionModel(const string model_name,const int model_type)
        {
      //---
         long  model=INVALID_HANDLE;
         ulong flags=ONNX_DEFAULT;
      
         if(model_type==TestFloatModel)
           {
            PrintFormat("\nTesting ONNX float: %s (%s)",model_name,ONNXFilenameFloat);
            model=OnnxCreateFromBuffer(ExtModelFloat,flags);
           }
         else
            if(model_type==TestDoubleModel)
              {
               PrintFormat("\nTesting ONNX double: %s (%s)",model_name,ONNXFilenameDouble);
               model=OnnxCreateFromBuffer(ExtModelDouble,flags);
              }
            else
              {
               PrintFormat("Model type is not incorrect.");
               return(false);
              }
      //--- check
         if(model==INVALID_HANDLE)
           {
            PrintFormat("model_name=%s OnnxCreate error %d",model_name,GetLastError());
            return(false);
           }
      //---
         vector x_values= {};
         vector y_true= {};
         vector y_predicted= {};
      //---
         int n=100;
         GenerateData(n,x_values,y_true);
      //---
         bool run_result=false;
         if(model_type==TestFloatModel)
           {
            run_result=RunModelFloat(model,x_values,y_predicted);
           }
         else
            if(model_type==TestDoubleModel)
              {
               run_result=RunModelDouble(model,x_values,y_predicted);
              }
      //---
         if(run_result)
           {
            PrintFormat("MQL5:   R-Squared (Coefficient of determination): %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_R2));
            PrintFormat("MQL5:   Mean Absolute Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MAE));
            PrintFormat("MQL5:   Mean Squared Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MSE));
           }
         else
            PrintFormat("Error %d",GetLastError());
      //--- release model
         OnnxRelease(model);
      //---
         return(true);
        }
      //+------------------------------------------------------------------+
      //| Script program start function                                    |
      //+------------------------------------------------------------------+
      int OnStart(void)
        {
      //--- test ONNX regression model for float
         TestRegressionModel(ModelName,TestFloatModel);
      //--- test ONNX regression model for double
         TestRegressionModel(ModelName,TestDoubleModel);
      //---
         return(0);
        }
      //+------------------------------------------------------------------+

      출력:

      HuberRegressor (EURUSD,H1)      Testing ONNX float: HuberRegressor (huber_regressor_float.onnx)
      HuberRegressor (EURUSD,H1)      MQL5:   R-Squared (Coefficient of determination): 0.9962363944236795
      HuberRegressor (EURUSD,H1)      MQL5:   Mean Absolute Error: 6.3416333002528074
      HuberRegressor (EURUSD,H1)      MQL5:   Mean Squared Error: 49.8028832812616571
      HuberRegressor (EURUSD,H1)      
      HuberRegressor (EURUSD,H1)      Testing ONNX double: HuberRegressor (huber_regressor_double.onnx)
      HuberRegressor (EURUSD,H1)      MQL5:   R-Squared (Coefficient of determination): 0.9962363935647066
      HuberRegressor (EURUSD,H1)      MQL5:   Mean Absolute Error: 6.3416337085696410
      HuberRegressor (EURUSD,H1)      MQL5:   Mean Squared Error: 49.8028946478433525
      

      파이썬에서 원래 double 모델과 비교:

      Testing ONNX float: HuberRegressor (huber_regressor_float.onnx)
      Python  Mean Absolute Error: 6.341633708569641
      MQL5:   Mean Absolute Error: 6.3416333002528074
            
      Testing ONNX double: HuberRegressor (huber_regressor_double.onnx)
      Python  Mean Absolute Error: 6.341633708569641
      MQL5:   Mean Absolute Error: 6.3416337085696410

      ONNX float MAE의 정확도: 소수점 이하 6자리, 정확도 ONNX double MAE: 소수점 이하 14자리.


      2.1.5.3. huber_regressor_float.onnx 및 huber_regressor_double.onnx의 ONNX 표현


      그림 22. Netron에서 huber_regressor_float.onnx의 ONNX 표현

      그림 22. 네트론에서 huber_regressor_float.onnx의 ONNX 표현


      그림 23. Netron에서 huber_regressor_double.onnx의 ONNX 표현

      그림 23. 네트론에서 huber_regressor_double.onnx의 ONNX 표현


      2.1.6. sklearn.linear_model.Lars

      LARS(최소 각 회귀)는 회귀 작업에 사용되는 머신 러닝 메서드입니다. 학습 과정에서 활성 피처(변수)를 선택해 선형 회귀 모델을 구축하는 알고리즘입니다.

      LARS는 목표 변수에 가장 가까운 근사치를 제공하는 최소한의 피처를 찾으려고 시도합니다.

      LARS의 작동 원리:

      1. 입력 데이터: LARS는 피처(독립 변수)와 그에 해당하는 목표 변수 값으로 구성된 원본 데이터 세트에서 시작합니다.
      2. 초기화: 활성 피처가 없다는 것을 의미하는 null 모델로 시작합니다. 모든 계수는 0으로 설정됩니다.
      3. 피처 선택: 각 단계에서 LARS는 모델의 잔차와 가장 상관관계가 높은 피처를 선택합니다. 그런 다음 이 피처를 모델에 추가하고 최소 제곱 메서드을 사용하여 해당 계수를 조정합니다.
      4. 활성 피처를 따라 회귀: 모델에 피처을 추가한 후 LARS는 새 모델의 변경 사항을 수용하도록 모든 활성 피처의 계수를 업데이트합니다.
      5. 반복 단계: 이 프로세스는 모든 피처가 선택되거나 지정된 중지 기준이 충족될 때까지 계속됩니다.
      6. 예측: 모델 학습 후에는 새로운 데이터의 목표 변수 값을 예측하는 데 사용할 수 있습니다.

      LARS의 장점:

      • 효율성: 특히 피처가 많지만 대상 변수에 큰 영향을 미치는 피처가 몇 개에 불과한 경우 LARS가 효율적인 메서드가 될 수 있습니다.
      • 해석 가능성: LARS는 가장 유익한 피처만 선택하는 것을 목표로 하기 때문에 비교적 해석하기 쉽습니다.

      LARS의 한계:

      • 선형 모델: LARS는 선형 모델을 구축하기 때문에 복잡한 비선형 관계를 모델링하는 데 부족할 수 있습니다.
      • 소음 민감도: 이 메서드는 데이터의 이상값에 민감할 수 있습니다.
      • 다중 선형성을 처리 불가: 피처의 상관관계가 높은 경우 LARS에 다중 상관성 문제가 발생할 수 있습니다.

      LARS는 가장 유익한 피처들을 선택하고 최소한의 피처으로 선형 모델을 구성하는 것이 필수적인 회귀 작업에 유용합니다.


      2.1.6.1. Lars 모델을 생성하고 float 및 double용 ONNX로 내보내기 위한 코드

      이 코드는 sklearn.linear_model.Lars 모델을 생성하고, 합성 데이터로 학습시키고 모델을 ONNX 형식으로 저장하고 플로트 및 이중 입력 데이터를 모두 사용하여 예측을 수행합니다. 또한 원본 모델과 ONNX로 내보낸 모델 모두의 정확도를 평가합니다.

      # Lars.py
      # 이 코드는 Lars 모델을 학습하고 이를 ONNX 형식(float 및 double 모두)으로 내보내고 ONNX 모델을 사용하여 예측하는 과정을 보여 줍니다.
      # Copyright 2023, MetaQuotes Ltd.
      # https://www.mql5.com


      # 소수점 이하 자릿수를 비교학 위한 함수
      def compare_decimal_places(value1, value2):
      # 두 값을 모두 문자열로 변환
          str_value1 = str(value1)
          str_value2 = str(value2)

      # 문자열에서 소수점 위치 찾기
          dot_position1 = str_value1.find(".")
          dot_position2 = str_value2.find(".")

      # 값 중 소수점이 없는 경우 0을 반환합니다.
          if dot_position1 == -1 or dot_position2 == -1:
              return 0

          # 소수점 이하 자릿수 계산
          decimal_places1 = len(str_value1) - dot_position1 - 1
          decimal_places2 = len(str_value2) - dot_position2 - 1

          # 소수점 이하 두 자리 수 중 최소값 찾기
          min_decimal_places = min(decimal_places1, decimal_places2)

          # 소수점 이하 자릿수가 일치하도록 카운트를 초기화
          matching_count = 0

          # 소수점 이하 문자 비교
          for i in range(1, min_decimal_places + 1):
              if str_value1[dot_position1 + i] == str_value2[dot_position2 + i]:
                  matching_count += 1
              else:
                  break

          return matching_count

      # 필요한 라이브러리 가져오기
      import numpy as np
      import matplotlib.pyplot as plt
      에서 sklearn.linear_model import Lars
      from sklearn.metrics import r2_score,mean_absolute_error,mean_squared_error
      import onnx
      import onnxruntime as ort
      from skl2onnx import convert_sklearn
      from skl2onnx.common.data_types import FloatTensorType
      from skl2onnx.common.data_types import DoubleTensorType
      from sys import argv

      # 모델 저장 경로를 정의합니다.
      data_path = argv[0]
      last_index = data_path.rfind("\\")+ 1
      data_path = data_path[0:last_index]

      # 회귀를 위한 합성 데이터 생성
      X = np.arange(0,100,1).reshape(-1,1)
      y = 4*X+ 10*np.sin(X*0.5)

      model_name = "Lars"
      onnx_model_filename = data_path + "lars"

      # 라스 회귀 모델 만들기
      lars_regressor_model = Lars()

      # 데이터에 모델 맞추기
      lars_regressor_model.fit(X, y.ravel())

      # 전체 데이터 집합에 대한 값 예측
      y_pred = lars_regressor_model.predict(X)

      # 모델의 성능 평가
      r2 = r2_score(y, y_pred)
      MSE = MEAN_SQUARE_ERROR(Y, Y_PED)
      MAE = MEAN_ABSOLUTE_ERROR(Y, Y_PED)

      print("\n"+model_name+" Original model (double)")
      print("R-squared (Coefficient of determination):", r2)
      print("Mean Absolute Error:", mae)
      print("Mean Squared Error:", mse)

      # ONNX 모델로 변환 (float)
      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.

      initial_type_float = [('float_input', FloatTensorType([None, X.shape[1]])]]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_float = convert_sklearn(lars_regressor_model, initial_types=initial_type_float, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_float.onnx"
      onnx.save_model(onnx_model_float, onnx_filename)

      print("\n"+model_name+" ONNX model (float)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.
      initial_type_float = X.astype(np.float32)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_float = onnx_session.run([output_name], {input_name: initial_type_float})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_float = r2_score(y, y_pred_onnx_float)
      mse_onnx_float = mean_squared_error(y, y_pred_onnx_float)
      mae_onnx_float = mean_absolute_error(y, y_pred_onnx_float)
      print("R-squared (Coefficient of determination)", r2_onnx_float)
      print("Mean Absolute Error:", mae_onnx_float)
      print("Mean Squared Error:", mse_onnx_float)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_float))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_float))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_float))
      print("float ONNX model precision: ",compare_decimal_places(mae, mae_onnx_float))

      # 그림 크기 설정
      plt.figure(figsize=(8,5))
      # 원본 데이터와 회귀선을 그림으로 표시합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with float ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_float.png')

      # ONNX 모델로 변환 (double)
      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.
      initial_type_double = [('double_input', DoubleTensorType([None, X.shape[1]]))]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_double = convert_sklearn(lars_regressor_model, initial_types=initial_type_double, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_double.onnx"
      onnx.save_model(onnx_model_double, onnx_filename)

      print("\n"+model_name+" ONNX model (double)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.
      initial_type_double = X.astype(np.float64)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_double = onnx_session.run([output_name], {input_name: initial_type_double})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_double = r2_score(y, y_pred_onnx_double)
      mse_onnx_double = mean_squared_error(y, y_pred_onnx_double)
      mae_onnx_double = mean_absolute_error(y, y_pred_onnx_double)
      print("R-squared (Coefficient of determination)", r2_onnx_double)
      print("Mean Absolute Error:", mae_onnx_double)
      print("Mean Squared Error:", mse_onnx_double)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_double))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_double))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_double))
      print("double ONNX model precision: ",compare_decimal_places(mae, mae_onnx_double))

      # 그림 크기 설정
      plt.figure(figsize=(8,5))
      # 원본 데이터와 회귀선을 그림으로 표시합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with double ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_double.png')

      출력:

      Python  Lars Original model (double)
      Python  R-squared (Coefficient of determination): 0.9962382642613388
      Python  Mean Absolute Error: 6.347737926336425
      Python  Mean Squared Error: 49.778140171281784
      Python  
      Python  Lars ONNX model (float)
      Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\lars_float.onnx
      Python  Information about input tensors in ONNX:
      Python  1. Name: float_input, Data Type: tensor(float), Shape: [None, 1]
      Python  Information about output tensors in ONNX:
      Python  1. Name: variable, Data Type: tensor(float), Shape: [None, 1]
      Python  R-squared (Coefficient of determination) 0.9962382641628886
      Python  Mean Absolute Error: 6.3477377671679385
      Python  Mean Squared Error: 49.77814147404787
      Python  R^2 matching decimal places:  9
      Python  MAE matching decimal places:  6
      Python  MSE matching decimal places:  5
      Python  float ONNX model precision:  6
      Python  
      Python  Lars ONNX model (double)
      Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\lars_double.onnx
      Python  Information about input tensors in ONNX:
      Python  1. Name: double_input, Data Type: tensor(double), Shape: [None, 1]
      Python  Information about output tensors in ONNX:
      Python  1. Name: variable, Data Type: tensor(double), Shape: [None, 1]
      Python  R-squared (Coefficient of determination) 0.9962382642613388
      Python  Mean Absolute Error: 6.347737926336425
      Python  Mean Squared Error: 49.778140171281784
      Python  R^2 matching decimal places:  16
      Python  MAE matching decimal places:  15
      Python  MSE matching decimal places:  15
      Python  double ONNX model precision:  15
      

      Fig.24. Lars.py(float ONNX) 결과

      그림 24. Lars.py 결과(float ONNX)


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

      이 코드는 저장된 lars_cv_float.onnxlars_cv_double.onnx 모델을 실행하고 MQL5에서 회귀 메트릭의 사용을 보여줍니다.

      //+------------------------------------------------------------------+
      //|                                                         Lars.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"
      
      #define   ModelName          "Lars"
      #define   ONNXFilenameFloat  "lars_float.onnx"
      #define   ONNXFilenameDouble "lars_double.onnx"
      
      #resource ONNXFilenameFloat  as const uchar ExtModelFloat[];
      #resource ONNXFilenameDouble as const uchar ExtModelDouble[];
      
      #define   TestFloatModel  1
      #define   TestDoubleModel 2
      
      //+------------------------------------------------------------------+
      //| Calculate regression using float values                          |
      //+------------------------------------------------------------------+
      bool RunModelFloat(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         float input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=(float)input_vector[k];
      //--- prepare output tensor
         float output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      //+------------------------------------------------------------------+
      //| Calculate regression using double values                         |
      //+------------------------------------------------------------------+
      bool RunModelDouble(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         double input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=input_vector[k];
      //--- prepare output tensor
         double output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      
      //+------------------------------------------------------------------+
      //| Generate synthetic data                                          |
      //+------------------------------------------------------------------+
      bool GenerateData(const int n,vector &x,vector &y)
        {
         if(n<=0)
            return(false);
      //--- prepare arrays
         x.Resize(n);
         y.Resize(n);
      //---
         for(int i=0; i<n; i++)
           {
            x[i]=(double)1.0*i;
            y[i]=(double)(4*x[i] + 10*sin(x[i]*0.5));
           }
      //---
         return(true);
        }
      
      //+------------------------------------------------------------------+
      //| TestRegressionModel                                              |
      //+------------------------------------------------------------------+
      bool TestRegressionModel(const string model_name,const int model_type)
        {
      //---
         long  model=INVALID_HANDLE;
         ulong flags=ONNX_DEFAULT;
      
         if(model_type==TestFloatModel)
           {
            PrintFormat("\nTesting ONNX float: %s (%s)",model_name,ONNXFilenameFloat);
            model=OnnxCreateFromBuffer(ExtModelFloat,flags);
           }
         else
            if(model_type==TestDoubleModel)
              {
               PrintFormat("\nTesting ONNX double: %s (%s)",model_name,ONNXFilenameDouble);
               model=OnnxCreateFromBuffer(ExtModelDouble,flags);
              }
            else
              {
               PrintFormat("Model type is not incorrect.");
               return(false);
              }
      //--- check
         if(model==INVALID_HANDLE)
           {
            PrintFormat("model_name=%s OnnxCreate error %d",model_name,GetLastError());
            return(false);
           }
      //---
         vector x_values= {};
         vector y_true= {};
         vector y_predicted= {};
      //---
         int n=100;
         GenerateData(n,x_values,y_true);
      //---
         bool run_result=false;
         if(model_type==TestFloatModel)
           {
            run_result=RunModelFloat(model,x_values,y_predicted);
           }
         else
            if(model_type==TestDoubleModel)
              {
               run_result=RunModelDouble(model,x_values,y_predicted);
              }
      //---
         if(run_result)
           {
            PrintFormat("MQL5:   R-Squared (Coefficient of determination): %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_R2));
            PrintFormat("MQL5:   Mean Absolute Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MAE));
            PrintFormat("MQL5:   Mean Squared Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MSE));
           }
         else
            PrintFormat("Error %d",GetLastError());
      //--- release model
         OnnxRelease(model);
      //---
         return(true);
        }
      //+------------------------------------------------------------------+
      //| Script program start function                                    |
      //+------------------------------------------------------------------+
      int OnStart(void)
        {
      //--- test ONNX regression model for float
         TestRegressionModel(ModelName,TestFloatModel);
      //--- test ONNX regression model for double
         TestRegressionModel(ModelName,TestDoubleModel);
      //---
         return(0);
        }
      //+------------------------------------------------------------------+

      출력:

      Lars (EURUSD,H1)        Testing ONNX float: Lars (lars_float.onnx)
      Lars (EURUSD,H1)        MQL5:   R-Squared (Coefficient of determination): 0.9962382641628886
      Lars (EURUSD,H1)        MQL5:   Mean Absolute Error: 6.3477377671679385
      Lars (EURUSD,H1)        MQL5:   Mean Squared Error: 49.7781414740478638
      Lars (EURUSD,H1)        
      Lars (EURUSD,H1)        Testing ONNX double: Lars (lars_double.onnx)
      Lars (EURUSD,H1)        MQL5:   R-Squared (Coefficient of determination): 0.9962382642613388
      Lars (EURUSD,H1)        MQL5:   Mean Absolute Error: 6.3477379263364302
      Lars (EURUSD,H1)        MQL5:   Mean Squared Error: 49.7781401712817768
      

      파이썬에서 원래 double 모델과 비교:

      Testing ONNX float: Lars (lars_float.onnx)
      Python  Mean Absolute Error: 6.347737926336425
      MQL5:   Mean Absolute Error: 6.3477377671679385
      
      Testing ONNX double: Lars (lars_double.onnx)
      Python  Mean Absolute Error: 6.347737926336425
      MQL5:   Mean Absolute Error: 6.3477379263364302

      ONNX float MAE의 정확도: 소수점 이하 6자리, 정확도 ONNX double MAE: 소수점 이하 13자리.


      2.1.6.3. lars_float.onnx 및 lars_double.onnx의 ONNX 표현


      Fig.25. Netron에서 lars_float.onnx의 ONNX 표현

      그림 25. 네트론에서 lars_float.onnx의 ONNX 표현


      Fig.26. Netron에서 lars_double.onnx의 ONNX 표현

      그림 26. Netron에서 lars_double.onnx의 ONNX 표현



      2.1.7. sklearn.linear_model.LarsCV

      LarsCV는 교차 검증을 사용하여 모델에 포함할 최적의 피처 수를 자동으로 선택하는 LARS(최소 각 회귀) 메서드의 변형입니다.

      이 메서드는 데이터를 효과적으로 일반화하는 모델과 최소한의 피처을 사용하는 모델 간의 균형을 맞추는 데 도움이 됩니다.

      LarsCV의 작동 원리:

      1. 입력 데이터: LarsCV는 피처(독립 변수)와 그에 해당하는 목표 변수 값으로 구성된 원본 데이터 집합으로 시작합니다.
      2. 초기화: 활성 피처가 없음을 의미하는 널 모델로 시작합니다. 모든 계수는 0으로 설정됩니다.
      3. 교차 유효성 검사: LarsCV는 다양한 량이 포함된 피처에 대해 교차 검증을 수행하며 다양한 피처 세트를 사용하여 모델의 성능을 평가합니다.
      4. 최적의 피처 수 선택하기: LarsCV는 결정된 최상의 모델 성능을 제공하는 피처의 수를 교차 검증을 통해 선택합니다.
      5. 모델 교육: 모델은 선택한 피처 수와 각각의 계수를 사용하여 학습됩니다.
      6. 예측: 학습 후에는 이 모델을 사용하여 새로운 데이터의 목표 변수 값을 예측할 수 있습니다.

      LarsCV의 장점:

      • 자동 피처 선택: LarsCV는 최적의 피처 수를 자동으로 선택해 모델 설정 프로세스를 간소화합니다.
      • 해석 가능성: 일반 LARS와 마찬가지로 LarsCV는 상대적으로 높은 모델 해석 가능성을 유지합니다.
      • 효율성: 이 메서드는 특히 데이터 집합에 많은 피처가 있지만 중요한 피처는 몇 개에 불과한 경우에 효율적일 수 있습니다.

      LarsCV의 한계:

      • 선형 모델: LarsCV는 선형 모델을 구축하기 때문에 복잡한 비선형 관계를 모델링하기에는 부족할 수 있습니다.
      • 잡음 민감도: 이 메서드는 데이터의 이상값에 민감할 수 있습니다.
      • 다중 선형성을 처리 불가: 피처의 상관관계가 높은 경우 LarsCV에 다중 상관성 문제가 발생할 수 있습니다.

      LarsCV이 유용하게 쓰이는 곳은 모델에 사용되는 최적의 피처 집합을 자동으로 선택하고 모델 해석 가능성을 유지하는 것이 중요한 회귀 작업에서입니다.


      2.1.7.1. LarsCV 모델을 생성하고 float 및 double용 ONNX로 내보내기 위한 코드

      이 코드는 sklearn.linear_model.LarsCV 모델을 생성하고 합성 데이터로 학습시키고 모델을 ONNX 형식으로 저장하고 플로트 및 더블 입력 데이터를 모두 사용하여 예측을 수행합니다. 또한 원본 모델과 ONNX로 내보낸 모델 모두의 정확도를 평가합니다.

      # LarsCV.py
      # 이 코드는 LarsCV 모델을 학습하고 이를 ONNX 형식(float 및 double)으로 내보내고 ONNX 모델을 사용하여 예측하는 과정을 보여 줍니다.
      # Copyright 2023, MetaQuotes Ltd.
      # https://www.mql5.com


      # 소수점 이하 자릿수를 비교학 위한 함수
      def compare_decimal_places(value1, value2):
      # 두 값을 모두 문자열로 변환
          str_value1 = str(value1)
          str_value2 = str(value2)

      # 문자열에서 소수점 위치 찾기
          dot_position1 = str_value1.find(".")
          dot_position2 = str_value2.find(".")

      # 값 중 소수점이 없는 경우 0을 반환합니다.
          if dot_position1 == -1 or dot_position2 == -1:
              return 0

          # 소수점 이하 자릿수 계산
          decimal_places1 = len(str_value1) - dot_position1 - 1
          decimal_places2 = len(str_value2) - dot_position2 - 1

          # 소수점 이하 두 자리 수 중 최소값 찾기
          min_decimal_places = min(decimal_places1, decimal_places2)

          # 소수점 이하 자릿수가 일치하도록 카운트를 초기화
          matching_count = 0

          # 소수점 이하 문자 비교
          for i in range(1, min_decimal_places + 1):
              if str_value1[dot_position1 + i] == str_value2[dot_position2 + i]:
                  matching_count += 1
              else:
                  break

          return matching_count

      # 필요한 라이브러리 가져오기
      import numpy as np
      import matplotlib.pyplot as plt
      from sklearn.linear_model import LarsCV
      from sklearn.metrics import r2_score,mean_absolute_error,mean_squared_error
      import onnx
      import onnxruntime as ort
      from skl2onnx import convert_sklearn
      from skl2onnx.common.data_types import FloatTensorType
      from skl2onnx.common.data_types import DoubleTensorType
      from sys import argv

      # 모델 저장 경로를 정의합니다.
      data_path = argv[0]
      last_index = data_path.rfind("\\")+ 1
      data_path = data_path[0:last_index]

      # 회귀를 위한 합성 데이터 생성
      X = np.arange(0,100,1).reshape(-1,1)
      y = 4*X+ 10*np.sin(X*0.5)

      model_name = "LarsCV"
      onnx_model_filename = data_path + "lars_cv"

      # LarsCV 회귀 모델 만들기
      larscv_regressor_model = LarsCV()

      # 데이터에 모델 맞추기
      larscv_regressor_model.fit(X, y.ravel())

      # 전체 데이터 집합에 대한 값 예측
      y_pred = larscv_regressor_model.predict(X)

      # 모델의 성능 평가
      r2 = r2_score(y, y_pred)
      MSE = MEAN_SQUARE_ERROR(Y, Y_PED)
      MAE = MEAN_ABSOLUTE_ERROR(Y, Y_PED)

      print("\n"+model_name+" Original model (double)")
      print("R-squared (Coefficient of determination):", r2)
      print("Mean Absolute Error:", mae)
      print("Mean Squared Error:", mse)

      # ONNX 모델로 변환 (float)
      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.

      initial_type_float = [('float_input', FloatTensorType([None, X.shape[1]])]]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_float = convert_sklearn(larscv_regressor_model, initial_types=initial_type_float, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_float.onnx"
      onnx.save_model(onnx_model_float, onnx_filename)

      print("\n"+model_name+" ONNX model (float)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.
      initial_type_float = X.astype(np.float32)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_float = onnx_session.run([output_name], {input_name: initial_type_float})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_float = r2_score(y, y_pred_onnx_float)
      mse_onnx_float = mean_squared_error(y, y_pred_onnx_float)
      mae_onnx_float = mean_absolute_error(y, y_pred_onnx_float)
      print("R-squared (Coefficient of determination)", r2_onnx_float)
      print("Mean Absolute Error:", mae_onnx_float)
      print("Mean Squared Error:", mse_onnx_float)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_float))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_float))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_float))
      print("float ONNX model precision: ",compare_decimal_places(mae, mae_onnx_float))

      # 그림 크기 설정
      plt.figure(figsize=(8,5))
      # 원본 데이터와 회귀선을 그림으로 표시합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with float ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_float.png')

      # ONNX 모델로 변환 (double)
      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.

      initial_type_double = [('double_input', DoubleTensorType([None, X.shape[1]]))]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_double = convert_sklearn(larscv_regressor_model, initial_types=initial_type_double, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_double.onnx"
      onnx.save_model(onnx_model_double, onnx_filename)

      print("\n"+model_name+" ONNX model (double)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.
      initial_type_double = X.astype(np.float64)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_double = onnx_session.run([output_name], {input_name: initial_type_double})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_double = r2_score(y, y_pred_onnx_double)
      mse_onnx_double = mean_squared_error(y, y_pred_onnx_double)
      mae_onnx_double = mean_absolute_error(y, y_pred_onnx_double)
      print("R-squared (Coefficient of determination)", r2_onnx_double)
      print("Mean Absolute Error:", mae_onnx_double)
      print("Mean Squared Error:", mse_onnx_double)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_double))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_double))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_double))
      print("double ONNX model precision: ",compare_decimal_places(mae, mae_onnx_double))

      # 그림 크기 설정
      plt.figure(figsize=(8,5))
      # 원본 데이터와 회귀선을 그림으로 표시합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with double ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_double.png')

      출력:

      Python  LarsCV Original model (double)
      Python  R-squared (Coefficient of determination): 0.9962382642612767
      Python  Mean Absolute Error: 6.3477379221400145
      Python  Mean Squared Error: 49.77814017210321
      Python  
      Python  LarsCV ONNX model (float)
      Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\lars_cv_float.onnx
      Python  Information about input tensors in ONNX:
      Python  1. Name: float_input, Data Type: tensor(float), Shape: [None, 1]
      Python  Information about output tensors in ONNX:
      Python  1. Name: variable, Data Type: tensor(float), Shape: [None, 1]
      Python  R-squared (Coefficient of determination) 0.9962382640824089
      Python  Mean Absolute Error: 6.347737845846069
      Python  Mean Squared Error: 49.778142539016564
      Python  R^2 matching decimal places:  9
      Python  MAE matching decimal places:  6
      Python  ONNX: MSE matching decimal places:  5
      Python  float ONNX model precision:  6
      Python  
      Python  LarsCV ONNX model (double)
      Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\lars_cv_double.onnx
      Python  Information about input tensors in ONNX:
      Python  1. Name: double_input, Data Type: tensor(double), Shape: [None, 1]
      Python  Information about output tensors in ONNX:
      Python  1. Name: variable, Data Type: tensor(double), Shape: [None, 1]
      Python  R-squared (Coefficient of determination) 0.9962382642612767
      Python  Mean Absolute Error: 6.3477379221400145
      Python  Mean Squared Error: 49.77814017210321
      Python  R^2 matching decimal places:  16
      Python  MAE matching decimal places:  16
      Python  MSE matching decimal places:  14
      Python  double ONNX model precision:  16
      

      Fig.27. LarsCV.py (float ONNX) 결과

      그림 27. LarsCV.py의 결과(float ONNX)


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

      이 코드는 저장된 lars_cv_float.onnx lars_cv_double.onnx 모델을 실행하고 MQL5에서 회귀 메트릭을 사용하는 것을 보여줍니다.

      //+------------------------------------------------------------------+
      //|                                                       LarsCV.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"
      
      #define   ModelName          "LarsCV"
      #define   ONNXFilenameFloat  "lars_cv_float.onnx"
      #define   ONNXFilenameDouble "lars_cv_double.onnx"
      
      #resource ONNXFilenameFloat  as const uchar ExtModelFloat[];
      #resource ONNXFilenameDouble as const uchar ExtModelDouble[];
      
      #define   TestFloatModel  1
      #define   TestDoubleModel 2
      
      //+------------------------------------------------------------------+
      //| Calculate regression using float values                          |
      //+------------------------------------------------------------------+
      bool RunModelFloat(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         float input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=(float)input_vector[k];
      //--- prepare output tensor
         float output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      //+------------------------------------------------------------------+
      //| Calculate regression using double values                         |
      //+------------------------------------------------------------------+
      bool RunModelDouble(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         double input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=input_vector[k];
      //--- prepare output tensor
         double output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      
      //+------------------------------------------------------------------+
      //| Generate synthetic data                                          |
      //+------------------------------------------------------------------+
      bool GenerateData(const int n,vector &x,vector &y)
        {
         if(n<=0)
            return(false);
      //--- prepare arrays
         x.Resize(n);
         y.Resize(n);
      //---
         for(int i=0; i<n; i++)
           {
            x[i]=(double)1.0*i;
            y[i]=(double)(4*x[i] + 10*sin(x[i]*0.5));
           }
      //---
         return(true);
        }
      
      //+------------------------------------------------------------------+
      //| TestRegressionModel                                              |
      //+------------------------------------------------------------------+
      bool TestRegressionModel(const string model_name,const int model_type)
        {
      //---
         long  model=INVALID_HANDLE;
         ulong flags=ONNX_DEFAULT;
      
         if(model_type==TestFloatModel)
           {
            PrintFormat("\nTesting ONNX float: %s (%s)",model_name,ONNXFilenameFloat);
            model=OnnxCreateFromBuffer(ExtModelFloat,flags);
           }
         else
            if(model_type==TestDoubleModel)
              {
               PrintFormat("\nTesting ONNX double: %s (%s)",model_name,ONNXFilenameDouble);
               model=OnnxCreateFromBuffer(ExtModelDouble,flags);
              }
            else
              {
               PrintFormat("Model type is not incorrect.");
               return(false);
              }
      //--- check
         if(model==INVALID_HANDLE)
           {
            PrintFormat("model_name=%s OnnxCreate error %d",model_name,GetLastError());
            return(false);
           }
      //---
         vector x_values= {};
         vector y_true= {};
         vector y_predicted= {};
      //---
         int n=100;
         GenerateData(n,x_values,y_true);
      //---
         bool run_result=false;
         if(model_type==TestFloatModel)
           {
            run_result=RunModelFloat(model,x_values,y_predicted);
           }
         else
            if(model_type==TestDoubleModel)
              {
               run_result=RunModelDouble(model,x_values,y_predicted);
              }
      //---
         if(run_result)
           {
            PrintFormat("MQL5:   R-Squared (Coefficient of determination): %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_R2));
            PrintFormat("MQL5:   Mean Absolute Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MAE));
            PrintFormat("MQL5:   Mean Squared Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MSE));
           }
         else
            PrintFormat("Error %d",GetLastError());
      //--- release model
         OnnxRelease(model);
      //---
         return(true);
        }
      //+------------------------------------------------------------------+
      //| Script program start function                                    |
      //+------------------------------------------------------------------+
      int OnStart(void)
        {
      //--- test ONNX regression model for float
         TestRegressionModel(ModelName,TestFloatModel);
      //--- test ONNX regression model for double
         TestRegressionModel(ModelName,TestDoubleModel);
      //---
         return(0);
        }
      //+------------------------------------------------------------------+

      출력:

      LarsCV (EURUSD,H1)      Testing ONNX float: LarsCV (lars_cv_float.onnx)
      LarsCV (EURUSD,H1)      MQL5:   R-Squared (Coefficient of determination): 0.9962382640824089
      LarsCV (EURUSD,H1)      MQL5:   Mean Absolute Error: 6.3477378458460691
      LarsCV (EURUSD,H1)      MQL5:   Mean Squared Error: 49.7781425390165566
      LarsCV (EURUSD,H1)      
      LarsCV (EURUSD,H1)      Testing ONNX double: LarsCV (lars_cv_double.onnx)
      LarsCV (EURUSD,H1)      MQL5:   R-Squared (Coefficient of determination): 0.9962382642612767
      LarsCV (EURUSD,H1)      MQL5:   Mean Absolute Error: 6.3477379221400145
      LarsCV (EURUSD,H1)      MQL5:   Mean Squared Error: 49.7781401721031642
      

      파이썬의 원래 배정밀도 모델과 비교:

      Testing ONNX float: LarsCV (lars_cv_float.onnx)
      Python  Mean Absolute Error: 6.3477379221400145
      MQL5:   Mean Absolute Error: 6.3477378458460691
      
      Testing ONNX double: LarsCV (lars_cv_double.onnx)
      Python  Mean Absolute Error: 6.3477379221400145
      MQL5:   Mean Absolute Error: 6.3477379221400145

      ONNX float MAE의 정확도: 소수점 이하 6자리, 정확도 ONNX double MAE: 소수점 이하 16자리.


      2.1.7.3. lars_cv_float.onnx 및 lars_cv_double.onnx의 ONNX 표현

      Fig.28. Netron에서 lars_cv_float.onnx의 ONNX 표현

      그림 28. 네트론에서 lars_cv_float.onnx의 ONNX 표현


      Fig.29. Netron에서 lars_cv_double.onnx의 ONNX 표현

      그림 29. 네트론에서 lars_cv_double.onnx의 ONNX 표현



      2.1.8. sklearn.linear_model.Lasso

      Lasso(최소 절대 축소 및 선택 연산자)는 가장 중요한 피처를 선택하고 모델 차원을 줄이는 데 사용되는 회귀 메서드입니다.

      선형 회귀 최적화 문제에서 계수 절대값의 합에 페널티를 추가하여(L1 정규화) 이를 달성합니다.

      Lasso의 작동 원리:

      1. 입력 데이터: Lasso는 피처(독립 변수) 및 해당 목표 변수 값을 포함한 원본 데이터 집합으로 시작합니다.
      2. 목적 함수: Lasso의 목적 함수에는 회귀 오차의 제곱합과 피처와 관련된 계수의 절대값 합에 대한 페널티가 포함됩니다.
      3. 최적화: Lasso 모델은 목적 함수를 최소화하여 학습되므로 일부 계수가 0이 되어 모델에서 해당 피처를 효과적으로 제외합니다.
      4. 최적의 페널티 값 선택하기: Lasso에는 정규화 강도를 결정하는 하이퍼파라미터가 포함되어 있습니다. 이 하이퍼파라미터에 대한 최적의 값을 선택하려면 교차 검증이 필요할 수 있습니다.
      5. 예측 생성하기: 학습 후에는 이 모델을 사용하여 새로운 데이터의 목표 변수 값을 예측할 수 있습니다.

      Lasso의 장점:

      • 피처 선택: Lasso는 모델에서 덜 중요한 피처을 제외하고 가장 중요한 피처를 자동으로 선택합니다. 이렇게 하면 데이터 차원이 줄어들고 모델이 단순화됩니다.
      • 정규화: 계수 절대값의 합에 대한 페널티는 모델 과적합을 방지하고 일반화를 향상시키는 데 도움이 됩니다.
      • 해석 가능성: Lasso는 일부 피처을 제외하기 때문에 비교적 해석하기 쉬운 모델로 남아 있습니다.

      Lasso의 한계:

      • 선형 모델: Lasso는 선형 모델을 구축하므로 복잡한 비선형 관계를 모델링하는 데 부족할 수 있습니다.
      • 잡음 민감도: 이 메서드는 데이터의 이상값에 민감할 수 있습니다.
      • 다중 선형성 처리 불가: 피처의 상관관계가 높은 경우 올가미에 다중 상관성 문제가 발생할 수 있습니다.

      Lasso는 가장 중요한 피처를 선택하고 해석 가능성을 유지하면서 모델의 차원을 줄이는 것이 필수적인 회귀 작업에 유용합니다.


      2.1.8.1. Lasso 모델을 생성하고 float 및 double용 ONNX로 내보내기 위한 코드

      이 코드는 sklearn.linear_model.Lasso 모델을 생성하고, 합성 데이터로 학습시키고, 모델을 ONNX 형식으로 저장하고, 플로트 및 더블 입력 데이터를 모두 사용하여 예측을 수행합니다. 또한 원본 모델과 ONNX로 내보낸 모델 모두의 정확도를 평가합니다.

      # Lasso.py
      # 이 코드는 Lasso 모델을 학습하고 이를 ONNX 형식(float 및 double 모두)으로 내보내고 ONNX 모델을 사용하여 예측하는 과정을 보여 줍니다.
      # Copyright 2023, MetaQuotes Ltd.
      # https://www.mql5.com


      # 소수점 이하 자릿수를 비교학 위한 함수
      def compare_decimal_places(value1, value2):
      # 두 값을 모두 문자열로 변환
          str_value1 = str(value1)
          str_value2 = str(value2)

      # 문자열에서 소수점 위치 찾기
          dot_position1 = str_value1.find(".")
          dot_position2 = str_value2.find(".")

      # 값 중 소수점이 없는 경우 0을 반환합니다.
          if dot_position1 == -1 or dot_position2 == -1:
              return 0

          # 소수점 이하 자릿수 계산
          decimal_places1 = len(str_value1) - dot_position1 - 1
          decimal_places2 = len(str_value2) - dot_position2 - 1

          # 소수점 이하 두 자리 수 중 최소값 찾기
          min_decimal_places = min(decimal_places1, decimal_places2)

          # 소수점 이하 자릿수가 일치하도록 카운트를 초기화
          matching_count = 0

          # 소수점 이하 문자 비교
          for i in range(1, min_decimal_places + 1):
              if str_value1[dot_position1 + i] == str_value2[dot_position2 + i]:
                  matching_count += 1
              else:
                  break

          return matching_count

      # 필요한 라이브러리 가져오기
      import numpy as np
      import matplotlib.pyplot as plt
      from sklearn.linear_model import Lasso
      from sklearn.metrics import r2_score,mean_absolute_error,mean_squared_error
      import onnx
      import onnxruntime as ort
      from skl2onnx import convert_sklearn
      from skl2onnx.common.data_types import FloatTensorType
      from skl2onnx.common.data_types import DoubleTensorType
      from sys import argv

      # 모델 저장 경로를 정의합니다.
      data_path = argv[0]
      last_index = data_path.rfind("\\")+ 1
      data_path = data_path[0:last_index]

      # 회귀를 위한 합성 데이터 생성
      X = np.arange(0,100,1).reshape(-1,1)
      y = 4*X+ 10*np.sin(X*0.5)

      model_name = "Lasso"
      onnx_model_filename = data_path + "lasso"

      # Lasso 모델 만들기
      lasso_model = Lasso()

      # 데이터에 모델 맞추기
      lasso_model.fit(X, y)

      # 전체 데이터 집합에 대한 값 예측
      y_pred = lasso_model.predict(X)

      # 모델의 성능 평가
      r2 = r2_score(y, y_pred)
      MSE = MEAN_SQUARE_ERROR(Y, Y_PED)
      MAE = MEAN_ABSOLUTE_ERROR(Y, Y_PED)

      print("\n"+model_name+" Original model (double)")
      print("R-squared (Coefficient of determination):", r2)
      print("Mean Absolute Error:", mae)
      print("Mean Squared Error:", mse)

      # ONNX 모델로 변환 (float)
      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.

      initial_type_float = [('float_input', FloatTensorType([None, X.shape[1]])]]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_float = convert_sklearn(lasso_model, initial_types=initial_type_float, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_float.onnx"
      onnx.save_model(onnx_model_float, onnx_filename)

      print("\n"+model_name+" ONNX model (float)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.
      initial_type_float = X.astype(np.float32)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_float = onnx_session.run([output_name], {input_name: initial_type_float})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_float = r2_score(y, y_pred_onnx_float)
      mse_onnx_float = mean_squared_error(y, y_pred_onnx_float)
      mae_onnx_float = mean_absolute_error(y, y_pred_onnx_float)
      print("R-squared (Coefficient of determination)", r2_onnx_float)
      print("Mean Absolute Error:", mae_onnx_float)
      print("Mean Squared Error:", mse_onnx_float)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_float))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_float))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_float))
      print("float ONNX model precision: ",compare_decimal_places(mae, mae_onnx_float))

      # 그림 크기 설정
      plt.figure(figsize=(8,5))
      # 원본 데이터와 회귀선을 그림으로 표시합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with float ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_float.png')

      # ONNX 모델로 변환 (double)
      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.

      initial_type_double = [('double_input', DoubleTensorType([None, X.shape[1]]))]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_double = convert_sklearn(lasso_model, initial_types=initial_type_double, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_double.onnx"
      onnx.save_model(onnx_model_double, onnx_filename)

      print("\n"+model_name+" ONNX model (double)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.
      initial_type_double = X.astype(np.float64)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_double = onnx_session.run([output_name], {input_name: initial_type_double})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_double = r2_score(y, y_pred_onnx_double)
      mse_onnx_double = mean_squared_error(y, y_pred_onnx_double)
      mae_onnx_double = mean_absolute_error(y, y_pred_onnx_double)
      print("R-squared (Coefficient of determination)", r2_onnx_double)
      print("Mean Absolute Error:", mae_onnx_double)
      print("Mean Squared Error:", mse_onnx_double)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_double))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_double))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_double))
      print("double ONNX model precision: ",compare_decimal_places(mae, mae_onnx_double))

      # 그림 크기 설정
      plt.figure(figsize=(8, 5))
      # 원본 데이터와 회귀선을 그림으로 표시합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with double ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_double.png')

      출력:

      Python  Lasso Original model (double)
      Python  R-squared (Coefficient of determination): 0.9962381735682287
      Python  Mean Absolute Error: 6.346393791922984
      Python  Mean Squared Error: 49.77934029129379
      Python  
      Python  Lasso ONNX model (float)
      Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\lasso_float.onnx
      Python  Information about input tensors in ONNX:
      Python  1. Name: float_input, Data Type: tensor(float), Shape: [None, 1]
      Python  Information about output tensors in ONNX:
      Python  1. Name: variable, Data Type: tensor(float), Shape: [None, 1]
      Python  R-squared (Coefficient of determination) 0.9962381720269486
      Python  Mean Absolute Error: 6.346395056911361
      Python  Mean Squared Error: 49.77936068668213
      Python  R^2 matching decimal places:  8
      Python  MAE matching decimal places:  5
      Python  MSE matching decimal places:  4
      Python  float ONNX model precision:  5
      Python  
      Python  Lasso ONNX model (double)
      Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\lasso_double.onnx
      Python  Information about input tensors in ONNX:
      Python  1. Name: double_input, Data Type: tensor(double), Shape: [None, 1]
      Python  Information about output tensors in ONNX:
      Python  1. Name: variable, Data Type: tensor(double), Shape: [None, 1]
      Python  R-squared (Coefficient of determination) 0.9962381735682287
      Python  Mean Absolute Error: 6.346393791922984
      Python  Mean Squared Error: 49.77934029129379
      Python  R^2 matching decimal places:  16
      Python  MAE matching decimal places:  15
      Python  MSE matching decimal places:  14
      Python  double ONNX model precision:  15
      


      Fig.30. 올가미.py(float ONNX)의 결과

      그림 30. 올가미.py 결과(float ONNX)


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

      이 코드는 저장된 lasso_float.onnx lasso_double.onnx를 실행하고 MQL5에서 회귀 메트릭을 사용하는 것을 보여줍니다.

      //+------------------------------------------------------------------+
      //|                                                        Lasso.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"
      
      #define   ModelName          "Lasso"
      #define   ONNXFilenameFloat  "lasso_float.onnx"
      #define   ONNXFilenameDouble "lasso_double.onnx"
      
      #resource ONNXFilenameFloat  as const uchar ExtModelFloat[];
      #resource ONNXFilenameDouble as const uchar ExtModelDouble[];
      
      #define   TestFloatModel  1
      #define   TestDoubleModel 2
      
      //+------------------------------------------------------------------+
      //| Calculate regression using float values                          |
      //+------------------------------------------------------------------+
      bool RunModelFloat(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         float input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=(float)input_vector[k];
      //--- prepare output tensor
         float output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      //+------------------------------------------------------------------+
      //| Calculate regression using double values                         |
      //+------------------------------------------------------------------+
      bool RunModelDouble(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         double input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=input_vector[k];
      //--- prepare output tensor
         double output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      
      //+------------------------------------------------------------------+
      //| Generate synthetic data                                          |
      //+------------------------------------------------------------------+
      bool GenerateData(const int n,vector &x,vector &y)
        {
         if(n<=0)
            return(false);
      //--- prepare arrays
         x.Resize(n);
         y.Resize(n);
      //---
         for(int i=0; i<n; i++)
           {
            x[i]=(double)1.0*i;
            y[i]=(double)(4*x[i] + 10*sin(x[i]*0.5));
           }
      //---
         return(true);
        }
      
      //+------------------------------------------------------------------+
      //| TestRegressionModel                                              |
      //+------------------------------------------------------------------+
      bool TestRegressionModel(const string model_name,const int model_type)
        {
      //---
         long  model=INVALID_HANDLE;
         ulong flags=ONNX_DEFAULT;
      
         if(model_type==TestFloatModel)
           {
            PrintFormat("\nTesting ONNX float: %s (%s)",model_name,ONNXFilenameFloat);
            model=OnnxCreateFromBuffer(ExtModelFloat,flags);
           }
         else
            if(model_type==TestDoubleModel)
              {
               PrintFormat("\nTesting ONNX double: %s (%s)",model_name,ONNXFilenameDouble);
               model=OnnxCreateFromBuffer(ExtModelDouble,flags);
              }
            else
              {
               PrintFormat("Model type is not incorrect.");
               return(false);
              }
      //--- check
         if(model==INVALID_HANDLE)
           {
            PrintFormat("model_name=%s OnnxCreate error %d",model_name,GetLastError());
            return(false);
           }
      //---
         vector x_values= {};
         vector y_true= {};
         vector y_predicted= {};
      //---
         int n=100;
         GenerateData(n,x_values,y_true);
      //---
         bool run_result=false;
         if(model_type==TestFloatModel)
           {
            run_result=RunModelFloat(model,x_values,y_predicted);
           }
         else
            if(model_type==TestDoubleModel)
              {
               run_result=RunModelDouble(model,x_values,y_predicted);
              }
      //---
         if(run_result)
           {
            PrintFormat("MQL5:   R-Squared (Coefficient of determination): %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_R2));
            PrintFormat("MQL5:   Mean Absolute Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MAE));
            PrintFormat("MQL5:   Mean Squared Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MSE));
           }
         else
            PrintFormat("Error %d",GetLastError());
      //--- release model
         OnnxRelease(model);
      //---
         return(true);
        }
      //+------------------------------------------------------------------+
      //| Script program start function                                    |
      //+------------------------------------------------------------------+
      int OnStart(void)
        {
      //--- test ONNX regression model for float
         TestRegressionModel(ModelName,TestFloatModel);
      //--- test ONNX regression model for double
         TestRegressionModel(ModelName,TestDoubleModel);
      //---
         return(0);
        }
      //+------------------------------------------------------------------+

      출력:

      Lasso (EURUSD,H1)       Testing ONNX float: Lasso (lasso_float.onnx)
      Lasso (EURUSD,H1)       MQL5:   R-Squared (Coefficient of determination): 0.9962381720269486
      Lasso (EURUSD,H1)       MQL5:   Mean Absolute Error: 6.3463950569113612
      Lasso (EURUSD,H1)       MQL5:   Mean Squared Error: 49.7793606866821037
      Lasso (EURUSD,H1)       
      Lasso (EURUSD,H1)       Testing ONNX double: Lasso (lasso_double.onnx)
      Lasso (EURUSD,H1)       MQL5:   R-Squared (Coefficient of determination): 0.9962381735682287
      Lasso (EURUSD,H1)       MQL5:   Mean Absolute Error: 6.3463937919229840
      Lasso (EURUSD,H1)       MQL5:   Mean Squared Error: 49.7793402912937850
      

      파이썬에서 원래 double 모델과 비교:

      Testing ONNX float: Lasso (lasso_float.onnx)
      Python  Mean Absolute Error: 6.346393791922984
      MQL5:   Mean Absolute Error: 6.3463950569113612
      
      Testing ONNX double: Lasso (lasso_double.onnx)
      Python  Mean Absolute Error: 6.346393791922984
      MQL5:   Mean Absolute Error: 6.3463937919229840

      ONNX float MAE의 정확도: 소수점 이하 5자리, 정확도 ONNX 더블 MAE: 소수점 이하 15자리.


      2.1.8.3. lasso_float.onnx 및 lasso_double.onnx의 ONNX 표현


      그림 31. Netron에서 lasso_float.onnx의 ONNX 표현

      그림 31. 네트론에서 lasso_float.onnx의 ONNX 표현



      Fig.32. Netron에서 lasso_double.onnx의 ONNX 표현

      그림 32. 네트론에서 lasso_double.onnx의 ONNX 표현



      2.1.9. sklearn.linear_model.LassoCV

      LassoCV는 Lasso 메서드(최소 절대 축소 및 선택 연산자)의 변형으로 교차 검증을 사용하여 정규화 하이퍼파라미터(알파)의 최적 값을 자동으로 선택하는 방식입니다.

      이 메서드를 사용하면 모델의 차원 축소(중요한 피처 선택)와 과적합 방지 사이의 균형을 찾을 수 있으므로 회귀 작업에 유용합니다.

      LassoCV의 작동 원리:

      1. 입력 데이터: LassoCV는 피처(독립 변수) 및 해당 목표 변수 값을 포함한 원본 데이터 집합으로 시작합니다.
      2. 초기화: LassoCV는 낮은 값부터 높은 값까지 다양한 정규화 하이퍼파라미터(알파) 값을 초기화합니다.
      3. 교차 유효성 검사: LassoCV는 각 알파 값에 대해 교차 검증을 수행하여 모델의 성능을 평가합니다. 평균 제곱 오차(MSE) 또는 결정 계수(R^2)와 같은 메트릭이 일반적으로 사용됩니다.
      4. 최적의 알파 선택하기: LassoCV는 교차 검증을 통해 모델이 최고의 성능을 달성하는 알파 값을 선택합니다.
      5. 모델 교육: Lasso 모델은 선택한 알파 값을 사용하여 덜 중요한 피처를 제외하고 L1 정규화를 적용하여 학습합니다.
      6. 예측 생성하기: 학습 후에는 이 모델을 사용하여 새로운 데이터의 목표 변수 값을 예측할 수 있습니다.

      LassoCV의 장점:

      • 자동 알파 선택: LassoCV는 교차 검증을 통해 최적의 알파 값을 자동으로 선택하여 모델 튜닝을 간소화합니다.
      • 피처 선택: LassoCV는 가장 중요한 피처를 자동으로 선택해 모델의 크기를 줄이고 해석을 단순화합니다.
      • 정규화: 이 메서드는 L1 정규화를 통해 모델 과적합을 방지합니다.

      LassoCV의 한계:

      • 선형 모델: LassoCV는 선형 모델을 구축하기 때문에 복잡한 비선형 관계를 모델링하는 데 부족할 수 있습니다.
      • 잡음 민감도: 이 메서드는 데이터의 이상값에 민감할 수 있습니다.
      • 다중 선형성 처리 불가: 피처의 상관관계가 높은 경우 LassoCV는 다중 선형성 문제에 직면할 수 있습니다.

      LassoCV는 해석 가능성을 유지하고 과적합을 방지하면서 가장 중요한 피처를 선택하고 모델의 차원을 줄이는 것이 중요한 회귀 작업에 유용합니다.


      2.1.9.1. LassoCV 모델을 생성하고 float 및 double용 ONNX로 내보내기 위한 코드

      이 코드는 sklearn.linear_model.LassoCV 모델을 생성하고 합성 데이터로 학습시키고 모델을 ONNX 형식으로 저장하고 플로트 및 더블 입력 데이터를 모두 사용하여 예측을 수행합니다. 또한 원본 모델과 ONNX로 내보낸 모델 모두의 정확도를 평가합니다.

      # LassoCV.py
      # 이 코드는 LassoCV 모델을 학습하고 ONNX 형식(float 및 double 모두)으로 내보내고 ONNX 모델을 사용하여 예측하는 과정을 보여 줍니다.
      # Copyright 2023, MetaQuotes Ltd.
      # https://www.mql5.com


      # 소수점 이하 자릿수를 비교학 위한 함수
      def compare_decimal_places(value1, value2):
      # 두 값을 모두 문자열로 변환
          str_value1 = str(value1)
          str_value2 = str(value2)

      # 문자열에서 소수점 위치 찾기
          dot_position1 = str_value1.find(".")
          dot_position2 = str_value2.find(".")

      # 값 중 소수점이 없는 경우 0을 반환합니다.
          if dot_position1 == -1 or dot_position2 == -1:
              return 0

          # 소수점 이하 자릿수 계산
          decimal_places1 = len(str_value1) - dot_position1 - 1
          decimal_places2 = len(str_value2) - dot_position2 - 1

          # 소수점 이하 두 자리 수 중 최소값 찾기
          min_decimal_places = min(decimal_places1, decimal_places2)

          # 소수점 이하 자릿수가 일치하도록 카운트를 초기화
          matching_count = 0

          # 소수점 이하 문자 비교
          for i in range(1, min_decimal_places + 1):
              if str_value1[dot_position1 + i] == str_value2[dot_position2 + i]:
                  matching_count += 1
              else:
                  break

          return matching_count

      # 필요한 라이브러리 가져오기
      import numpy as np
      import matplotlib.pyplot as plt
      from sklearn.linear_model import LassoCV
      from sklearn.metrics import r2_score,mean_absolute_error,mean_squared_error
      import onnx
      import onnxruntime as ort
      from skl2onnx import convert_sklearn
      from skl2onnx.common.data_types import FloatTensorType
      from skl2onnx.common.data_types import DoubleTensorType
      from sys import argv

      # 모델 저장 경로를 정의합니다.
      data_path = argv[0]
      last_index = data_path.rfind("\\")+ 1
      data_path = data_path[0:last_index]

      # 회귀를 위한 합성 데이터 생성
      X = np.arange(0,100,1).reshape(-1,1)
      y = 4*X + 10*np.sin(X*0.5)

      model_name = "LassoCV"
      onnx_model_filename = data_path + "lasso_cv"

      # 올가미 CV 회귀 모델 만들기
      lassocv_regressor_model = LassoCV()

      # 데이터에 모델 맞추기
      lassocv_regressor_model.fit(X, y.ravel())

      # 전체 데이터 집합에 대한 값 예측
      y_pred = lassocv_regressor_model.predict(X)

      # 모델의 성능 평가
      r2 = r2_score(y, y_pred)
      MSE = MEAN_SQUARE_ERROR(Y, Y_PED)
      MAE = MEAN_ABSOLUTE_ERROR(Y, Y_PED)

      print("\n"+model_name+" Original model (double)")
      print("R-squared (Coefficient of determination):", r2)
      print("Mean Absolute Error:", mae)
      print("Mean Squared Error:", mse)

      # ONNX 모델로 변환 (float)
      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.

      initial_type_float = [('float_input', FloatTensorType([None, X.shape[1]])]]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_float = convert_sklearn(lassocv_regressor_model, initial_types=initial_type_float, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_float.onnx"
      onnx.save_model(onnx_model_float, onnx_filename)

      print("\n"+model_name+" ONNX model (float)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.
      initial_type_float = X.astype(np.float32)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_float = onnx_session.run([output_name], {input_name: initial_type_float})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_float = r2_score(y, y_pred_onnx_float)
      mse_onnx_float = mean_squared_error(y, y_pred_onnx_float)
      mae_onnx_float = mean_absolute_error(y, y_pred_onnx_float)
      print("R-squared (Coefficient of determination)", r2_onnx_float)
      print("Mean Absolute Error:", mae_onnx_float)
      print("Mean Squared Error:", mse_onnx_float)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_float))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_float))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_float))
      print("float ONNX model precision: ",compare_decimal_places(mae, mae_onnx_float))

      # 그림 크기 설정
      plt.figure(figsize=(8,5))
      # 원본 데이터와 회귀선을 그림으로 표시합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with float ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_float.png')

      # ONNX 모델로 변환 (double)
      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.

      initial_type_double = [('double_input', DoubleTensorType([None, X.shape[1]]))]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_double = convert_sklearn(lassocv_regressor_model, initial_types=initial_type_double, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_double.onnx"
      onnx.save_model(onnx_model_double, onnx_filename)

      print("\n"+model_name+" ONNX model (double)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.
      initial_type_double = X.astype(np.float64)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_double = onnx_session.run([output_name], {input_name: initial_type_double})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_double = r2_score(y, y_pred_onnx_double)
      mse_onnx_double = mean_squared_error(y, y_pred_onnx_double)
      mae_onnx_double = mean_absolute_error(y, y_pred_onnx_double)
      print("R-squared (Coefficient of determination)", r2_onnx_double)
      print("Mean Absolute Error:", mae_onnx_double)
      print("Mean Squared Error:", mse_onnx_double)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_double))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_double))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_double))
      print("double ONNX model precision: ",compare_decimal_places(mae, mae_onnx_double))

      # 그림 크기 설정
      plt.figure(figsize=(8,5))
      # 원본 데이터와 회귀선을 그림으로 표시합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with double ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_double.png')

      출력:

      Python  LassoCV Original model (double)
      Python  R-squared (Coefficient of determination): 0.9962241428413416
      Python  Mean Absolute Error: 6.33567334453819
      Python  Mean Squared Error: 49.96500551028169
      Python  
      Python  LassoCV ONNX model (float)
      Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\lasso_cv_float.onnx
      Python  Information about input tensors in ONNX:
      Python  1. Name: float_input, Data Type: tensor(float), Shape: [None, 1]
      Python  Information about output tensors in ONNX:
      Python  1. Name: variable, Data Type: tensor(float), Shape: [None, 1]
      Python  R-squared (Coefficient of determination) 0.996224142876629
      Python  Mean Absolute Error: 6.335673221332177
      Python  Mean Squared Error: 49.96500504333324
      Python  R^2 matching decimal places:  10
      Python  MAE matching decimal places:  6
      Python  ONNX: MSE matching decimal places:  6
      Python  float ONNX model precision:  6
      Python  
      Python  LassoCV ONNX model (double)
      Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\lasso_cv_double.onnx
      Python  Information about input tensors in ONNX:
      Python  1. Name: double_input, Data Type: tensor(double), Shape: [None, 1]
      Python  Information about output tensors in ONNX:
      Python  1. Name: variable, Data Type: tensor(double), Shape: [None, 1]
      Python  R-squared (Coefficient of determination) 0.9962241428413416
      Python  Mean Absolute Error: 6.33567334453819
      Python  Mean Squared Error: 49.96500551028169
      Python  R^2 matching decimal places:  16
      Python  MAE matching decimal places:  14
      Python  MSE matching decimal places:  14
      Python  double ONNX model precision:  14
      

      Fig.33. LassoCV.py 결과(float ONNX)

      그림 33. LassoCV.py의 결과(float ONNX)



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

      이 코드는 저장된 lasso_cv_float.onnxlasso_cv_double.onnx 모델을 실행하고 MQL5에서 회귀 메트릭을 사용하는 것을 보여줍니다.

      //+------------------------------------------------------------------+
      //|                                                      LassoCV.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"
      
      #define   ModelName          "LassoCV"
      #define   ONNXFilenameFloat  "lasso_cv_float.onnx"
      #define   ONNXFilenameDouble "lasso_cv_double.onnx"
      
      #resource ONNXFilenameFloat  as const uchar ExtModelFloat[];
      #resource ONNXFilenameDouble as const uchar ExtModelDouble[];
      
      #define   TestFloatModel  1
      #define   TestDoubleModel 2
      
      //+------------------------------------------------------------------+
      //| Calculate regression using float values                          |
      //+------------------------------------------------------------------+
      bool RunModelFloat(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         float input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=(float)input_vector[k];
      //--- prepare output tensor
         float output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      //+------------------------------------------------------------------+
      //| Calculate regression using double values                         |
      //+------------------------------------------------------------------+
      bool RunModelDouble(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         double input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=input_vector[k];
      //--- prepare output tensor
         double output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      
      //+------------------------------------------------------------------+
      //| Generate synthetic data                                          |
      //+------------------------------------------------------------------+
      bool GenerateData(const int n,vector &x,vector &y)
        {
         if(n<=0)
            return(false);
      //--- prepare arrays
         x.Resize(n);
         y.Resize(n);
      //---
         for(int i=0; i<n; i++)
           {
            x[i]=(double)1.0*i;
            y[i]=(double)(4*x[i] + 10*sin(x[i]*0.5));
           }
      //---
         return(true);
        }
      
      //+------------------------------------------------------------------+
      //| TestRegressionModel                                              |
      //+------------------------------------------------------------------+
      bool TestRegressionModel(const string model_name,const int model_type)
        {
      //---
         long  model=INVALID_HANDLE;
         ulong flags=ONNX_DEFAULT;
      
         if(model_type==TestFloatModel)
           {
            PrintFormat("\nTesting ONNX float: %s (%s)",model_name,ONNXFilenameFloat);
            model=OnnxCreateFromBuffer(ExtModelFloat,flags);
           }
         else
            if(model_type==TestDoubleModel)
              {
               PrintFormat("\nTesting ONNX double: %s (%s)",model_name,ONNXFilenameDouble);
               model=OnnxCreateFromBuffer(ExtModelDouble,flags);
              }
            else
              {
               PrintFormat("Model type is not incorrect.");
               return(false);
              }
      //--- check
         if(model==INVALID_HANDLE)
           {
            PrintFormat("model_name=%s OnnxCreate error %d",model_name,GetLastError());
            return(false);
           }
      //---
         vector x_values= {};
         vector y_true= {};
         vector y_predicted= {};
      //---
         int n=100;
         GenerateData(n,x_values,y_true);
      //---
         bool run_result=false;
         if(model_type==TestFloatModel)
           {
            run_result=RunModelFloat(model,x_values,y_predicted);
           }
         else
            if(model_type==TestDoubleModel)
              {
               run_result=RunModelDouble(model,x_values,y_predicted);
              }
      //---
         if(run_result)
           {
            PrintFormat("MQL5:   R-Squared (Coefficient of determination): %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_R2));
            PrintFormat("MQL5:   Mean Absolute Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MAE));
            PrintFormat("MQL5:   Mean Squared Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MSE));
           }
         else
            PrintFormat("Error %d",GetLastError());
      //--- release model
         OnnxRelease(model);
      //---
         return(true);
        }
      //+------------------------------------------------------------------+
      //| Script program start function                                    |
      //+------------------------------------------------------------------+
      int OnStart(void)
        {
      //--- test ONNX regression model for float
         TestRegressionModel(ModelName,TestFloatModel);
      //--- test ONNX regression model for double
         TestRegressionModel(ModelName,TestDoubleModel);
      //---
         return(0);
        }
      //+------------------------------------------------------------------+

      출력:

      2023.10.26 22:14:00.736 LassoCV (EURUSD,H1)     Testing ONNX float: LassoCV (lasso_cv_float.onnx)
      2023.10.26 22:14:00.739 LassoCV (EURUSD,H1)     MQL5:   R-Squared (Coefficient of determination): 0.9962241428766290
      2023.10.26 22:14:00.739 LassoCV (EURUSD,H1)     MQL5:   Mean Absolute Error: 6.3356732213321800
      2023.10.26 22:14:00.739 LassoCV (EURUSD,H1)     MQL5:   Mean Squared Error: 49.9650050433332211
      2023.10.26 22:14:00.748 LassoCV (EURUSD,H1)     
      2023.10.26 22:14:00.748 LassoCV (EURUSD,H1)     Testing ONNX double: LassoCV (lasso_cv_double.onnx)
      2023.10.26 22:14:00.753 LassoCV (EURUSD,H1)     MQL5:   R-Squared (Coefficient of determination): 0.9962241428413416
      2023.10.26 22:14:00.753 LassoCV (EURUSD,H1)     MQL5:   Mean Absolute Error: 6.3356733445381899
      2023.10.26 22:14:00.753 LassoCV (EURUSD,H1)     MQL5:   Mean Squared Error: 49.9650055102816992
      

      파이썬에서 원래 double 모델과 비교:

      Testing ONNX float: LassoCV (lasso_cv_float.onnx)
      Python  Mean Absolute Error: 6.33567334453819
      MQL5:   Mean Absolute Error: 6.3356732213321800
              
      Testing ONNX double: LassoCV (lasso_cv_double.onnx)
      Python  Mean Absolute Error: 6.33567334453819
      MQL5:   Mean Absolute Error: 6.3356733445381899

      ONNX float MAE의 정확도: 소수점 이하 6자리, 정확도 ONNX double MAE: 소수점 이하 13자리.


      2.1.9.3. lasso_cv_float.onnx 및 lasso_cv_double.onnx의 ONNX 표현

      Fig.34. Netron에서 lasso_cv_float.onnx의 ONNX 표현

      그림 34. 네트론에서 lasso_cv_float.onnx의 ONNX 표현


      Fig.35. Netron에서 lasso_cv_double.onnx의 ONNX 표현

      그림 35. 네트론에서 lasso_cv_double.onnx의 ONNX 표현



      2.1.10. sklearn.linear_model.LassoLars

      LassoLars 두 가지 메서드를 조합한 것입니다: Lasso(최소 절대 축소 및 선택 연산자) 및 LARS(최소 각도 회귀).

      이 메서드는 회귀 작업에 사용되며 두 알고리즘의 장점을 결합하여 피처 선택과 모델 차원 축소를 동시에 수행할 수 있습니다.

      LassoLars의 작동 원리:

      1. 입력 데이터: LassoCV는 피처(독립 변수) 및 해당 목표 변수 값을 포함한 원본 데이터 집합으로 시작합니다.
      2. 초기화: LassoLars는 활성 피처가 없는 널 모델로 시작합니다. 모든 계수는 0으로 설정됩니다.
      3. 단계별 피처 선택: LARS 메서드와 유사하게 LassoLars는 각 단계에서 모델 잔차와 가장 상관관계가 높은 피처를 선택하여 모델에 추가합니다. 그런 다음 이 피처의 계수는 최소 제곱 메서드를 사용하여 조정됩니다.
      4. L1 정규화 적용: LassoLars는 단계적 피처 선택과 동시에 L1 정규화를 적용하여 계수 절대값의 합에 페널티를 추가합니다. 이를 통해 복잡한 관계를 모델링하고 가장 중요한 피처를 선택할 수 있습니다.
      5. 예측하기: 학습 후에는 이 모델을 사용하여 새로운 데이터의 목표 변수 값을 예측할 수 있습니다.

      LassoLars의 장점:

      • 피처 선택: LassoLars는 가장 중요한 피처를 자동으로 선택하고 모델의 차원을 줄여 과적합을 방지하고 해석을 단순화합니다.
      • 해석 가능성: 이 메서드는 모델의 해석 가능성을 유지하여 어떤 피처가 포함되고 목표 변수에 어떤 영향을 미치는지 쉽게 파악할 수 있습니다.
      • 정규화: LassoLars는 L1 정규화를 적용하여 과적합을 방지하고 모델의 일반화를 향상시킵니다.

      LassoLars의 한계:

      • 선형 모델: LassoLars는 선형 모델을 구축하기 때문에 복잡한 비선형 관계를 모델링하는 데 부족할 수 있습니다.
      • 소음에 대한 민감도: 이 메서드는 데이터의 이상값에 민감할 수 있습니다.
      • 계산의 복잡성: 각 단계에서 피처를 선택하고 정규화를 적용하려면 단순한 선형 회귀보다 더 많은 계산 리소스가 필요할 수 있습니다.

      LassoLars는 가장 중요한 피처를 선택하고 모델의 차원을 줄이며 해석 가능성을 유지하는 것이 중요한 회귀 작업에 유용합니다.


      2.1.10.1. LassoLars 모델을 생성하고 float 및 double용 ONNX로 내보내기 위한 코드

      이 코드는 sklearn.linear_model.LassoLars 모델을 생성하고 합성 데이터로 학습시키고 모델을 ONNX 형식으로 저장하고 플로트 및 더블 입력 데이터를 모두 사용하여 예측을 수행합니다. 또한 원본 모델과 ONNX로 내보낸 모델 모두의 정확도를 평가합니다.

      # LassoLars.py
      # 이 코드는 LassoLars 모델을 학습하고 ONNX 형식(float 및 double 모두)으로 내보내고 ONNX 모델을 사용하여 예측하는 과정을 보여 줍니다.
      # Copyright 2023, MetaQuotes Ltd.
      # https://www.mql5.com


      # 소수점 이하 자릿수를 비교학 위한 함수
      def compare_decimal_places(value1, value2):
      # 두 값을 모두 문자열로 변환
          str_value1 = str(value1)
          str_value2 = str(value2)

      # 문자열에서 소수점 위치 찾기
          dot_position1 = str_value1.find(".")
          dot_position2 = str_value2.find(".")

      # 값 중 소수점이 없는 경우 0을 반환합니다.
          if dot_position1 == -1 or dot_position2 == -1:
              return 0

          # 소수점 이하 자릿수 계산
          decimal_places1 = len(str_value1) - dot_position1 - 1
          decimal_places2 = len(str_value2) - dot_position2 - 1

          # 소수점 이하 두 자리 수 중 최소값 찾기
          min_decimal_places = min(decimal_places1, decimal_places2)

          # 소수점 이하 자릿수가 일치하도록 카운트를 초기화
          matching_count = 0

          # 소수점 이하 문자 비교
          for i in range(1, min_decimal_places + 1):
              if str_value1[dot_position1 + i] == str_value2[dot_position2 + i]:
                  matching_count += 1
              else:
                  break

          return matching_count

      # 필요한 라이브러리 가져오기
      import numpy as np
      import matplotlib.pyplot as plt
      from sklearn.linear_model import LassoLars
      from sklearn.metrics import r2_score,mean_absolute_error,mean_squared_error
      import onnx
      import onnxruntime as ort
      from skl2onnx import convert_sklearn
      from skl2onnx.common.data_types import FloatTensorType
      from skl2onnx.common.data_types import DoubleTensorType
      from sys import argv

      # 모델 저장 경로를 정의합니다.
      data_path = argv[0]
      last_index = data_path.rfind("\\")+ 1
      data_path = data_path[0:last_index]

      # 회귀를 위한 합성 데이터 생성
      X = np.arange(0,100,1).reshape(-1,1)
      y = 4*X + 10*np.sin(X*0.5)

      model_name = "LassoLars"
      onnx_model_filename = data_path + "lasso_lars"

      # LassoLars 회귀 모델 만들기
      lassolars_regressor_model = LassoLars(alpha=0.1)

      # 데이터에 모델 맞추기
      lassolars_regressor_model.fit(X, y.ravel())

      # 전체 데이터 집합에 대한 값 예측
      y_pred = lassolars_regressor_model.predict(X)

      # 모델의 성능 평가
      r2 = r2_score(y, y_pred)
      MSE = MEAN_SQUARE_ERROR(Y, Y_PED)
      MAE = MEAN_ABSOLUTE_ERROR(Y, Y_PED)

      print("\n"+model_name+" Original model (double)")
      print("R-squared (Coefficient of determination):", r2)
      print("Mean Absolute Error:", mae)
      print("Mean Squared Error:", mse)

      # ONNX 모델로 변환 (float)
      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.

      initial_type_float = [('float_input', FloatTensorType([None, X.shape[1]])]]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_float = convert_sklearn(lassolars_regressor_model, initial_types=initial_type_float, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_float.onnx"
      onnx.save_model(onnx_model_float, onnx_filename)

      print("\n"+model_name+" ONNX model (float)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.
      initial_type_float = X.astype(np.float32)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_float = onnx_session.run([output_name], {input_name: initial_type_float})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_float = r2_score(y, y_pred_onnx_float)
      mse_onnx_float = mean_squared_error(y, y_pred_onnx_float)
      mae_onnx_float = mean_absolute_error(y, y_pred_onnx_float)
      print("R-squared (Coefficient of determination)", r2_onnx_float)
      print("Mean Absolute Error:", mae_onnx_float)
      print("Mean Squared Error:", mse_onnx_float)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_float))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_float))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_float))
      print("float ONNX model precision: ",compare_decimal_places(mae, mae_onnx_float))

      # 그림 크기 설정
      plt.figure(figsize=(8, 5))
      # 원본 데이터와 회귀선을 그림으로 표시합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with float ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_float.png')

      # ONNX 모델로 변환 (double)
      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.

      initial_type_double = [('double_input', DoubleTensorType([None, X.shape[1]]))]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_double = convert_sklearn(lassolars_regressor_model, initial_types=initial_type_double, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_double.onnx"
      onnx.save_model(onnx_model_double, onnx_filename)

      print("\n"+model_name+" ONNX model (double)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.
      initial_type_double = X.astype(np.float64)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_double = onnx_session.run([output_name], {input_name: initial_type_double})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_double = r2_score(y, y_pred_onnx_double)
      mse_onnx_double = mean_squared_error(y, y_pred_onnx_double)
      mae_onnx_double = mean_absolute_error(y, y_pred_onnx_double)
      print("R-squared (Coefficient of determination)", r2_onnx_double)
      print("Mean Absolute Error:", mae_onnx_double)
      print("Mean Squared Error:", mse_onnx_double)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_double))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_double))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_double))
      print("double ONNX model precision: ",compare_decimal_places(mae, mae_onnx_double))

      # 그림 크기 설정
      plt.figure(figsize=(8, 5))
      # plot the original data and the regression line
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with double ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_double.png')

      출력:

      Python  LassoLars Original model (double)
      Python  R-squared (Coefficient of determination): 0.9962382633544077
      Python  Mean Absolute Error: 6.3476035128950805
      Python  Mean Squared Error: 49.778152172481896
      Python  
      Python  LassoLars ONNX model (float)
      Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\lasso_lars_float.onnx
      Python  Information about input tensors in ONNX:
      Python  1. Name: float_input, Data Type: tensor(float), Shape: [None, 1]
      Python  Information about output tensors in ONNX:
      Python  1. Name: variable, Data Type: tensor(float), Shape: [None, 1]
      Python  R-squared (Coefficient of determination) 0.9962382635045889
      Python  Mean Absolute Error: 6.3476034814795375
      Python  Mean Squared Error: 49.77815018516975
      Python  R^2 matching decimal places:  9
      Python  MAE matching decimal places:  6
      Python  MSE matching decimal places:  5
      Python  float ONNX model precision:  6
      Python  
      Python  LassoLars ONNX model (double)
      Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\lasso_lars_double.onnx
      Python  Information about input tensors in ONNX:
      Python  1. Name: double_input, Data Type: tensor(double), Shape: [None, 1]
      Python  Information about output tensors in in ONNX:
      Python  1. Name: variable, Data Type: tensor(double), Shape: [None, 1]
      Python  R-squared (Coefficient of determination) 0.9962382633544077
      Python  Mean Absolute Error: 6.3476035128950805
      Python  Mean Squared Error: 49.778152172481896
      Python  R^2 matching decimal places:  16
      Python  MAE matching decimal places:  16
      Python  MSE matching decimal places:  15
      Python  double ONNX model precision:  16
      

      Fig.36. Result of the LassoLars.py (float)

      그림 36. LassoLars.py의 결과(float)


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

      이 코드는 저장된 lasso_lars_float.onnxlasso_lars_double.onnx를 실행하고 MQL5에서 회귀 메트릭을 사용하는 것을 보여줍니다.

      //+------------------------------------------------------------------+
      //|                                                    LassoLars.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"
      
      #define   ModelName          "LassoLars"
      #define   ONNXFilenameFloat  "lasso_lars_float.onnx"
      #define   ONNXFilenameDouble "lasso_lars_double.onnx"
      
      #resource ONNXFilenameFloat  as const uchar ExtModelFloat[];
      #resource ONNXFilenameDouble as const uchar ExtModelDouble[];
      
      #define   TestFloatModel  1
      #define   TestDoubleModel 2
      
      //+------------------------------------------------------------------+
      //| Calculate regression using float values                          |
      //+------------------------------------------------------------------+
      bool RunModelFloat(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         float input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=(float)input_vector[k];
      //--- prepare output tensor
         float output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      //+------------------------------------------------------------------+
      //| Calculate regression using double values                         |
      //+------------------------------------------------------------------+
      bool RunModelDouble(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         double input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=input_vector[k];
      //--- prepare output tensor
         double output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      
      //+------------------------------------------------------------------+
      //| Generate synthetic data                                          |
      //+------------------------------------------------------------------+
      bool GenerateData(const int n,vector &x,vector &y)
        {
         if(n<=0)
            return(false);
      //--- prepare arrays
         x.Resize(n);
         y.Resize(n);
      //---
         for(int i=0; i<n; i++)
           {
            x[i]=(double)1.0*i;
            y[i]=(double)(4*x[i] + 10*sin(x[i]*0.5));
           }
      //---
         return(true);
        }
      
      //+------------------------------------------------------------------+
      //| TestRegressionModel                                              |
      //+------------------------------------------------------------------+
      bool TestRegressionModel(const string model_name,const int model_type)
        {
      //---
         long  model=INVALID_HANDLE;
         ulong flags=ONNX_DEFAULT;
      
         if(model_type==TestFloatModel)
           {
            PrintFormat("\nTesting ONNX float: %s (%s)",model_name,ONNXFilenameFloat);
            model=OnnxCreateFromBuffer(ExtModelFloat,flags);
           }
         else
            if(model_type==TestDoubleModel)
              {
               PrintFormat("\nTesting ONNX double: %s (%s)",model_name,ONNXFilenameDouble);
               model=OnnxCreateFromBuffer(ExtModelDouble,flags);
              }
            else
              {
               PrintFormat("Model type is not incorrect.");
               return(false);
              }
      //--- check
         if(model==INVALID_HANDLE)
           {
            PrintFormat("model_name=%s OnnxCreate error %d",model_name,GetLastError());
            return(false);
           }
      //---
         vector x_values= {};
         vector y_true= {};
         vector y_predicted= {};
      //---
         int n=100;
         GenerateData(n,x_values,y_true);
      //---
         bool run_result=false;
         if(model_type==TestFloatModel)
           {
            run_result=RunModelFloat(model,x_values,y_predicted);
           }
         else
            if(model_type==TestDoubleModel)
              {
               run_result=RunModelDouble(model,x_values,y_predicted);
              }
      //---
         if(run_result)
           {
            PrintFormat("MQL5:   R-Squared (Coefficient of determination): %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_R2));
            PrintFormat("MQL5:   Mean Absolute Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MAE));
            PrintFormat("MQL5:   Mean Squared Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MSE));
           }
         else
            PrintFormat("Error %d",GetLastError());
      //--- release model
         OnnxRelease(model);
      //---
         return(true);
        }
      //+------------------------------------------------------------------+
      //| Script program start function                                    |
      //+------------------------------------------------------------------+
      int OnStart(void)
        {
      //--- test ONNX regression model for float
         TestRegressionModel(ModelName,TestFloatModel);
      //--- test ONNX regression model for double
         TestRegressionModel(ModelName,TestDoubleModel);
      //---
         return(0);
        }
      //+------------------------------------------------------------------+

      출력:

      LassoLars (EURUSD,H1)   Testing ONNX float: LassoLars (lasso_lars_float.onnx)
      LassoLars (EURUSD,H1)   MQL5:   R-Squared (Coefficient of determination): 0.9962382635045889
      LassoLars (EURUSD,H1)   MQL5:   Mean Absolute Error: 6.3476034814795375
      LassoLars (EURUSD,H1)   MQL5:   Mean Squared Error: 49.7781501851697357
      LassoLars (EURUSD,H1)   
      LassoLars (EURUSD,H1)   Testing ONNX double: LassoLars (lasso_lars_double.onnx)
      LassoLars (EURUSD,H1)   MQL5:   R-Squared (Coefficient of determination): 0.9962382633544077
      LassoLars (EURUSD,H1)   MQL5:   Mean Absolute Error: 6.3476035128950858
      LassoLars (EURUSD,H1)   MQL5:   Mean Squared Error: 49.7781521724819029
      

      파이썬에서 원래 double 모델과 비교:

      Testing ONNX float: LassoLars (lasso_lars_float.onnx)
      Python  Mean Absolute Error: 6.3476035128950805
      MQL5:   Mean Absolute Error: 6.3476034814795375
      
      Testing ONNX double: LassoLars (lasso_lars_double.onnx)
      Python  Mean Absolute Error: 6.3476035128950805
      MQL5:   Mean Absolute Error: 6.3476035128950858

      ONNX float MAE의 정확도: 소수점 이하 6자리, 정확도 ONNX double MAE: 소수점 이하 14자리.


      2.1.10.3. lasso_lars_float.onnx 및 lasso_lars_double.onnx의 ONNX 표현


      그림 37. Netron에서 lasso_lars_float.onnx의 ONNX 표현

      그림 37. 네트론에서 lasso_lars_float.onnx의 ONNX 표현



      Fig.38. Netron에서 lasso_lars_double.onnx의 ONNX 표현

      그림 38. 네트론에서 lasso_lars_double.onnx의 ONNX 표현


      2.1.11. sklearn.linear_model.LassoLarsCV

      LassoLarsCV는 교차 검증을 통해 최적의 정규화 하이퍼파라미터(알파)를 자동으로 선택하는 Lasso(최소 절대 축소 및 선택 연산자)와 LARS(최소 각도 회귀)를 결합한 방식입니다.

      이 메서드는 두 알고리즘의 장점을 결합하여 피처 선택과 정규화를 고려하여 모델에 대한 최적의 알파 값을 결정할 수 있습니다.

      LassoLarsCV의 작동 원리:

      1. 입력 데이터: LassoCV는 피처(독립 변수) 및 해당 목표 변수 값을 포함한 원본 데이터 집합으로 시작합니다.
      2. 초기화: 올가미LarsCV는 모든 계수가 0으로 설정된 널 모델로 시작합니다.
      3. 알파 범위의 정의: 하이퍼파라미터 알파의 값 범위가 결정되며 이는 선택 과정에서 고려됩니다. 일반적으로 알파 값의 로그 눈금이 사용됩니다.
      4. 교차 유효성 검사: 선택한 범위의 각 알파 값에 대해 올가미LarsCV는 교차 검증을 수행하여 이 알파 값으로 모델의 성능을 평가합니다. 일반적으로 평균 제곱 오차(MSE) 또는 결정 계수(R^2)와 같은 메트릭이 사용됩니다.
      5. 최적의 알파 선택: LassoLarsCV는 교차 검증 결과를 바탕으로 모델이 최고의 성능을 달성하는 알파 값을 선택합니다.
      6. 모델 교육: LassoLars 모델은 선택된 알파 값을 사용하여 덜 중요한 피처을 제외하고 L1 정규화를 적용하여 학습합니다.
      7. 예측하기: 학습 후에는 이 모델을 사용하여 새로운 데이터의 목표 변수 값을 예측할 수 있습니다.

      LassoLarsCV의 장점:

      • 자동 알파 선택: LassoLarsCV는 교차 검증을 통해 최적의 하이퍼파라미터 알파를 자동으로 선택하므로 모델 튜닝이 간소화됩니다.
      • 피처 선택: LassoLarsCV는 가장 중요한 피처을 자동으로 선택하고 모델의 크기를 줄입니다.
      • 정규화: 이 메서드는 L1 정규화를 적용하여 과적합을 방지하고 모델의 일반화를 향상시킵니다.

      LassoLarsCV의 한계:

      • 선형 모델: LassoLarsCV는 선형 모델을 구축하므로 복잡한 비선형 관계를 모델링하는 데 부족할 수 있습니다.
      • 소음에 대한 민감도: 이 메서드는 데이터의 이상값에 민감할 수 있습니다.
      • 계산의 복잡성: 각 단계에서 피처를 선택하고 정규화를 적용하려면 단순한 선형 회귀보다 더 많은 계산 리소스가 필요할 수 있습니다.

      LassoLarsCV는 가장 중요한 피처을 선택하고 모델의 차원을 줄이고 과적합을 방지하고 모델의 하이퍼파라미터를 자동으로 조정하는 것이 필수적인 회귀 작업에 유용합니다.


      2.1.11.1. 올가미LassoLarsCV 모델을 생성하고 float 및 double으로 ONNX로 내보내기 위한 코드

      이 코드는 sklearn.linear_model.LassoLarsCV 모델을 생성하고 합성 데이터로 학습시키고 모델을 ONNX 형식으로 저장하고 플로트 및 더블 입력 데이터를 모두 사용하여 예측을 수행합니다. 또한 원본 모델과 ONNX로 내보낸 모델 모두의 정확도를 평가합니다.

      # LassoLarsCV.py
      # 이 코드는 LassoLars 모델을 학습하고 ONNX 형식(float 및 double 모두)으로 내보내고 ONNX 모델을 사용하여 예측하는 과정을 보여 줍니다.
      # Copyright 2023, MetaQuotes Ltd.
      # https://www.mql5.com


      # 소수점 이하 자릿수를 비교학 위한 함수
      def compare_decimal_places(value1, value2):
      # 두 값을 모두 문자열로 변환
          str_value1 = str(value1)
          str_value2 = str(value2)

      # 문자열에서 소수점 위치 찾기
          dot_position1 = str_value1.find(".")
          dot_position2 = str_value2.find(".")

      # 값 중 소수점이 없는 경우 0을 반환합니다.
          if dot_position1 == -1 or dot_position2 == -1:
              return 0

          # 소수점 이하 자릿수 계산
          decimal_places1 = len(str_value1) - dot_position1 - 1
          decimal_places2 = len(str_value2) - dot_position2 - 1

          # 소수점 이하 두 자리 수 중 최소값 찾기
          min_decimal_places = min(decimal_places1, decimal_places2)

          # 소수점 이하 자릿수가 일치하도록 카운트를 초기화
          matching_count = 0

          # 소수점 이하 문자 비교
          for i in range(1, min_decimal_places + 1):
              if str_value1[dot_position1 + i] == str_value2[dot_position2 + i]:
                  matching_count += 1
              else:
                  break

          return matching_count

      # 필요한 라이브러리 가져오기
      import numpy as np
      import matplotlib.pyplot as plt
      에서 sklearn.linear_model import LassoLarsCV
      from sklearn.metrics import r2_score,mean_absolute_error,mean_squared_error
      import onnx
      import onnxruntime as ort
      from skl2onnx import convert_sklearn
      from skl2onnx.common.data_types import FloatTensorType
      from skl2onnx.common.data_types import DoubleTensorType
      from sys import argv

      # 모델 저장 경로를 정의합니다.
      data_path = argv[0]
      last_index = data_path.rfind("\\")+ 1
      data_path = data_path[0:last_index]

      # 회귀를 위한 합성 데이터 생성
      X = np.arange(0,100,1).reshape(-1,1)
      y = 4*X + 10*np.sin(X*0.5)

      model_name = "LassoLarsCV"
      onnx_model_filename = data_path + "lasso_lars_cv"

      # LassoLarsCV 회귀 모델 만들기
      lassolars_cv_regressor_model = LassoLarsCV(cv=5)

      # 데이터에 모델 맞추기
      lassolars_cv_regressor_model.fit(X, y.ravel())

      # 전체 데이터 집합에 대한 값 예측
      y_pred = lassolars_cv_regressor_model.predict(X)

      # 모델의 성능 평가
      r2 = r2_score(y, y_pred)
      MSE = MEAN_SQUARE_ERROR(Y, Y_PED)
      MAE = MEAN_ABSOLUTE_ERROR(Y, Y_PED)

      print("\n"+model_name+" Original model (double)")
      print("R-squared (Coefficient of determination):", r2)
      print("Mean Absolute Error:", mae)
      print("Mean Squared Error:", mse)

      # ONNX 모델로 변환 (float)
      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.

      initial_type_float = [('float_input', FloatTensorType([None, X.shape[1]])]]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_float = convert_sklearn(lassolars_cv_regressor_model, initial_types=initial_type_float, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_float.onnx"
      onnx.save_model(onnx_model_float, onnx_filename)

      print("\n"+model_name+" ONNX model (float)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.
      initial_type_float = X.astype(np.float32)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_float = onnx_session.run([output_name], {input_name: initial_type_float})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_float = r2_score(y, y_pred_onnx_float)
      mse_onnx_float = mean_squared_error(y, y_pred_onnx_float)
      mae_onnx_float = mean_absolute_error(y, y_pred_onnx_float)
      print("R-squared (Coefficient of determination)", r2_onnx_float)
      print("Mean Absolute Error:", mae_onnx_float)
      print("Mean Squared Error:", mse_onnx_float)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_float))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_float))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_float))
      print("float ONNX model precision: ",compare_decimal_places(mae, mae_onnx_float))

      # 그림 크기 설정
      plt.figure(figsize=(8, 5))
      # 원본 데이터와 회귀선을 그림으로 표시합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with float ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_float.png')

      # ONNX 모델로 변환 (double)
      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.

      initial_type_double = [('double_input', DoubleTensorType([None, X.shape[1]]))]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_double = convert_sklearn(lassolars_cv_regressor_model, initial_types=initial_type_double, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_double.onnx"
      onnx.save_model(onnx_model_double, onnx_filename)

      print("\n"+model_name+" ONNX model (double)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.
      initial_type_double = X.astype(np.float64)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_double = onnx_session.run([output_name], {input_name: initial_type_double})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_double = r2_score(y, y_pred_onnx_double)
      mse_onnx_double = mean_squared_error(y, y_pred_onnx_double)
      mae_onnx_double = mean_absolute_error(y, y_pred_onnx_double)
      print("R-squared (Coefficient of determination)", r2_onnx_double)
      print("Mean Absolute Error:", mae_onnx_double)
      print("Mean Squared Error:", mse_onnx_double)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_double))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_double))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_double))
      print("double ONNX model precision: ",compare_decimal_places(mae, mae_onnx_double))

      # 그림 크기 설정
      plt.figure(figsize=(8, 5))
      # 원본 데이터와 회귀선을 그림으로 표시합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with double ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_double.png')

      출력:

      Python  LassoLarsCV Original model (double)
      Python  R-squared (Coefficient of determination): 0.9962382642612767
      Python  Mean Absolute Error: 6.3477379221400145
      Python  Mean Squared Error: 49.77814017210321
      Python  
      Python  LassoLarsCV ONNX model (float)
      Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\lasso_lars_cv_float.onnx
      Python  Information about input tensors in ONNX:
      Python  1. Name: float_input, Data Type: tensor(float), Shape: [None, 1]
      Python  Information about output tensors in ONNX:
      Python  1. Name: variable, Data Type: tensor(float), Shape: [None, 1]
      Python  R-squared (Coefficient of determination) 0.9962382640824089
      Python  Mean Absolute Error: 6.347737845846069
      Python  Mean Squared Error: 49.778142539016564
      Python  R^2 matching decimal places:  9
      Python  MAE matching decimal places:  6
      Python  MSE matching decimal places:  5
      Python  float ONNX model precision:  6
      Python  
      Python  LassoLarsCV ONNX model (double)
      Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\lasso_lars_cv_double.onnx
      Python  Information about input tensors in ONNX:
      Python  1. Name: double_input, Data Type: tensor(double), Shape: [None, 1]
      Python  Information about output tensors in ONNX:
      Python  1. Name: variable, Data Type: tensor(double), Shape: [None, 1]
      Python  R-squared (Coefficient of determination) 0.9962382642612767
      Python  Mean Absolute Error: 6.3477379221400145
      Python  Mean Squared Error: 49.77814017210321
      Python  R^2 matching decimal places:  16
      Python  MAE matching decimal places:  16
      Python  MSE matching decimal places:  14
      Python  double ONNX model precision:  16
      

      Fig.39. LassoLarsCV.py(float ONNX) (float ONNX) 결과

      그림 39. LassoLarsCV.py 결과(float ONNX)


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

      이 코드는 저장된 lasso_lars_cv_float.onnxlasso_lars_cv_double.onnx 모델을 실행하고 MQL5에서 회귀 메트릭을 사용하는 것을 보여 줍니다.

      //+------------------------------------------------------------------+
      //|                                                  LassoLarsCV.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"
      
      #define   ModelName          "LassoLarsCV"
      #define   ONNXFilenameFloat  "lasso_lars_cv_float.onnx"
      #define   ONNXFilenameDouble "lasso_lars_cv_double.onnx"
      
      #resource ONNXFilenameFloat  as const uchar ExtModelFloat[];
      #resource ONNXFilenameDouble as const uchar ExtModelDouble[];
      
      #define   TestFloatModel  1
      #define   TestDoubleModel 2
      
      //+------------------------------------------------------------------+
      //| Calculate regression using float values                          |
      //+------------------------------------------------------------------+
      bool RunModelFloat(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         float input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=(float)input_vector[k];
      //--- prepare output tensor
         float output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      //+------------------------------------------------------------------+
      //| Calculate regression using double values                         |
      //+------------------------------------------------------------------+
      bool RunModelDouble(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         double input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=input_vector[k];
      //--- prepare output tensor
         double output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      
      //+------------------------------------------------------------------+
      //| Generate synthetic data                                          |
      //+------------------------------------------------------------------+
      bool GenerateData(const int n,vector &x,vector &y)
        {
         if(n<=0)
            return(false);
      //--- prepare arrays
         x.Resize(n);
         y.Resize(n);
      //---
         for(int i=0; i<n; i++)
           {
            x[i]=(double)1.0*i;
            y[i]=(double)(4*x[i] + 10*sin(x[i]*0.5));
           }
      //---
         return(true);
        }
      
      //+------------------------------------------------------------------+
      //| TestRegressionModel                                              |
      //+------------------------------------------------------------------+
      bool TestRegressionModel(const string model_name,const int model_type)
        {
      //---
         long  model=INVALID_HANDLE;
         ulong flags=ONNX_DEFAULT;
      
         if(model_type==TestFloatModel)
           {
            PrintFormat("\nTesting ONNX float: %s (%s)",model_name,ONNXFilenameFloat);
            model=OnnxCreateFromBuffer(ExtModelFloat,flags);
           }
         else
            if(model_type==TestDoubleModel)
              {
               PrintFormat("\nTesting ONNX double: %s (%s)",model_name,ONNXFilenameDouble);
               model=OnnxCreateFromBuffer(ExtModelDouble,flags);
              }
            else
              {
               PrintFormat("Model type is not incorrect.");
               return(false);
              }
      //--- check
         if(model==INVALID_HANDLE)
           {
            PrintFormat("model_name=%s OnnxCreate error %d",model_name,GetLastError());
            return(false);
           }
      //---
         vector x_values= {};
         vector y_true= {};
         vector y_predicted= {};
      //---
         int n=100;
         GenerateData(n,x_values,y_true);
      //---
         bool run_result=false;
         if(model_type==TestFloatModel)
           {
            run_result=RunModelFloat(model,x_values,y_predicted);
           }
         else
            if(model_type==TestDoubleModel)
              {
               run_result=RunModelDouble(model,x_values,y_predicted);
              }
      //---
         if(run_result)
           {
            PrintFormat("MQL5:   R-Squared (Coefficient of determination): %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_R2));
            PrintFormat("MQL5:   Mean Absolute Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MAE));
            PrintFormat("MQL5:   Mean Squared Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MSE));
           }
         else
            PrintFormat("Error %d",GetLastError());
      //--- release model
         OnnxRelease(model);
      //---
         return(true);
        }
      //+------------------------------------------------------------------+
      //| Script program start function                                    |
      //+------------------------------------------------------------------+
      int OnStart(void)
        {
      //--- test ONNX regression model for float
         TestRegressionModel(ModelName,TestFloatModel);
      //--- test ONNX regression model for double
         TestRegressionModel(ModelName,TestDoubleModel);
      //---
         return(0);
        }
      //+------------------------------------------------------------------+

      출력:

      LassoLarsCV (EURUSD,H1) Testing ONNX float: LassoLarsCV (lasso_lars_cv_float.onnx)
      LassoLarsCV (EURUSD,H1) MQL5:   R-Squared (Coefficient of determination): 0.9962382640824089
      LassoLarsCV (EURUSD,H1) MQL5:   Mean Absolute Error: 6.3477378458460691
      LassoLarsCV (EURUSD,H1) MQL5:   Mean Squared Error: 49.7781425390165566
      LassoLarsCV (EURUSD,H1) 
      LassoLarsCV (EURUSD,H1) Testing ONNX double: LassoLarsCV (lasso_lars_cv_double.onnx)
      LassoLarsCV (EURUSD,H1) MQL5:   R-Squared (Coefficient of determination): 0.9962382642612767
      LassoLarsCV (EURUSD,H1) MQL5:   Mean Absolute Error: 6.3477379221400145
      LassoLarsCV (EURUSD,H1) MQL5:   Mean Squared Error: 49.7781401721031642
      

      파이썬에서 원래 double 모델과 비교:

      Testing ONNX float: LassoLarsCV (lasso_lars_cv_float.onnx)
      Python  Mean Absolute Error: 6.3477379221400145
      MQL5:   Mean Absolute Error: 6.3477378458460691
              
      Testing ONNX double: LassoLarsCV (lasso_lars_cv_double.onnx)
      Python  Mean Absolute Error: 6.3477379221400145
      MQL5:   Mean Absolute Error: 6.3477379221400145
      

      ONNX float MAE의 정확도: 소수점 이하 6자리, 정확도 ONNX double MAE: 소수점 이하 16자리.


      2.1.11.3. lasso_lars_cv_float.onnx 및 lasso_lars_cv_double.onnx의 ONNX 표현


      Fig.40. Netron에서 lasso_lars_cv_float.onnx의 ONNX 표현

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


      Fig.41. Netron에서 lasso_lars_cv_double.onnx의 ONNX 표현

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



      2.1.12. sklearn.linear_model.LassoLarsIC

      LassoLarsIC는 올가미(최소 절대 축소 및 선택 연산자)와 정보 기준(IC)을 결합하여 최적의 피처 집합을 자동으로 선택하는 회귀 메서드입니다.

      모델에 포함할 피처를 결정하기 위해 AIC(Akaike 정보 기준) 및 BIC(베이지안 정보 기준)와 같은 정보 기준을 활용하고 모델 계수를 추정하기 위해 L1 정규화를 적용합니다.

      LassoLarsIC의 작동 원리:

      1. 입력 데이터: LassoCV는 피처(독립 변수) 및 해당 목표 변수 값을 포함한 원본 데이터 집합으로 시작합니다.
      2. 초기화: LassoLarsIC는 null 모델로 시작하며 이는 활성 피처가 없음을 의미합니다. 모든 계수는 0으로 설정됩니다.
      3. 정보 기준을 사용한 피처 선택: 이 메서드는 빈 모델에서 시작하여 점차적으로 모델에 피처을 통합하면서 다양한 피처 세트에 대한 정보 기준(예: AIC 또는 BIC)을 평가합니다. 정보 기준은 데이터 적합성과 모델 복잡성 간의 절충점을 고려하여 모델의 품질을 평가합니다.
      4. 최적의 피처 세트 선택: LassoLarsIC는 정보 기준이 최고의 가치를 달성하는 피처 세트를 선택합니다. 이 피처 세트는 모델에 포함됩니다.
      5. L1 정규화 적용: 선택한 피처에 L1 정규화가 적용되어 모델 계수 추정에 도움이 됩니다.
      6. 예측하기: 학습 후에는 이 모델을 사용하여 새로운 데이터의 목표 변수 값을 예측할 수 있습니다.

      LassoLarsIC의 장점:

      • 자동 피처 선택: LassoLarsIC는 최적의 피처 세트를 자동으로 선택하여 모델의 치수를 줄이고 과적합을 방지합니다.
      • 정보 기준: 정보 기준을 사용하면 모델 품질과 복잡성의 균형을 맞출 수 있습니다.
      • 정규화: 이 메서드는 L1 정규화를 적용하여 과적합을 방지하고 모델의 일반화를 향상시킵니다.

      LassoLarsIC의 한계:

      • 선형 모델: LassoLarsIC는 선형 모델을 구축하므로 복잡한 비선형 관계를 모델링하는 데 부족할 수 있습니다.
      • 소음에 대한 민감도: 이 메서드는 데이터의 이상값에 민감할 수 있습니다.
      • 계산의 복잡성: 다양한 피처 세트에 대한 정보 기준을 평가하려면 추가적인 컴퓨팅 리소스가 필요할 수 있습니다.

      정보 기준에 따라 최적의 피처 집합을 자동으로 선택하고 모델의 차원을 줄이는 것이 중요한 회귀 작업에서 LassoLarsIC는 유용합니다.


      2.1.12.1. LassoLarsIC 모델을 생성하고 float 및 double용 ONNX로 내보내기 위한 코드

      이 코드는 sklearn.linear_model.LassoLarsIC 모델을 생성하고 합성 데이터로 학습시키고 모델을 ONNX 형식으로 저장하고 플로트 및 더블 입력 데이터를 모두 사용하여 예측을 수행합니다. 또한 원본 모델과 ONNX로 내보낸 모델 모두의 정확도를 평가합니다.

      # LassoLarsIC.py
      # 이 코드는 LassoLarsIC 모델을 학습하고 이를 ONNX 형식(float 및 double 모두)으로 내보내고 ONNX 모델을 사용하여 예측하는 과정을 보여 줍니다.
      # Copyright 2023, MetaQuotes Ltd.
      # https://www.mql5.com


      # 소수점 이하 자릿수를 비교학 위한 함수
      def compare_decimal_places(value1, value2):
      # 두 값을 모두 문자열로 변환
          str_value1 = str(value1)
          str_value2 = str(value2)

      # 문자열에서 소수점 위치 찾기
          dot_position1 = str_value1.find(".")
          dot_position2 = str_value2.find(".")

      # 값 중 소수점이 없는 경우 0을 반환합니다.
          if dot_position1 == -1 or dot_position2 == -1:
              return 0

          # 소수점 이하 자릿수 계산
          decimal_places1 = len(str_value1) - dot_position1 - 1
          decimal_places2 = len(str_value2) - dot_position2 - 1

          # 소수점 이하 두 자리 수 중 최소값 찾기
          min_decimal_places = min(decimal_places1, decimal_places2)

          # 소수점 이하 자릿수가 일치하도록 카운트를 초기화
          matching_count = 0

          # 소수점 이하 문자 비교
          for i in range(1, min_decimal_places + 1):
              if str_value1[dot_position1 + i] == str_value2[dot_position2 + i]:
                  matching_count += 1
              else:
                  break

          return matching_count

      # 필요한 라이브러리 가져오기
      import numpy as np
      import matplotlib.pyplot as plt
      from sklearn.linear_model import LassoLarsIC
      from sklearn.metrics import r2_score,mean_absolute_error,mean_squared_error
      import onnx
      import onnxruntime as ort
      from skl2onnx import convert_sklearn
      from skl2onnx.common.data_types import FloatTensorType
      from skl2onnx.common.data_types import DoubleTensorType
      from sys import argv

      # 모델 저장 경로를 정의합니다.
      data_path = argv[0]
      last_index = data_path.rfind("\\")+ 1
      data_path = data_path[0:last_index]

      # 회귀를 위한 합성 데이터 생성
      X = np.arange(0,100,1).reshape(-1,1)
      y = 4*X + 10*np.sin(X*0.5)

      model_name="LassoLarsIC"
      onnx_model_filename = data_path + "lasso_lars_ic"

      # LassoLarsIC 회귀 모델 만들기
      lasso_lars_ic_regressor_model = LassoLarsIC(criterion='aic')

      # 데이터에 모델 맞추기
      lasso_lars_ic_regressor_model.fit(X, y.ravel())

      # 전체 데이터 집합에 대한 값 예측
      y_pred = lasso_lars_ic_regressor_model.predict(X)

      # 모델의 성능 평가
      r2 = r2_score(y, y_pred)
      MSE = MEAN_SQUARE_ERROR(Y, Y_PED)
      MAE = MEAN_ABSOLUTE_ERROR(Y, Y_PED)

      print("\n"+model_name+" Original model (double)")
      print("R-squared (Coefficient of determination):", r2)
      print("Mean Absolute Error:", mae)
      print("Mean Squared Error:", mse)

      # ONNX 모델로 변환 (float)
      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.

      initial_type_float = [('float_input', FloatTensorType([None, X.shape[1]])]]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_float = convert_sklearn(lasso_lars_ic_regressor_model, initial_types=initial_type_float, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_float.onnx"
      onnx.save_model(onnx_model_float, onnx_filename)

      print("\n"+model_name+" ONNX model (float)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.
      initial_type_float = X.astype(np.float32)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_float = onnx_session.run([output_name], {input_name: initial_type_float})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_float = r2_score(y, y_pred_onnx_float)
      mse_onnx_float = mean_squared_error(y, y_pred_onnx_float)
      mae_onnx_float = mean_absolute_error(y, y_pred_onnx_float)
      print("R-squared (Coefficient of determination)", r2_onnx_float)
      print("Mean Absolute Error:", mae_onnx_float)
      print("Mean Squared Error:", mse_onnx_float)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_float))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_float))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_float))
      print("float ONNX model precision: ",compare_decimal_places(mae, mae_onnx_float))

      # 그림 크기 설정
      plt.figure(figsize=(8, 5))
      # 원본 데이터와 회귀선을 그림으로 표시합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with float ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_float.png')

      # ONNX 모델로 변환 (double)
      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.

      initial_type_double = [('double_input', DoubleTensorType([None, X.shape[1]]))]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_double = convert_sklearn(lasso_lars_ic_regressor_model, initial_types=initial_type_double, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_double.onnx"
      onnx.save_model(onnx_model_double, onnx_filename)

      print("\n"+model_name+" ONNX model (double)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.
      initial_type_double = X.astype(np.float64)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_double = onnx_session.run([output_name], {input_name: initial_type_double})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_double = r2_score(y, y_pred_onnx_double)
      mse_onnx_double = mean_squared_error(y, y_pred_onnx_double)
      mae_onnx_double = mean_absolute_error(y, y_pred_onnx_double)
      print("R-squared (Coefficient of determination)", r2_onnx_double)
      print("Mean Absolute Error:", mae_onnx_double)
      print("Mean Squared Error:", mse_onnx_double)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_double))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_double))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_double))
      print("double ONNX model precision: ",compare_decimal_places(mae, mae_onnx_double))

      # 그림 크기 설정
      plt.figure(figsize=(8, 5))
      # 원본 데이터와 회귀선을 그림으로 표시합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with double ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_double.png')

      출력:

      Python  LassoLarsIC Original model (double)
      Python  R-squared (Coefficient of determination): 0.9962382642613388
      Python  Mean Absolute Error: 6.347737926336425
      Python  Mean Squared Error: 49.778140171281784
      Python  
      Python  LassoLarsIC ONNX model (float)
      Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\lasso_lars_ic_float.onnx
      Python  Information about input tensors in ONNX:
      Python  1. Name: float_input, Data Type: tensor(float), Shape: [None, 1]
      Python  Information about output tensors in ONNX:
      Python  1. Name: variable, Data Type: tensor(float), Shape: [None, 1]
      Python  R-squared (Coefficient of determination) 0.9962382641628886
      Python  Mean Absolute Error: 6.3477377671679385
      Python  Mean Squared Error: 49.77814147404787
      Python  R^2 matching decimal places:  9
      Python  MAE matching decimal places:  6
      Python  MSE matching decimal places:  5
      Python  float ONNX model precision:  6
      Python  
      Python  LassoLarsIC ONNX model (double)
      Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\lasso_lars_ic_double.onnx
      Python  Information about input tensors in ONNX:
      Python  1. Name: double_input, Data Type: tensor(double), Shape: [None, 1]
      Python  Information about output tensors in ONNX:
      Python  1. Name: variable, Data Type: tensor(double), Shape: [None, 1]
      Python  R-squared (Coefficient of determination) 0.9962382642613388
      Python  Mean Absolute Error: 6.347737926336425
      Python  Mean Squared Error: 49.778140171281784
      Python  R^2 matching decimal places:  16
      Python  MAE matching decimal places:  15
      Python  MSE matching decimal places:  15
      Python  double ONNX model precision:  15
      

      Fig.42. LassoLarsIC.py (float ONNX) 결과

      그림 42. LassoLarsIC.py 결과(float ONNX)


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

      이 코드는 저장된 lasso_lars_ic_float.onnxlasso_lars_ic_double.onnx 모델을 실행하고 MQL5에서 회귀 메트릭을 사용하는 것을 보여 줍니다.

      //+------------------------------------------------------------------+
      //|                                                  LassoLarsIC.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"
      
      #define   ModelName          "LassoLarsIC"
      #define   ONNXFilenameFloat  "lasso_lars_ic_float.onnx"
      #define   ONNXFilenameDouble "lasso_lars_ic_double.onnx"
      
      #resource ONNXFilenameFloat  as const uchar ExtModelFloat[];
      #resource ONNXFilenameDouble as const uchar ExtModelDouble[];
      
      #define   TestFloatModel  1
      #define   TestDoubleModel 2
      
      //+------------------------------------------------------------------+
      //| Calculate regression using float values                          |
      //+------------------------------------------------------------------+
      bool RunModelFloat(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         float input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=(float)input_vector[k];
      //--- prepare output tensor
         float output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      //+------------------------------------------------------------------+
      //| Calculate regression using double values                         |
      //+------------------------------------------------------------------+
      bool RunModelDouble(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         double input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=input_vector[k];
      //--- prepare output tensor
         double output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      
      //+------------------------------------------------------------------+
      //| Generate synthetic data                                          |
      //+------------------------------------------------------------------+
      bool GenerateData(const int n,vector &x,vector &y)
        {
         if(n<=0)
            return(false);
      //--- prepare arrays
         x.Resize(n);
         y.Resize(n);
      //---
         for(int i=0; i<n; i++)
           {
            x[i]=(double)1.0*i;
            y[i]=(double)(4*x[i] + 10*sin(x[i]*0.5));
           }
      //---
         return(true);
        }
      
      //+------------------------------------------------------------------+
      //| TestRegressionModel                                              |
      //+------------------------------------------------------------------+
      bool TestRegressionModel(const string model_name,const int model_type)
        {
      //---
         long  model=INVALID_HANDLE;
         ulong flags=ONNX_DEFAULT;
      
         if(model_type==TestFloatModel)
           {
            PrintFormat("\nTesting ONNX float: %s (%s)",model_name,ONNXFilenameFloat);
            model=OnnxCreateFromBuffer(ExtModelFloat,flags);
           }
         else
            if(model_type==TestDoubleModel)
              {
               PrintFormat("\nTesting ONNX double: %s (%s)",model_name,ONNXFilenameDouble);
               model=OnnxCreateFromBuffer(ExtModelDouble,flags);
              }
            else
              {
               PrintFormat("Model type is not incorrect.");
               return(false);
              }
      //--- check
         if(model==INVALID_HANDLE)
           {
            PrintFormat("model_name=%s OnnxCreate error %d",model_name,GetLastError());
            return(false);
           }
      //---
         vector x_values= {};
         vector y_true= {};
         vector y_predicted= {};
      //---
         int n=100;
         GenerateData(n,x_values,y_true);
      //---
         bool run_result=false;
         if(model_type==TestFloatModel)
           {
            run_result=RunModelFloat(model,x_values,y_predicted);
           }
         else
            if(model_type==TestDoubleModel)
              {
               run_result=RunModelDouble(model,x_values,y_predicted);
              }
      //---
         if(run_result)
           {
            PrintFormat("MQL5:   R-Squared (Coefficient of determination): %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_R2));
            PrintFormat("MQL5:   Mean Absolute Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MAE));
            PrintFormat("MQL5:   Mean Squared Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MSE));
           }
         else
            PrintFormat("Error %d",GetLastError());
      //--- release model
         OnnxRelease(model);
      //---
         return(true);
        }
      //+------------------------------------------------------------------+
      //| Script program start function                                    |
      //+------------------------------------------------------------------+
      int OnStart(void)
        {
      //--- test ONNX regression model for float
         TestRegressionModel(ModelName,TestFloatModel);
      //--- test ONNX regression model for double
         TestRegressionModel(ModelName,TestDoubleModel);
      //---
         return(0);
        }
      //+------------------------------------------------------------------+

      출력:

      LassoLarsIC (EURUSD,H1) Testing ONNX float: LassoLarsIC (lasso_lars_ic_float.onnx)
      LassoLarsIC (EURUSD,H1) MQL5:   R-Squared (Coefficient of determination): 0.9962382641628886
      LassoLarsIC (EURUSD,H1) MQL5:   Mean Absolute Error: 6.3477377671679385
      LassoLarsIC (EURUSD,H1) MQL5:   Mean Squared Error: 49.7781414740478638
      LassoLarsIC (EURUSD,H1) 
      LassoLarsIC (EURUSD,H1) Testing ONNX double: LassoLarsIC (lasso_lars_ic_double.onnx)
      LassoLarsIC (EURUSD,H1) MQL5:   R-Squared (Coefficient of determination): 0.9962382642613388
      LassoLarsIC (EURUSD,H1) MQL5:   Mean Absolute Error: 6.3477379263364302
      LassoLarsIC (EURUSD,H1) MQL5:   Mean Squared Error: 49.7781401712817768
      

      파이썬의 원래 배정밀도 모델과 비교:

      Testing ONNX float: LassoLarsIC (lasso_lars_ic_float.onnx)
      Python  Mean Absolute Error: 6.347737926336425
      MQL5:   Mean Absolute Error: 6.3477377671679385
       
      Testing ONNX double: LassoLarsIC (lasso_lars_ic_double.onnx)
      Python  Mean Absolute Error: 6.347737926336425
      MQL5:   Mean Absolute Error: 6.3477379263364302
      

      ONNX float MAE의 정확도: 소수점 이하 6자리, 정확도 ONNX double MAE: 소수점 이하 13자리.


      2.1.12.3. lasso_lars_ic_float.onnx 및 lasso_lars_ic_double.onnx의 ONNX 표현


      Fig.43. Netron에서 lasso_lars_ic_float.onnx의 ONNX 표현

      그림 43. 네트론에서 lasso_lars_ic_float.onnx의 ONNX 표현


      Fig.44. Netron에서 lasso_lars_ic_double.onnx의 ONNX 표현

      그림 44. 네트론에서 lasso_lars_ic_double.onnx의 ONNX 표현




      2.1.13. sklearn.linear_model.LinearRegression

      LinearRegression은 머신 러닝에서 회귀 작업에 가장 간단하고 널리 사용되는 메서드 중 하나로

      입력 피처의 선형 조합을 기반으로 대상 변수의 수치(연속형)를 예측하는 선형 모델을 구축하는 데 사용됩니다.

      선형 회귀의 작동 원리:

      1. 선형 모델: 선형 회귀 모델은 독립 변수(피처)와 대상 변수 사이에 선형 관계가 있다고 가정합니다. 이 관계는 선형 회귀 방정식으로 표현할 수 있습니다: y = β₀ + β₁x₁ + β₂x₂ + ... + βₚxₚ에서 y는 목표 변수, β₀ -는 절편 계수, β₁, β₂, ... βₚ -는 피처 계수, x₁, x₂, ... xₚ는 피처 값입니다.
      2. 매개변수 추정: 선형 회귀의 목표는 데이터에 가장 잘 맞는 계수 β₀, β₁, β₂, ... βₚ를 추정하는 것입니다. 이는 일반적으로 실제 값과 예측 값의 제곱 차이의 합을 최소화하는 OLS(최소제곱법) 메서드를 사용하여 수행됩니다.
      3. 모델 평가: 선형 회귀 모델의 품질을 평가하기 위해 평균 제곱 오차(MSE), 결정 계수(R²) 등 다양한 메트릭이 사용됩니다.

      선형 회귀의 장점:

      • 단순성 및 해석 가능성: 선형 회귀는 해석하기 쉬운 간단한 메서드로 각 피처가 대상 변수에 미치는 영향을 분석할 수 있습니다.
      • 빠른 훈련 및 예측 속도: 선형 회귀 모델은 학습 및 예측 속도가 빠르므로 대규모 데이터 세트에 적합합니다.
      • 적용 가능성: 선형 회귀는 다양한 회귀 작업에 성공적으로 적용될 수 있습니다.

      선형 회귀의 한계:

      • 선형성: 이 메서드는 피처와 대상 변수 간의 관계에 선형성이 있다고 가정하므로 복잡한 비선형 종속성을 모델링하는 데 충분하지 않을 수 있습니다.
      • 이상값에 대한 민감도: 선형 회귀는 데이터의 이상값에 민감하므로 모델의 품질에 영향을 줄 수 있습니다.

      선형 회귀는 입력 피처의 선형 조합을 기반으로 대상 변수의 수치 값을 예측하기 위해 선형 모델을 구성하는 간단하고 널리 사용되는 회귀 메서드입니다. 선형 관계가 있는 문제나 모델 해석 가능성이 중요한 경우에 적합합니다.


      2.1.13.1. LinearRegression 모델을 생성하고 float 및 double용 ONNX로 내보내기 위한 코드

      이 코드는 sklearn.linear_model.LinearRegression 모델을 생성하고 합성 데이터로 학습시키고 모델을 ONNX 형식으로 저장하고 플로트 및 더블 입력 데이터를 모두 사용하여 예측을 수행합니다. 또한 원본 모델과 ONNX로 내보낸 모델 모두의 정확도를 평가합니다.

      # LinearRegression.py
      # 이 코드는 선형 회귀 모델을 학습하고 이를 ONNX 형식(float 및 double 모두)으로 내보내고 ONNX 모델을 사용하여 예측하는 프로세스를 보여 줍니다.
      # Copyright 2023, MetaQuotes Ltd.
      # https://www.mql5.com


      # 소수점 이하 자릿수를 비교학 위한 함수
      def compare_decimal_places(value1, value2):
      # 두 값을 모두 문자열로 변환
          str_value1 = str(value1)
          str_value2 = str(value2)

      # 문자열에서 소수점 위치 찾기
          dot_position1 = str_value1.find(".")
          dot_position2 = str_value2.find(".")

      # 값 중 소수점이 없는 경우 0을 반환합니다.
          if dot_position1 == -1 or dot_position2 == -1:
              return 0

          # 소수점 이하 자릿수 계산
          decimal_places1 = len(str_value1) - dot_position1 - 1
          decimal_places2 = len(str_value2) - dot_position2 - 1

          # 소수점 이하 두 자리 수 중 최소값 찾기
          min_decimal_places = min(decimal_places1, decimal_places2)

          # 소수점 이하 자릿수가 일치하도록 카운트를 초기화
          matching_count = 0

          # 소수점 이하 문자 비교
          for i in range(1, min_decimal_places + 1):
              if str_value1[dot_position1 + i] == str_value2[dot_position2 + i]:
                  matching_count += 1
              else:
                  break

          return matching_count

      # 필요한 라이브러리 가져오기
      import numpy as np
      import matplotlib.pyplot as plt
      from sklearn.linear_model import LinearRegression
      from sklearn.metrics import r2_score,mean_absolute_error,mean_squared_error
      import onnx
      import onnxruntime as ort
      from skl2onnx import convert_sklearn
      from skl2onnx.common.data_types import FloatTensorType
      from skl2onnx.common.data_types import DoubleTensorType
      from sys import argv

      # 모델 저장 경로를 정의합니다.
      data_path = argv[0]
      last_index = data_path.rfind("\\")+ 1
      data_path = data_path[0:last_index]

      # 회귀를 위한 합성 데이터 생성
      X = np.arange(0,100,1).reshape(-1,1)
      y = 4*X + 10*np.sin(X*0.5)

      model_name = "LinearRegression"
      onnx_model_filename = data_path + "linear_regression"

      # 선형 회귀 모델 만들기
      linear_model = LinearRegression()

      # 데이터에 모델 맞추기
      linear_model.fit(X, y)

      # 전체 데이터 집합에 대한 값 예측
      y_pred = linear_model.predict(X)

      # 모델의 성능 평가
      r2 = r2_score(y, y_pred)
      MSE = MEAN_SQUARE_ERROR(Y, Y_PED)
      MAE = MEAN_ABSOLUTE_ERROR(Y, Y_PED)

      print("\n"+model_name+" Original model (double)")
      print("R-squared (Coefficient of determination):", r2)
      print("Mean Absolute Error:", mae)
      print("Mean Squared Error:", mse)

      # ONNX 모델로 변환 (float)
      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.

      initial_type_float = [('float_input', FloatTensorType([None, X.shape[1]]))]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_float = convert_sklearn(linear_model, initial_types=initial_type_float, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_float.onnx"
      onnx.save_model(onnx_model_float, onnx_filename)

      print("\n"+model_name+" ONNX model (float)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.
      initial_type_float = X.astype(np.float32)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_float = onnx_session.run([output_name], {input_name: initial_type_float})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_float = r2_score(y, y_pred_onnx_float)
      mse_onnx_float = mean_squared_error(y, y_pred_onnx_float)
      mae_onnx_float = mean_absolute_error(y, y_pred_onnx_float)
      print("R-squared (Coefficient of determination)", r2_onnx_float)
      print("Mean Absolute Error:", mae_onnx_float)
      print("Mean Squared Error:", mse_onnx_float)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_float))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_float))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_float))
      print("float ONNX model precision: ",compare_decimal_places(mae, mae_onnx_float))

      # 그림 크기 설정
      plt.figure(figsize=(8,5))
      # 원본 데이터와 회귀 데이터를 플로팅합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with float ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_float.png')

      # ONNX 모델로 변환 (double)
      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.

      initial_type_double = [('double_input', DoubleTensorType([None, X.shape[1]]))]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_double = convert_sklearn(linear_model, initial_types=initial_type_double, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_double.onnx"
      onnx.save_model(onnx_model_double, onnx_filename)

      print("\n"+model_name+" ONNX model (double)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.
      initial_type_double = X.astype(np.float64)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_double = onnx_session.run([output_name], {input_name: initial_type_double})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_double = r2_score(y, y_pred_onnx_double)
      mse_onnx_double = mean_squared_error(y, y_pred_onnx_double)
      mae_onnx_double = mean_absolute_error(y, y_pred_onnx_double)
      print("R-squared (Coefficient of determination)", r2_onnx_double)
      print("Mean Absolute Error:", mae_onnx_double)
      print("Mean Squared Error:", mse_onnx_double)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_double))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_double))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_double))
      print("double ONNX model precision: ",compare_decimal_places(mae, mae_onnx_double))

      # 그림 크기 설정
      plt.figure(figsize=(8,5))
      # 원본 데이터와 회귀 데이터를 플로팅합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with double ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_double.png')

      출력:

      Python  LinearRegression Original model (double)
      Python  R-squared (Coefficient of determination): 0.9962382642613388
      Python  Mean Absolute Error: 6.347737926336427
      Python  Mean Squared Error: 49.77814017128179
      Python  
      Python  LinearRegression ONNX model (float)
      Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\linear_regression_float.onnx
      Python  Information about input tensors in ONNX:
      Python  1. Name: float_input, Data Type: tensor(float), Shape: [None, 1]
      Python  Information about output tensors in ONNX:
      Python  1. Name: variable, Data Type: tensor(float), Shape: [None, 1]
      Python  R-squared (Coefficient of determination) 0.9962382641628886
      Python  Mean Absolute Error: 6.3477377671679385
      Python  Mean Squared Error: 49.77814147404787
      Python  R^2 matching decimal places:  9
      Python  MAE matching decimal places:  6
      Python  ONNX: MSE matching decimal places:  5
      Python  float ONNX model precision:  6
      Python  
      Python  LinearRegression ONNX model (double)
      Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\linear_regression_double.onnx
      Python  Information about input tensors in ONNX:
      Python  1. Name: double_input, Data Type: tensor(double), Shape: [None, 1]
      Python  Information about output tensors in ONNX:
      Python  1. Name: variable, Data Type: tensor(double), Shape: [None, 1]
      Python  R-squared (Coefficient of determination) 0.9962382642613388
      Python  Mean Absolute Error: 6.347737926336427
      Python  Mean Squared Error: 49.77814017128179
      Python  R^2 matching decimal places:  16
      Python  MAE matching decimal places:  15
      Python  MSE matching decimal places:  14
      Python  double ONNX model precision:  15
      

      그림.45.LinearRegression.py 결과 (float ONNX)

      그림.45.LinearRegression.py 결과(float ONNX)


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

      이 코드는 저장된 linear_regression_float.onnxlinear_regression_double.onnx를 실행하고 MQL5에서 회귀 메트릭을 사용하는 것을 보여줍니다.

      //+------------------------------------------------------------------+
      //|                                             LinearRegression.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"
      
      #define   ModelName          "LinearRegression"
      #define   ONNXFilenameFloat  "linear_regression_float.onnx"
      #define   ONNXFilenameDouble "linear_regression_double.onnx"
      
      #resource ONNXFilenameFloat  as const uchar ExtModelFloat[];
      #resource ONNXFilenameDouble as const uchar ExtModelDouble[];
      
      #define   TestFloatModel  1
      #define   TestDoubleModel 2
      
      //+------------------------------------------------------------------+
      //| Calculate regression using float values                          |
      //+------------------------------------------------------------------+
      bool RunModelFloat(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         float input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=(float)input_vector[k];
      //--- prepare output tensor
         float output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      //+------------------------------------------------------------------+
      //| Calculate regression using double values                         |
      //+------------------------------------------------------------------+
      bool RunModelDouble(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         double input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=input_vector[k];
      //--- prepare output tensor
         double output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      
      //+------------------------------------------------------------------+
      //| Generate synthetic data                                          |
      //+------------------------------------------------------------------+
      bool GenerateData(const int n,vector &x,vector &y)
        {
         if(n<=0)
            return(false);
      //--- prepare arrays
         x.Resize(n);
         y.Resize(n);
      //---
         for(int i=0; i<n; i++)
           {
            x[i]=(double)1.0*i;
            y[i]=(double)(4*x[i] + 10*sin(x[i]*0.5));
           }
      //---
         return(true);
        }
      
      //+------------------------------------------------------------------+
      //| TestRegressionModel                                              |
      //+------------------------------------------------------------------+
      bool TestRegressionModel(const string model_name,const int model_type)
        {
      //---
         long  model=INVALID_HANDLE;
         ulong flags=ONNX_DEFAULT;
      
         if(model_type==TestFloatModel)
           {
            PrintFormat("\nTesting ONNX float: %s (%s)",model_name,ONNXFilenameFloat);
            model=OnnxCreateFromBuffer(ExtModelFloat,flags);
           }
         else
            if(model_type==TestDoubleModel)
              {
               PrintFormat("\nTesting ONNX double: %s (%s)",model_name,ONNXFilenameDouble);
               model=OnnxCreateFromBuffer(ExtModelDouble,flags);
              }
            else
              {
               PrintFormat("Model type is not incorrect.");
               return(false);
              }
      //--- check
         if(model==INVALID_HANDLE)
           {
            PrintFormat("model_name=%s OnnxCreate error %d",model_name,GetLastError());
            return(false);
           }
      //---
         vector x_values= {};
         vector y_true= {};
         vector y_predicted= {};
      //---
         int n=100;
         GenerateData(n,x_values,y_true);
      //---
         bool run_result=false;
         if(model_type==TestFloatModel)
           {
            run_result=RunModelFloat(model,x_values,y_predicted);
           }
         else
            if(model_type==TestDoubleModel)
              {
               run_result=RunModelDouble(model,x_values,y_predicted);
              }
      //---
         if(run_result)
           {
            PrintFormat("MQL5:   R-Squared (Coefficient of determination): %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_R2));
            PrintFormat("MQL5:   Mean Absolute Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MAE));
            PrintFormat("MQL5:   Mean Squared Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MSE));
           }
         else
            PrintFormat("Error %d",GetLastError());
      //--- release model
         OnnxRelease(model);
      //---
         return(true);
        }
      //+------------------------------------------------------------------+
      //| Script program start function                                    |
      //+------------------------------------------------------------------+
      int OnStart(void)
        {
      //--- test ONNX regression model for float
         TestRegressionModel(ModelName,TestFloatModel);
      //--- test ONNX regression model for double
         TestRegressionModel(ModelName,TestDoubleModel);
      //---
         return(0);
        }
      //+------------------------------------------------------------------+

      출력:

      LinearRegression (EURUSD,H1)    Testing ONNX float: LinearRegression (linear_regression_float.onnx)
      LinearRegression (EURUSD,H1)    MQL5:   R-Squared (Coefficient of determination): 0.9962382641628886
      LinearRegression (EURUSD,H1)    MQL5:   Mean Absolute Error: 6.3477377671679385
      LinearRegression (EURUSD,H1)    MQL5:   Mean Squared Error: 49.7781414740478638
      LinearRegression (EURUSD,H1)    
      LinearRegression (EURUSD,H1)    Testing ONNX double: LinearRegression (linear_regression_double.onnx)
      LinearRegression (EURUSD,H1)    MQL5:   R-Squared (Coefficient of determination): 0.9962382642613388
      LinearRegression (EURUSD,H1)    MQL5:   Mean Absolute Error: 6.3477379263364266
      LinearRegression (EURUSD,H1)    MQL5:   Mean Squared Error: 49.7781401712817768
      

      파이썬의 원래 배정밀도 모델과 비교:

      Testing ONNX float: LinearRegression (linear_regression_float.onnx)
      Python  Mean Absolute Error: 6.347737926336427
      MQL5:   Mean Absolute Error: 6.3477377671679385
      
      Testing ONNX double: LinearRegression (linear_regression_double.onnx)
      Python  Mean Absolute Error: 6.347737926336427
      MQL5:   Mean Absolute Error: 6.3477379263364266
      

      ONNX float MAE의 정확도: 소수점 이하 6자리, 정확도 ONNX double MAE: 소수점 이하 14자리.


      2.1.13.3. linear_regression_float.onnx 및 linear_regression_double.onnx의 ONNX 표현


      Fig.46. Netron에서 linear_regression_float.onnx의 ONNX 표현

      그림 46. 네트론에서 linear_regression_float.onnx의 ONNX 표현



      그림 47. Netron에서 linear_regression_double.onnx의 ONNX 표현

      그림 47. 네트론에서 linear_regression_double.onnx의 ONNX 표현



      Ridge 및 RidgeCV 메서드에 대한 참고 사항

      Ridge와 RidgeCV는 Ridge 회귀의 정규화에 사용되는 머신 러닝의 두 가지 관련 메서드입니다. 이들은 유사한 기능을 공유하지만 사용법과 매개변수 튜닝에 차이가 있습니다.

      Ridge (Ridge Regression)의 작동 원리:

      • Ridge는 L2 정규화와 관련된 회귀 메서드입니다. 즉 모델에서 최소화한 손실 함수에 제곱 계수(L2 norm)의 합을 더한다는 의미입니다. 이 추가 정규화 항은 모델 계수의 크기를 줄여 과적합을 방지하는 데 도움이 됩니다.
      • 알파 매개변수 사용: Ridge 방식에서는 알파 매개변수(정규화 강도라고도 함)가 미리 설정되어 있으며 자동으로 변경되지 않습니다. 사용자는 데이터와 실험에 대한 지식을 바탕으로 적절한 알파 값을 선택해야 합니다.

      RidgeCV(Ridge 교차 검증)의 작동 원리:

      • RidgeCV는 교차 검증을 통해 알파 파라미터에 대한 최적의 값을 자동으로 선택하는 Ridge 메서드을 확장한 것입니다. 알파를 수동으로 설정하는 대신 RidgeCV는 다양한 알파 값을 반복하여 교차 검증에서 가장 우수한 성능을 제공하는 값을 선택합니다.
      • 자동 튜닝의 장점: RidgeCV의 가장 큰 장점은 수동으로 조정할 필요 없이 최적의 알파 값을 자동으로 결정한다는 점입니다. 이렇게 하면 튜닝 프로세스가 더 편리해지고 알파 선택 시 발생할 수 있는 오류를 방지할 수 있습니다.

      Ridge와 RidgeCV의 주요 차이점은 Ridge는 사용자가 알파 파라미터 값을 명시적으로 지정해야 하는 반면 RidgeCV는 교차 검증을 통해 최적의 알파 값을 자동으로 찾아낸다는 점입니다. 일반적으로 많은 양의 데이터를 처리하고 수동 매개변수 조정을 피하고자 할 때는 RidgeCV가 더 선호됩니다.


      2.1.14. sklearn.linear_model.Ridge

      Ridge는 머신러닝에서 회귀 문제를 해결하기 위해 사용되는 회귀 메서드입니다. 선형 모델 제품군의 일부이며 정규화된 선형 회귀를 나타냅니다.

      릿지 회귀의 주요 피처은 표준 최소자승법(OLS) 방식에 L2 정규화를 추가하는 것입니다.

      릿지 회귀의 작동 방식:

      1. 선형 회귀: 일반 선형 회귀와 마찬가지로 릿지 회귀는 독립 변수(피처)와 목표 변수 사이의 선형 관계를 찾는 것을 목표로 합니다.
      2. L2 정규화: 릿지 회귀의 주요 차이점은 손실 함수에 L2 정규화를 추가하는 것입니다. 즉 회귀 계수의 값이 클 경우 실제 값과 예측 값의 제곱 차이의 합에 페널티가 추가됩니다.
      3. 페널티 계수: L2 정규화는 회귀 계수 값에 페널티를 부과합니다. 결과적으로 일부 계수는 0에 가까워지는 경향이 있어 과적합이 줄어들고 모델 안정성이 향상됩니다.
      4. 하이퍼파라미터 α: Ridge 회귀의 필수 파라미터 중 하나는 정규화 정도를 결정하는 하이퍼파라미터 α(알파)입니다. α 값이 높을수록 정규화가 강해져 계수 값이 낮은 더 단순한 모델이 생성됩니다.

      Ridge 회귀의 장점:

      • 과적합 감소: Ridge의 L2 정규화는 과적합을 줄여 데이터의 노이즈에 대해 모델을 더욱 견고하게 만듭니다.
      • 다중 선형성 처리하기: 릿지 회귀는 특히 피처의 상관관계가 높은 경우 다중 선형성 문제에 잘 대처합니다.
      • 차원의 저주 해결: Ridge는 OLS가 불안정할 수 있는 많은 피처에 있는 시나리오에 도움이 됩니다.

      Ridge 회귀의 한계:

      • 피처를 제거하지 않습니다: Ridge 회귀는 피처 계수를 0으로 만들지 않고 감소시킬 뿐이므로 일부 피처는 여전히 모델에 남아있을 수 있습니다.
      • 최적의 α 선택: 하이퍼파라미터 α의 올바른 값을 선택하려면 교차 검증이 필요할 수 있습니다.

      Ridge 회귀는 표준 선형 회귀에 L2 정규화를 도입하여 과적합을 줄이고 안정성을 높이며 다중공선성 문제를 해결하는 회귀 메서드입니다. 이 메서드는 정확도와 모델 안정성의 균형을 맞춰야 할 때 유용합니다.


      2.1.14.1. Ridge 모델을 생성하고 float 및 double을 위해 ONNX로 내보내기 위한 코드

      이 코드는 sklearn.linear_model.Ridge 모델을 생성하고 합성 데이터로 학습시키고 모델을 ONNX 형식으로 저장하고 플로트 및 더블 입력 데이터를 모두 사용하여 예측을 수행합니다. 또한 원본 모델과 ONNX로 내보낸 모델 모두의 정확도를 평가합니다.

      # Ridge.py
      # 이 코드는 Ridge 모델을 학습하고 이를 ONNX 형식(float 및 double)으로 내보내고 ONNX 모델을 사용하여 예측하는 과정을 보여줍니다.
      # Copyright 2023, MetaQuotes Ltd.
      # https://www.mql5.com


      # 소수점 이하 자릿수를 비교학 위한 함수
      def compare_decimal_places(value1, value2):
      # 두 값을 모두 문자열로 변환
          str_value1 = str(value1)
          str_value2 = str(value2)

      # 문자열에서 소수점 위치 찾기
          dot_position1 = str_value1.find(".")
          dot_position2 = str_value2.find(".")

      # 값 중 소수점이 없는 경우 0을 반환합니다.
          if dot_position1 == -1 or dot_position2 == -1:
              return 0

          # 소수점 이하 자릿수 계산
          decimal_places1 = len(str_value1) - dot_position1 - 1
          decimal_places2 = len(str_value2) - dot_position2 - 1

          # 소수점 이하 두 자리 수 중 최소값 찾기
          min_decimal_places = min(decimal_places1, decimal_places2)

          # 소수점 이하 자릿수가 일치하도록 카운트를 초기화
          matching_count = 0

          # 소수점 이하 문자 비교
          for i in range(1, min_decimal_places + 1):
              if str_value1[dot_position1 + i] == str_value2[dot_position2 + i]:
                  matching_count += 1
              else:
                  break

          return matching_count

      # 필요한 라이브러리 가져오기
      import numpy as np
      import matplotlib.pyplot as plt
      에서 sklearn.linear_model import Ridge
      from sklearn.metrics import r2_score,mean_absolute_error,mean_squared_error
      import onnx
      import onnxruntime as ort
      from skl2onnx import convert_sklearn
      from skl2onnx.common.data_types import FloatTensorType
      from skl2onnx.common.data_types import DoubleTensorType
      from sys import argv

      # 모델 저장 경로를 정의합니다.
      data_path = argv[0]
      last_index = data_path.rfind("\\")+ 1
      data_path = data_path[0:last_index]

      # 회귀를 위한 합성 데이터 생성
      X = np.arange(0,100,1).reshape(-1,1)
      y = 4*X + 10*np.sin(X*0.5)

      model_name = "Ridge"
      onnx_model_filename = data_path + "ridge"

      # Ridge 모델 만들기
      regression_model = Ridge()

      # 데이터에 모델 맞추기
      regression_model.fit(X, y.ravel())

      # 전체 데이터 집합에 대한 값 예측
      y_pred = regression_model.predict(X)

      # 모델의 성능 평가
      r2 = r2_score(y, y_pred)
      MSE = MEAN_SQUARE_ERROR(Y, Y_PED)
      MAE = MEAN_ABSOLUTE_ERROR(Y, Y_PED)

      print("\n"+model_name+" Original model (double)")
      print("R-squared (Coefficient of determination):", r2)
      print("Mean Absolute Error:", mae)
      print("Mean Squared Error:", mse)

      # ONNX 모델로 변환 (float)
      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.

      initial_type_float = [('float_input', FloatTensorType([None, X.shape[1]])]]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_float = convert_sklearn(regression_model, initial_types=initial_type_float, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_float.onnx"
      onnx.save_model(onnx_model_float, onnx_filename)

      print("\n"+model_name+" ONNX model (float)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.
      initial_type_float = X.astype(np.float32)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_float = onnx_session.run([output_name], {input_name: initial_type_float})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_float = r2_score(y, y_pred_onnx_float)
      mse_onnx_float = mean_squared_error(y, y_pred_onnx_float)
      mae_onnx_float = mean_absolute_error(y, y_pred_onnx_float)
      print("R-squared (Coefficient of determination)", r2_onnx_float)
      print("Mean Absolute Error:", mae_onnx_float)
      print("Mean Squared Error:", mse_onnx_float)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_float))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_float))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_float))
      print("float ONNX model precision: ",compare_decimal_places(mae, mae_onnx_float))

      # 그림 크기 설정
      plt.figure(figsize=(8,5))
      # 원본 데이터와 회귀선을 그림으로 표시합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with float ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_float.png')

      # ONNX 모델로 변환 (double)
      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.

      initial_type_double = [('double_input', DoubleTensorType([None, X.shape[1]]))]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_double = convert_sklearn(regression_model, initial_types=initial_type_double, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_double.onnx"
      onnx.save_model(onnx_model_double, onnx_filename)

      print("\n"+model_name+" ONNX model (double)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.
      initial_type_double = X.astype(np.float64)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_double = onnx_session.run([output_name], {input_name: initial_type_double})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_double = r2_score(y, y_pred_onnx_double)
      mse_onnx_double = mean_squared_error(y, y_pred_onnx_double)
      mae_onnx_double = mean_absolute_error(y, y_pred_onnx_double)
      print("R-squared (Coefficient of determination)", r2_onnx_double)
      print("Mean Absolute Error:", mae_onnx_double)
      print("Mean Squared Error:", mse_onnx_double)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_double))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_double))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_double))
      print("double ONNX model precision: ",compare_decimal_places(mae, mae_onnx_double))

      # 그림 크기 설정
      plt.figure(figsize=(8,5))
      # 원본 데이터와 회귀선을 그림으로 표시합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with double ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_double.png')

      출력:

      Python  Ridge Original model (double)
      Python  R-squared (Coefficient of determination): 0.9962382641178552
      Python  Mean Absolute Error: 6.347684462929819
      Python  Mean Squared Error: 49.77814206996523
      Python  
      Python  Ridge ONNX model (float)
      Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\ridge_float.onnx
      Python  Information about input tensors in ONNX:
      Python  1. Name: float_input, Data Type: tensor(float), Shape: [None, 1]
      Python  Information about output tensors in ONNX:
      Python  1. Name: variable, Data Type: tensor(float), Shape: [None, 1]
      Python  R-squared (Coefficient of determination) 0.9962382634837793
      Python  Mean Absolute Error: 6.347684915729416
      Python  Mean Squared Error: 49.77815046053819
      Python  R^2 matching decimal places:  8
      Python  MAE matching decimal places:  6
      Python  MSE matching decimal places:  4
      Python  float ONNX model precision:  6
      Python  
      Python  Ridge ONNX model (double)
      Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\ridge_double.onnx
      Python  Information about input tensors in ONNX:
      Python  1. Name: double_input, Data Type: tensor(double), Shape: [None, 1]
      Python  Information about output tensors in ONNX:
      Python  1. Name: variable, Data Type: tensor(double), Shape: [None, 1]
      Python  R-squared (Coefficient of determination) 0.9962382641178552
      Python  Mean Absolute Error: 6.347684462929819
      Python  Mean Squared Error: 49.77814206996523
      Python  R^2 matching decimal places:  16
      Python  MAE matching decimal places:  15
      Python  MSE matching decimal places:  14
      Python  double ONNX model precision:  15
      

      Fig.49. Ridge.py의 결과(float ONNX)

      그림 49. Ridge.py 결과(float ONNX)



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

      이 코드는 저장된 ridge_float.onnxridge_double.onnx 모델을 실행하고 MQL5에서 회귀 메트릭을 사용하는 것을 보여줍니다.

      //+------------------------------------------------------------------+
      //|                                                        Ridge.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"
      
      #define   ModelName          "Ridge"
      #define   ONNXFilenameFloat  "ridge_float.onnx"
      #define   ONNXFilenameDouble "ridge_double.onnx"
      
      #resource ONNXFilenameFloat  as const uchar ExtModelFloat[];
      #resource ONNXFilenameDouble as const uchar ExtModelDouble[];
      
      #define   TestFloatModel  1
      #define   TestDoubleModel 2
      
      //+------------------------------------------------------------------+
      //| Calculate regression using float values                          |
      //+------------------------------------------------------------------+
      bool RunModelFloat(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         float input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=(float)input_vector[k];
      //--- prepare output tensor
         float output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      //+------------------------------------------------------------------+
      //| Calculate regression using double values                         |
      //+------------------------------------------------------------------+
      bool RunModelDouble(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         double input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=input_vector[k];
      //--- prepare output tensor
         double output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      
      //+------------------------------------------------------------------+
      //| Generate synthetic data                                          |
      //+------------------------------------------------------------------+
      bool GenerateData(const int n,vector &x,vector &y)
        {
         if(n<=0)
            return(false);
      //--- prepare arrays
         x.Resize(n);
         y.Resize(n);
      //---
         for(int i=0; i<n; i++)
           {
            x[i]=(double)1.0*i;
            y[i]=(double)(4*x[i] + 10*sin(x[i]*0.5));
           }
      //---
         return(true);
        }
      
      //+------------------------------------------------------------------+
      //| TestRegressionModel                                              |
      //+------------------------------------------------------------------+
      bool TestRegressionModel(const string model_name,const int model_type)
        {
      //---
         long  model=INVALID_HANDLE;
         ulong flags=ONNX_DEFAULT;
      
         if(model_type==TestFloatModel)
           {
            PrintFormat("\nTesting ONNX float: %s (%s)",model_name,ONNXFilenameFloat);
            model=OnnxCreateFromBuffer(ExtModelFloat,flags);
           }
         else
            if(model_type==TestDoubleModel)
              {
               PrintFormat("\nTesting ONNX double: %s (%s)",model_name,ONNXFilenameDouble);
               model=OnnxCreateFromBuffer(ExtModelDouble,flags);
              }
            else
              {
               PrintFormat("Model type is not incorrect.");
               return(false);
              }
      //--- check
         if(model==INVALID_HANDLE)
           {
            PrintFormat("model_name=%s OnnxCreate error %d",model_name,GetLastError());
            return(false);
           }
      //---
         vector x_values= {};
         vector y_true= {};
         vector y_predicted= {};
      //---
         int n=100;
         GenerateData(n,x_values,y_true);
      //---
         bool run_result=false;
         if(model_type==TestFloatModel)
           {
            run_result=RunModelFloat(model,x_values,y_predicted);
           }
         else
            if(model_type==TestDoubleModel)
              {
               run_result=RunModelDouble(model,x_values,y_predicted);
              }
      //---
         if(run_result)
           {
            PrintFormat("MQL5:   R-Squared (Coefficient of determination): %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_R2));
            PrintFormat("MQL5:   Mean Absolute Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MAE));
            PrintFormat("MQL5:   Mean Squared Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MSE));
           }
         else
            PrintFormat("Error %d",GetLastError());
      //--- release model
         OnnxRelease(model);
      //---
         return(true);
        }
      //+------------------------------------------------------------------+
      //| Script program start function                                    |
      //+------------------------------------------------------------------+
      int OnStart(void)
        {
      //--- test ONNX regression model for float
         TestRegressionModel(ModelName,TestFloatModel);
      //--- test ONNX regression model for double
         TestRegressionModel(ModelName,TestDoubleModel);
      //---
         return(0);
        }
      //+------------------------------------------------------------------+

      출력:

      Ridge (EURUSD,H1)       Testing ONNX float: Ridge (ridge_float.onnx)
      Ridge (EURUSD,H1)       MQL5:   R-Squared (Coefficient of determination): 0.9962382634837793
      Ridge (EURUSD,H1)       MQL5:   Mean Absolute Error: 6.3476849157294160
      Ridge (EURUSD,H1)       MQL5:   Mean Squared Error: 49.7781504605381784
      Ridge (EURUSD,H1)       
      Ridge (EURUSD,H1)       Testing ONNX double: Ridge (ridge_double.onnx)
      Ridge (EURUSD,H1)       MQL5:   R-Squared (Coefficient of determination): 0.9962382641178552
      Ridge (EURUSD,H1)       MQL5:   Mean Absolute Error: 6.3476844629298235
      Ridge (EURUSD,H1)       MQL5:   Mean Squared Error: 49.7781420699652131
      

      파이썬의 원래 배정밀도 모델과 비교:

      Testing ONNX float: Ridge (ridge_float.onnx)
      Python  Mean Absolute Error: 6.347684462929819
      MQL5:   Mean Absolute Error: 6.3476849157294160
             
      Testing ONNX double: Ridge (ridge_double.onnx)
      Python  Mean Absolute Error: 6.347684462929819
      MQL5:   Mean Absolute Error: 6.3476844629298235

      ONNX float MAE의 정확도: 소수점 이하 6자리, 정확도 ONNX double MAE: 소수점 이하 13자리.


      2.1.14.3. ridge_float.onnx와 ridge_double.onnx의 ONNX 표현

      Fig.50. Netron에서 ridge_float.onnx의 ONNX 표현

      그림 50. 네트론에서 ridge_float.onnx의 ONNX 표현



      Fig.51. Netron에서 ridge_double.onnx의 ONNX 표현

      그림 51. 넷트론에서 ridge_double.onnx의 ONNX 표현



      2.1.15. sklearn.linear_model.RidgeCV

      - Ridge 회귀의 정규화 정도를 결정하는 최상의 하이퍼파라미터 α(알파)의 자동 선택을 포함하는 Ridge 회귀의 확장 기능입니다. 하이퍼파라미터 α는 일반 선형 회귀에서와 같이 제곱 오차의 합을 최소화하는 것과 회귀 계수의 값을 최소화하는 것(정규화) 사이의 균형을 제어합니다. RidgeCV는 지정된 매개변수와 기준에 따라 α의 최적 값을 자동으로 선택합니다.

      RidgeCV의 작동 방식:

      1. 데이터를 입력합니다: RidgeCV는 피처(독립 변수)과 목표 변수(연속형)로 구성된 입력 데이터를 받습니다.
      2. α를 선택합니다: Ridge 회귀를 사용하려면 정규화 정도를 결정하는 하이퍼파라미터 α를 선택해야 합니다. RidgeCV는 주어진 범위에서 α의 최적 값을 자동으로 선택합니다.
      3. 교차 검증: RidgeCV는 k-배 교차 검증과 같은 교차 검증을 사용하여 독립 데이터에 대해 어떤 α 값이 최상의 모델 일반화를 제공하는지 평가합니다.
      4. 최적의 α: 훈련 프로세스가 완료되면 RidgeCV는 교차 검증에서 최고의 성능을 제공하는 α 값을 선택하고 이 값을 사용하여 최종 Ridge 회귀 모델을 훈련합니다.

      RidgeCV의 장점:

      • α 자동 선택: RidgeCV는 하이퍼파라미터 α의 최적값을 자동으로 선택할 수 있어 모델 튜닝 프로세스를 간소화합니다.
      • 정규화와 성능 간의 균형: 이 메서드는 정규화(과적합 감소)와 모델 성능 간의 최적의 균형을 찾는 데 도움이 됩니다.

      RidgeCV의 한계:

      • 계산의 복잡성: 교차 검증에는 특히 큰 범위의 α 값을 사용하는 경우 상당한 계산 리소스가 필요할 수 있습니다.

      RidgeCV는 교차 검증을 사용하여 최적의 하이퍼파라미터 α를 자동으로 선택하는 Ridge 회귀 메서드입니다. 이 메서드를 사용하면 하이퍼파라미터 선택 프로세스를 간소화하고 정규화와 모델 성능 간에 최적의 균형을 찾을 수 있습니다.


      2.1.15.1. RidgeCV 모델을 생성하고 float 및 double을 위해 ONNX로 내보내기 위한 코드

      이 코드는 sklearn.linear_model.RidgeCV 모델을 생성하고 합성 데이터로 학습시키고 모델을 ONNX 형식으로 저장하고 플로트 및 더블 입력 데이터를 모두 사용하여 예측을 수행합니다. 또한 원본 모델과 ONNX로 내보낸 모델 모두의 정확도를 평가합니다.

      # RidgeCV.py
      # 이 코드는 RidgeCV 모델을 학습하고 이를 ONNX 형식(float 및 double 모두)으로 내보내고 ONNX 모델을 사용하여 예측하는 과정을 보여줍니다.
      # Copyright 2023, MetaQuotes Ltd.
      # https://www.mql5.com


      # 소수점 이하 자릿수를 비교학 위한 함수
      def compare_decimal_places(value1, value2):
      # 두 값을 모두 문자열로 변환
          str_value1 = str(value1)
          str_value2 = str(value2)

      # 문자열에서 소수점 위치 찾기
          dot_position1 = str_value1.find(".")
          dot_position2 = str_value2.find(".")

      # 값 중 소수점이 없는 경우 0을 반환합니다.
          dot_position1 == -1 또는 dot_position2 == -1:
              return 0

          # 소수점 이하 자릿수 계산
          decimal_places1 = len(str_value1) - dot_position1 - 1
          decimal_places2 = len(str_value2) - dot_position2 - 1

          # 소수점 이하 두 자리 수 중 최소값 찾기
          min_decimal_places = min(decimal_places1, decimal_places2)

          # 소수점 이하 자릿수가 일치하도록 카운트를 초기화
          matching_count = 0

          # 소수점 이하 문자 비교
          for i in range(1, min_decimal_places + 1):
              if str_value1[dot_position1 + i] == str_value2[dot_position2 + i]:
                  matching_count += 1
              else:
                  break

          return matching_count

      # 필요한 라이브러리 가져오기
      import numpy as np
      import matplotlib.pyplot as plt
      에서 sklearn.linear_model import RidgeCV
      from sklearn.metrics import r2_score,mean_absolute_error,mean_squared_error
      import onnx
      import onnxruntime as ort
      from skl2onnx import convert_sklearn
      from skl2onnx.common.data_types import FloatTensorType
      from skl2onnx.common.data_types import DoubleTensorType
      from sys import argv

      # 모델 저장 경로를 정의합니다.
      data_path = argv[0]
      last_index = data_path.rfind("\\")+ 1
      data_path = data_path[0:last_index]

      # 회귀를 위한 합성 데이터 생성
      X = np.arange(0,100,1).reshape(-1,1)
      y = 4*X + 10*np.sin(X*0.5)

      model_name = "RidgeCV"
      onnx_model_filename = data_path + "ridge_cv"

      # RidgeCV 모델 생성
      regression_model = RidgeCV()

      # 데이터에 모델 맞추기
      regression_model.fit(X, y.ravel())

      # 전체 데이터 집합에 대한 값 예측
      y_pred = regression_model.predict(X)

      # 모델의 성능 평가
      r2 = r2_score(y, y_pred)
      MSE = MEAN_SQUARE_ERROR(Y, Y_PED)
      MAE = MEAN_ABSOLUTE_ERROR(Y, Y_PED)

      print("\n"+model_name+" Original model (double)")
      print("R-squared (Coefficient of determination):", r2)
      print("Mean Absolute Error:", mae)
      print("Mean Squared Error:", mse)

      # ONNX 모델로 변환 (float)
      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.

      initial_type_float = [('float_input', FloatTensorType([None, X.shape[1]]))]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_float = convert_sklearn(regression_model, initial_types=initial_type_float, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_float.onnx"
      onnx.save_model(onnx_model_float, onnx_filename)

      print("\n"+model_name+" ONNX model (float)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.
      initial_type_float = X.astype(np.float32)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_float = onnx_session.run([output_name], {input_name: initial_type_float})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_float = r2_score(y, y_pred_onnx_float)
      mse_onnx_float = mean_squared_error(y, y_pred_onnx_float)
      mae_onnx_float = mean_absolute_error(y, y_pred_onnx_float)
      print("R-squared (Coefficient of determination)", r2_onnx_float)
      print("Mean Absolute Error:", mae_onnx_float)
      print("Mean Squared Error:", mse_onnx_float)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_float))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_float))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_float))
      print("float ONNX model precision: ",compare_decimal_places(mae, mae_onnx_float))

      # 그림 크기 설정
      plt.figure(figsize=(8,5))
      # 원본 데이터와 회귀선을 그림으로 표시합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with float ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_float.png')

      # ONNX 모델로 변환 (double)
      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.

      initial_type_double = [('double_input', DoubleTensorType([None, X.shape[1]]))]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_double = convert_sklearn(regression_model, initial_types=initial_type_double, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_double.onnx"
      onnx.save_model(onnx_model_double, onnx_filename)

      print("\n"+model_name+" ONNX model (double)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.
      initial_type_double = X.astype(np.float64)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_double = onnx_session.run([output_name], {input_name: initial_type_double})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_double = r2_score(y, y_pred_onnx_double)
      mse_onnx_double = mean_squared_error(y, y_pred_onnx_double)
      mae_onnx_double = mean_absolute_error(y, y_pred_onnx_double)
      print("R-squared (Coefficient of determination)", r2_onnx_double)
      print("Mean Absolute Error:", mae_onnx_double)
      print("Mean Squared Error:", mse_onnx_double)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_double))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_double))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_double))
      print("double ONNX model precision: ",compare_decimal_places(mae, mae_onnx_double))

      # 그림 크기 설정
      plt.figure(figsize=(8,5))
      # 원본 데이터와 회귀선을 그림으로 표시합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with double ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_double.png')

      출력:

      Python  RidgeCV Original model (double)
      Python  R-squared (Coefficient of determination): 0.9962382499160807
      Python  Mean Absolute Error: 6.34720334999352
      Python  Mean Squared Error: 49.77832999861571
      Python  
      Python  RidgeCV ONNX model (float)
      Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\ridge_cv_float.onnx
      Python  Information about input tensors in ONNX:
      Python  1. Name: float_input, Data Type: tensor(float), Shape: [None, 1]
      Python  Information about output tensors in ONNX:
      Python  1. Name: variable, Data Type: tensor(float), Shape: [None, 1]
      Python  R-squared (Coefficient of determination) 0.9962382499108485
      Python  Mean Absolute Error: 6.3472036427935485
      Python  Mean Squared Error: 49.77833006785168
      Python  R^2 matching decimal places:  11
      Python  MAE matching decimal places:  6
      Python  MSE matching decimal places:  4
      Python  float ONNX model precision:  6
      Python  
      Python  RidgeCV ONNX model (double)
      Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\ridge_cv_double.onnx
      Python  Information about input tensors in ONNX:
      Python  1. Name: double_input, Data Type: tensor(double), Shape: [None, 1]
      Python  Information about output tensors in ONNX:
      Python  1. Name: variable, Data Type: tensor(double), Shape: [None, 1]
      Python  R-squared (Coefficient of determination) 0.9962382499160807
      Python  Mean Absolute Error: 6.34720334999352
      Python  Mean Squared Error: 49.77832999861571
      Python  R^2 matching decimal places:  16
      Python  MAE matching decimal places:  14
      Python  MSE matching decimal places:  14
      Python  double ONNX model precision:  14
      

      Fig.52. RidgeCV.py의 결과(float ONNX)

      그림 52. RidgeCV.py 결과(float ONNX)


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

      이 코드는 저장된 ridge_cv_float.onnxridge_cv_double.onnx 모델을 실행하고 MQL5에서 회귀 메트릭을 사용하는 것을 보여줍니다.

      //+------------------------------------------------------------------+
      //|                                                      RidgeCV.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"
      
      #define   ModelName          "RidgeCV"
      #define   ONNXFilenameFloat  "ridge_cv_float.onnx"
      #define   ONNXFilenameDouble "ridge_cv_double.onnx"
      
      #resource ONNXFilenameFloat  as const uchar ExtModelFloat[];
      #resource ONNXFilenameDouble as const uchar ExtModelDouble[];
      
      #define   TestFloatModel  1
      #define   TestDoubleModel 2
      
      //+------------------------------------------------------------------+
      //| Calculate regression using float values                          |
      //+------------------------------------------------------------------+
      bool RunModelFloat(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         float input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=(float)input_vector[k];
      //--- prepare output tensor
         float output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      //+------------------------------------------------------------------+
      //| Calculate regression using double values                         |
      //+------------------------------------------------------------------+
      bool RunModelDouble(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         double input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=input_vector[k];
      //--- prepare output tensor
         double output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      
      //+------------------------------------------------------------------+
      //| Generate synthetic data                                          |
      //+------------------------------------------------------------------+
      bool GenerateData(const int n,vector &x,vector &y)
        {
         if(n<=0)
            return(false);
      //--- prepare arrays
         x.Resize(n);
         y.Resize(n);
      //---
         for(int i=0; i<n; i++)
           {
            x[i]=(double)1.0*i;
            y[i]=(double)(4*x[i] + 10*sin(x[i]*0.5));
           }
      //---
         return(true);
        }
      
      //+------------------------------------------------------------------+
      //| TestRegressionModel                                              |
      //+------------------------------------------------------------------+
      bool TestRegressionModel(const string model_name,const int model_type)
        {
      //---
         long  model=INVALID_HANDLE;
         ulong flags=ONNX_DEFAULT;
      
         if(model_type==TestFloatModel)
           {
            PrintFormat("\nTesting ONNX float: %s (%s)",model_name,ONNXFilenameFloat);
            model=OnnxCreateFromBuffer(ExtModelFloat,flags);
           }
         else
            if(model_type==TestDoubleModel)
              {
               PrintFormat("\nTesting ONNX double: %s (%s)",model_name,ONNXFilenameDouble);
               model=OnnxCreateFromBuffer(ExtModelDouble,flags);
              }
            else
              {
               PrintFormat("Model type is not incorrect.");
               return(false);
              }
      //--- check
         if(model==INVALID_HANDLE)
           {
            PrintFormat("model_name=%s OnnxCreate error %d",model_name,GetLastError());
            return(false);
           }
      //---
         vector x_values= {};
         vector y_true= {};
         vector y_predicted= {};
      //---
         int n=100;
         GenerateData(n,x_values,y_true);
      //---
         bool run_result=false;
         if(model_type==TestFloatModel)
           {
            run_result=RunModelFloat(model,x_values,y_predicted);
           }
         else
            if(model_type==TestDoubleModel)
              {
               run_result=RunModelDouble(model,x_values,y_predicted);
              }
      //---
         if(run_result)
           {
            PrintFormat("MQL5:   R-Squared (Coefficient of determination): %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_R2));
            PrintFormat("MQL5:   Mean Absolute Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MAE));
            PrintFormat("MQL5:   Mean Squared Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MSE));
           }
         else
            PrintFormat("Error %d",GetLastError());
      //--- release model
         OnnxRelease(model);
      //---
         return(true);
        }
      //+------------------------------------------------------------------+
      //| Script program start function                                    |
      //+------------------------------------------------------------------+
      int OnStart(void)
        {
      //--- test ONNX regression model for float
         TestRegressionModel(ModelName,TestFloatModel);
      //--- test ONNX regression model for double
         TestRegressionModel(ModelName,TestDoubleModel);
      //---
         return(0);
        }
      //+------------------------------------------------------------------+

      출력:

      RidgeCV (EURUSD,H1)     Testing ONNX float: RidgeCV (ridge_cv_float.onnx)
      RidgeCV (EURUSD,H1)     MQL5:   R-Squared (Coefficient of determination): 0.9962382499108485
      RidgeCV (EURUSD,H1)     MQL5:   Mean Absolute Error: 6.3472036427935485
      RidgeCV (EURUSD,H1)     MQL5:   Mean Squared Error: 49.7783300678516909
      RidgeCV (EURUSD,H1)     
      RidgeCV (EURUSD,H1)     Testing ONNX double: RidgeCV (ridge_cv_double.onnx)
      RidgeCV (EURUSD,H1)     MQL5:   R-Squared (Coefficient of determination): 0.9962382499160807
      RidgeCV (EURUSD,H1)     MQL5:   Mean Absolute Error: 6.3472033499935216
      RidgeCV (EURUSD,H1)     MQL5:   Mean Squared Error: 49.7783299986157246
      

      파이썬의 원래 배정밀도 모델과 비교:

      Testing ONNX float: RidgeCV (ridge_cv_float.onnx)
      Python  Mean Absolute Error: 6.34720334999352
      MQL5:   Mean Absolute Error: 6.3472036427935485
      
      Testing ONNX double: RidgeCV (ridge_cv_double.onnx)
      Python  Mean Absolute Error: 6.34720334999352
      MQL5:   Mean Absolute Error: 6.3472033499935216
      

      ONNX float MAE의 정확도: 소수점 이하 6자리, 정확도 ONNX double MAE: 소수점 이하 14자리.


      2.1.15.3. ridge_cv_float.onnx 및 ridge_cv_double.onnx의 ONNX 표현


      Fig.53. Netron에서 ridge_cv_float.onnx의 ONNX 표현

      그림 53. 네트론에서 ridge_cv_float.onnx의 ONNX 표현



      Fig.54. Netron에서 ridge_cv_double.onnx의 ONNX 표현

      그림 54. 네트론에서 ridge_cv_double.onnx의 ONNX 표현


      2.1.16. sklearn.linear_model.OrthogonalMatchingPursuit

      OrthogonalMatchingPursuit (OMP)는 피처 선택 및 선형 회귀 문제를 해결하는 데 사용되는 알고리즘입니다.

      가장 중요한 피처을 선택하는 메서드 중 하나로 데이터 차원을 줄이고 모델의 일반화 능력을 향상시키는 데 도움이 될 수 있습니다.

      OrthogonalMatchingPursuit 작동 방식:

      1. 데이터를 입력합니다: 피처(독립 변수)와 대상 변수(연속형)의 값을 포함하는 데이터 집합으로 시작합니다.
      2. 피처 수 선택하기: OrthogonalMatchingPursuit를 사용할 때 초기 단계 중 하나는 모델에 포함할 피처의 수를 결정하는 것입니다. 이 숫자는 미리 정의하거나 AIC(Akaike 정보 기준) 또는 최소 오류 기준과 같은 기준을 사용하여 선택할 수 있습니다.
      3. 반복 피처 추가: 알고리즘은 빈 모델에서 시작하여 모델의 잔여값을 가장 잘 설명하는 피처를 반복적으로 추가합니다. 각 반복에서 이전에 선택한 피처에 직교하도록 새 피처가 선택됩니다. 최적의 피처는 모델 잔차와의 상관관계를 기반으로 선택됩니다.
      4. 모델 훈련: 지정된 수의 피처를 추가하면 선택한 피처만 고려하여 모델을 데이터에 대해 학습시킵니다.
      5. 예측하기: 학습 후 모델은 새로운 데이터에 대한 목표 변수의 값을 예측할 수 있습니다.

      OrthogonalMatchingPursuit의 장점:

      • 차원 축소: OMP는 가장 유익한 피처만 선택하여 데이터 차원을 줄일 수 있습니다.
      • 해석 가능성: OMP는 소수의 피처만 선택하기 때문에 이를 사용하여 만든 모델을 더 쉽게 해석할 수 있습니다.

      OrthogonalMatchingPursuit의 한계:

      • 선택한 피처의 수에 대한 민감도입니다: 선택한 피처의 개수를 적절히 조정해야 하며 잘못 선택하면 과적합 또는 과소적합이 발생할 수 있습니다.
      • 다중 선형성을 고려하지 않습니다: OMP는 피처 간의 다중 선형성을 고려하지 않을 수 있으므로 최적의 피처 선택에 영향을 미칠 수 있습니다.
      • 계산의 복잡성: OMP는 특히 대규모 데이터 세트의 경우 계산 비용이 많이 듭니다.

      OrthogonalMatchingPursuit는 피처 선택 및 선형 회귀를 위한 알고리즘으로 모델에 가장 유익한 피처를 선택할 수 있게 해줍니다. 이 메서드는 데이터 차원을 줄이고 모델 해석 가능성을 개선하는 데 유용할 수 있습니다.


      2.1.16.1. OrthogonalMatchingPursuit 모델을 생성하고 float 및 double용 ONNX로 내보내기 위한 코드입니다.

      이 코드는 OrthogonalMatchingPursuit 모델을 생성하고 합성 데이터로 학습시키고 모델을 ONNX 형식으로 저장하고 플로트 및 더블 입력 데이터를 모두 사용하여 예측을 수행합니다. 또한 원본 모델과 ONNX로 내보낸 모델 모두의 정확도를 평가합니다.

      # OrthogonalMatchingPursuit.py
      # 이 코드는 OrthogonalMatchingPursuit 모델을 훈련하고 이를 ONNX 형식(float 및 double 모두)으로 내보내고 ONNX 모델을 사용하여 예측하는 과정을 보여 줍니다.
      # Copyright 2023, MetaQuotes Ltd.
      # https://www.mql5.com


      # 소수점 이하 자릿수를 비교학 위한 함수
      def compare_decimal_places(value1, value2):
      # 두 값을 모두 문자열로 변환
          str_value1 = str(value1)
          str_value2 = str(value2)

      # 문자열에서 소수점 위치 찾기
          dot_position1 = str_value1.find(".")
          dot_position2 = str_value2.find(".")

      # 값 중 소수점이 없는 경우 0을 반환합니다.
          if dot_position1 == -1 or dot_position2 == -1:
              return 0

          # 소수점 이하 자릿수 계산
          decimal_places1 = len(str_value1) - dot_position1 - 1
          decimal_places2 = len(str_value2) - dot_position2 - 1

          # 소수점 이하 두 자리 수 중 최소값 찾기
          min_decimal_places = min(decimal_places1, decimal_places2)

          # 소수점 이하 자릿수가 일치하도록 카운트를 초기화
          matching_count = 0

          # 소수점 이하 문자 비교
          for i in range(1, min_decimal_places + 1):
              if str_value1[dot_position1 + i] == str_value2[dot_position2 + i]:
                  matching_count += 1
              else:
                  break

          return matching_count

      # 필요한 라이브러리 가져오기
      import numpy as np
      import matplotlib.pyplot as plt
      from sklearn.linear_model import OrthogonalMatchingPursuit
      from sklearn.metrics import r2_score,mean_absolute_error,mean_squared_error
      import onnx
      import onnxruntime as ort
      from skl2onnx import convert_sklearn
      from skl2onnx.common.data_types import FloatTensorType
      from skl2onnx.common.data_types import DoubleTensorType
      from sys import argv

      # 모델 저장 경로를 정의합니다.
      data_path = argv[0]
      last_index = data_path.rfind("\\")+ 1
      data_path = data_path[0:last_index]

      # 회귀를 위한 합성 데이터 생성
      X = np.arange(0,100,1).reshape(-1,1)
      y = 4*X + 10*np.sin(X*0.5)

      model_name = "OrthogonalMatchingPursuit"
      onnx_model_filename = data_path + "orthogonal_matching_pursuit"

      # OrthogonalMatchingPursuit 모델 만들기
      regression_model = OrthogonalMatchingPursuit()

      # 데이터에 모델 맞추기
      regression_model.fit(X, y)

      # 전체 데이터 집합에 대한 값 예측
      y_pred = regression_model.predict(X)

      # 모델의 성능 평가
      r2 = r2_score(y, y_pred)
      MSE = MEAN_SQUARE_ERROR(Y, Y_PED)
      MAE = MEAN_ABSOLUTE_ERROR(Y, Y_PED)

      print("\n"+model_name+" Original model (double)")
      print("R-squared (Coefficient of determination):", r2)
      print("Mean Absolute Error:", mae)
      print("Mean Squared Error:", mse)

      # ONNX 모델로 변환 (float)
      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.

      initial_type_float = [('float_input', FloatTensorType([None, X.shape[1]]))]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_float = convert_sklearn(regression_model, initial_types=initial_type_float, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_float.onnx"
      onnx.save_model(onnx_model_float, onnx_filename)

      print("\n"+model_name+" ONNX model (float)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.
      initial_type_float = X.astype(np.float32)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_float = onnx_session.run([output_name], {input_name: initial_type_float})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_float = r2_score(y, y_pred_onnx_float)
      mse_onnx_float = mean_squared_error(y, y_pred_onnx_float)
      mae_onnx_float = mean_absolute_error(y, y_pred_onnx_float)
      print("R-squared (Coefficient of determination)", r2_onnx_float)
      print("Mean Absolute Error:", mae_onnx_float)
      print("Mean Squared Error:", mse_onnx_float)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_float))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_float))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_float))
      print("float ONNX model precision: ",compare_decimal_places(mae, mae_onnx_float))

      # 그림 크기 설정
      plt.figure(figsize=(8,5))
      # 원본 데이터와 회귀 데이터를 플로팅합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with float ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_float.png')

      # ONNX 모델로 변환 (double)
      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.

      initial_type_double = [('double_input', DoubleTensorType([None, X.shape[1]]))]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_double = convert_sklearn(regression_model, initial_types=initial_type_double, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_double.onnx"
      onnx.save_model(onnx_model_double, onnx_filename)

      print("\n"+model_name+" ONNX model (double)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.
      initial_type_double = X.astype(np.float64)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_double = onnx_session.run([output_name], {input_name: initial_type_double})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_double = r2_score(y, y_pred_onnx_double)
      mse_onnx_double = mean_squared_error(y, y_pred_onnx_double)
      mae_onnx_double = mean_absolute_error(y, y_pred_onnx_double)
      print("R-squared (Coefficient of determination)", r2_onnx_double)
      print("Mean Absolute Error:", mae_onnx_double)
      print("Mean Squared Error:", mse_onnx_double)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_double))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_double))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_double))
      print("double ONNX model precision: ",compare_decimal_places(mae, mae_onnx_double))

      # 그림 크기 설정
      plt.figure(figsize=(8,5))
      # 원본 데이터와 회귀선을 그림으로 표시합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with double ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_double.png')

      출력:

      Python  OrthogonalMatchingPursuit Original model (double)
      Python  R-squared (Coefficient of determination): 0.9962382642613388
      Python  Mean Absolute Error: 6.3477379263364275
      Python  Mean Squared Error: 49.778140171281784
      Python  
      Python  OrthogonalMatchingPursuit ONNX model (float)
      Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\orthogonal_matching_pursuit_float.onnx
      Python  Information about input tensors in ONNX:
      Python  1. Name: float_input, Data Type: tensor(float), Shape: [None, 1]
      Python  Information about output tensors in ONNX:
      Python  1. Name: variable, Data Type: tensor(float), Shape: [None, 1]
      Python  R-squared (Coefficient of determination) 0.9962382641628886
      Python  Mean Absolute Error: 6.3477377671679385
      Python  Mean Squared Error: 49.77814147404787
      Python  R^2 matching decimal places:  9
      Python  MAE matching decimal places:  6
      Python  MSE matching decimal places:  5
      Python  float ONNX model precision:  6
      Python  
      Python  OrthogonalMatchingPursuit ONNX model (double)
      Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\orthogonal_matching_pursuit_double.onnx
      Python  Information about input tensors in ONNX:
      Python  1. Name: double_input, Data Type: tensor(double), Shape: [None, 1]
      Python  Information about output tensors in ONNX:
      Python  1. Name: variable, Data Type: tensor(double), Shape: [None, 1]
      Python  R-squared (Coefficient of determination) 0.9962382642613388
      Python  Mean Absolute Error: 6.3477379263364275
      Python  Mean Squared Error: 49.778140171281784
      Python  R^2 matching decimal places:  16
      Python  MAE matching decimal places:  16
      Python  MSE matching decimal places:  15
      Python  double ONNX model precision:  16
      

      Fig.55. OrthogonalMatchingPursuit.py의 결과 (float ONNX)

      그림 55. OrthogonalMatchingPursuit.py의 결과 (float ONNX)


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

      이 코드는 저장된 orthogonal_matching_pursuit_float.onnxorthogonal_matching_pursuit_double.onnx 모델을 실행하고 MQL5에서 회귀 메트릭을 사용하는 것을 보여 줍니다.

      //+------------------------------------------------------------------+
      //|                                    OrthogonalMatchingPursuit.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"
      
      #define   ModelName          "OrthogonalMatchingPursuit"
      #define   ONNXFilenameFloat  "orthogonal_matching_pursuit_float.onnx"
      #define   ONNXFilenameDouble "orthogonal_matching_pursuit_double.onnx"
      
      #resource ONNXFilenameFloat  as const uchar ExtModelFloat[];
      #resource ONNXFilenameDouble as const uchar ExtModelDouble[];
      
      #define   TestFloatModel  1
      #define   TestDoubleModel 2
      
      //+------------------------------------------------------------------+
      //| Calculate regression using float values                          |
      //+------------------------------------------------------------------+
      bool RunModelFloat(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         float input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=(float)input_vector[k];
      //--- prepare output tensor
         float output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      //+------------------------------------------------------------------+
      //| Calculate regression using double values                         |
      //+------------------------------------------------------------------+
      bool RunModelDouble(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         double input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=input_vector[k];
      //--- prepare output tensor
         double output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      
      //+------------------------------------------------------------------+
      //| Generate synthetic data                                          |
      //+------------------------------------------------------------------+
      bool GenerateData(const int n,vector &x,vector &y)
        {
         if(n<=0)
            return(false);
      //--- prepare arrays
         x.Resize(n);
         y.Resize(n);
      //---
         for(int i=0; i<n; i++)
           {
            x[i]=(double)1.0*i;
            y[i]=(double)(4*x[i] + 10*sin(x[i]*0.5));
           }
      //---
         return(true);
        }
      
      //+------------------------------------------------------------------+
      //| TestRegressionModel                                              |
      //+------------------------------------------------------------------+
      bool TestRegressionModel(const string model_name,const int model_type)
        {
      //---
         long  model=INVALID_HANDLE;
         ulong flags=ONNX_DEFAULT;
      
         if(model_type==TestFloatModel)
           {
            PrintFormat("\nTesting ONNX float: %s (%s)",model_name,ONNXFilenameFloat);
            model=OnnxCreateFromBuffer(ExtModelFloat,flags);
           }
         else
            if(model_type==TestDoubleModel)
              {
               PrintFormat("\nTesting ONNX double: %s (%s)",model_name,ONNXFilenameDouble);
               model=OnnxCreateFromBuffer(ExtModelDouble,flags);
              }
            else
              {
               PrintFormat("Model type is not incorrect.");
               return(false);
              }
      //--- check
         if(model==INVALID_HANDLE)
           {
            PrintFormat("model_name=%s OnnxCreate error %d",model_name,GetLastError());
            return(false);
           }
      //---
         vector x_values= {};
         vector y_true= {};
         vector y_predicted= {};
      //---
         int n=100;
         GenerateData(n,x_values,y_true);
      //---
         bool run_result=false;
         if(model_type==TestFloatModel)
           {
            run_result=RunModelFloat(model,x_values,y_predicted);
           }
         else
            if(model_type==TestDoubleModel)
              {
               run_result=RunModelDouble(model,x_values,y_predicted);
              }
      //---
         if(run_result)
           {
            PrintFormat("MQL5:   R-Squared (Coefficient of determination): %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_R2));
            PrintFormat("MQL5:   Mean Absolute Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MAE));
            PrintFormat("MQL5:   Mean Squared Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MSE));
           }
         else
            PrintFormat("Error %d",GetLastError());
      //--- release model
         OnnxRelease(model);
      //---
         return(true);
        }
      //+------------------------------------------------------------------+
      //| Script program start function                                    |
      //+------------------------------------------------------------------+
      int OnStart(void)
        {
      //--- test ONNX regression model for float
         TestRegressionModel(ModelName,TestFloatModel);
      //--- test ONNX regression model for double
         TestRegressionModel(ModelName,TestDoubleModel);
      //---
         return(0);
        }
      //+------------------------------------------------------------------+

      출력:

      OrthogonalMatchingPursuit (EURUSD,H1)   Testing ONNX float: OrthogonalMatchingPursuit (orthogonal_matching_pursuit_float.onnx)
      OrthogonalMatchingPursuit (EURUSD,H1)   MQL5:   R-Squared (Coefficient of determination): 0.9962382641628886
      OrthogonalMatchingPursuit (EURUSD,H1)   MQL5:   Mean Absolute Error: 6.3477377671679385
      OrthogonalMatchingPursuit (EURUSD,H1)   MQL5:   Mean Squared Error: 49.7781414740478638
      OrthogonalMatchingPursuit (EURUSD,H1)   
      OrthogonalMatchingPursuit (EURUSD,H1)   Testing ONNX double: OrthogonalMatchingPursuit (orthogonal_matching_pursuit_double.onnx)
      OrthogonalMatchingPursuit (EURUSD,H1)   MQL5:   R-Squared (Coefficient of determination): 0.9962382642613388
      OrthogonalMatchingPursuit (EURUSD,H1)   MQL5:   Mean Absolute Error: 6.3477379263364275
      OrthogonalMatchingPursuit (EURUSD,H1)   MQL5:   Mean Squared Error: 49.7781401712817768
      

      파이썬의 원래 배정밀도 모델과 비교:

      Testing ONNX float: OrthogonalMatchingPursuit (orthogonal_matching_pursuit_float.onnx)
      Python  Mean Absolute Error: 6.3477379263364275
      MQL5:   Mean Absolute Error: 6.3477377671679385
              
      Testing ONNX double: OrthogonalMatchingPursuit (orthogonal_matching_pursuit_double.onnx)
      Python  Mean Absolute Error: 6.3477379263364275
      MQL5:   Mean Absolute Error: 6.3477379263364275

      ONNX float MAE의 정확도: 소수점 이하 6자리, 정확도 ONNX double MAE: 소수점 이하 16자리.


      2.1.16.3. orthogonal_matching_pursuit_float.onnx 및 orthogonal_matching_pursuit_double.onnx의 ONNX 표현


      그림 56. Netron에서 orthogonal_matching_pursuit_float.onnx의 ONNX 표현

      그림 56. 네트론에서 orthogonal_matching_pursuit_float.onnx의 ONNX 표현



      그림 57. Netron에서 orthogonal_matching_pursuit_double.onnx의 ONNX 표현

      그림 57. 네트론에서 orthogonal_matching_pursuit_double.onnx의 ONNX 표현

      2.1.17. sklearn.linear_model.PassiveAggressiveRegressor

      PassiveAggressiveRegressor는 회귀 작업에 사용되는 머신 러닝 메서드입니다.

      이 메서드는 대상 변수의 연속적인 값을 예측할 수 있는 모델을 학습시키는 데 사용할 수 있는 Passive-Aggressive(PA) 알고리즘의 변형입니다.

      PassiveAggressiveRegressor의 작동 방식:

      1. 데이터를 입력합니다: 피처(독립 변수)와 대상 변수(연속형)의 값으로 구성된 데이터 집합으로 시작합니다.
      2. 지도 학습: PassiveAggressiveRegressor는 쌍(X, y)으로 훈련된 지도 학습 메서드로 여기서 X는 피처를 나타내고 Y는 목표 변수 값에 해당합니다.
      3. 적응형 학습: PassiveAggressiveRegressor 메서드의 기본 개념은 적응형 학습 접근 방식입니다. 모델은 각 학습 예제에서 예측 오류를 최소화하여 학습합니다. 예측 오차를 줄이기 위해 가중치를 수정하여 업데이트합니다.
      4. 파라미터 C: 모델에는 오류에 적응하는 정도를 제어하는 하이퍼파라미터 C가 PassiveAggressiveRegressor에 있습니다. C 값이 높을수록 가중치 업데이트가 더 공격적으로 이루어지며 C 값이 낮을수록 모델이 덜 공격적으로 업데이트됩니다.
      5. 예측: 학습이 완료된 모델은 새로운 데이터에 대한 목표 변수 값을 예측할 수 있습니다.

      PassiveAggressiveRegressor의 장점:

      • 적응력: 이 메서드는 데이터의 변화에 적응하고 모델을 업데이트하여 예측 오류를 최소화할 수 있습니다.
      • 대규모 데이터 세트의 효율성: 특히 상당한 양의 데이터로 학습할 경우 PassiveAggressiveRegressor는 회귀에 효과적인 메서드가 될 수 있습니다.

      PassiveAggressiveRegressor의 한계:

      • 매개변수 선택에 대한 민감도 C: C 값을 올바르게 선택하려면 튜닝과 실험이 필요할 수 있습니다.
      • 추가 피처가 필요할 수 있습니다: 경우에 따라 성공적인 모델 학습을 위해 추가로 맞춘 피처가 필요할 수 있습니다.

      PassiveAggressiveRegressor는 학습 데이터의 예측 오류를 최소화하여 적응적으로 학습하는 회귀 작업용 머신러닝 메서드입니다. 이 메서드는 대규모 데이터 세트를 처리하는 데 유용할 수 있으며 최적의 성능을 위해 C 매개변수를 조정해야 합니다.


      2.1.17.1. float 및 double용PassiveAgressiveRegressor 모델을 생성하고 이를 ONNX로 내보내기 위한 코드

      이 코드는 PassiveAggressiveRegressor 모델을 생성하고 합성 데이터로 학습시키고 모델을 ONNX 형식으로 저장하고 플로트 및 더블 입력 데이터를 모두 사용하여 예측을 수행합니다. 또한 원본 모델과 ONNX로 내보낸 모델 모두의 정확도를 평가합니다.

      # PassiveAggressiveRegressor.py
      # 이 코드는 수동 공격적 회귀 모델을 학습하고 이를 ONNX 형식(float 및 double 모두)으로 내보내고 ONNX 모델을 사용하여 예측하는 과정을 보여줍니다.
      # Copyright 2023, MetaQuotes Ltd.
      # https://www.mql5.com


      # 소수점 이하 자릿수를 비교학 위한 함수
      def compare_decimal_places(value1, value2):
      # 두 값을 모두 문자열로 변환
          str_value1 = str(value1)
          str_value2 = str(value2)

      # 문자열에서 소수점 위치 찾기
          dot_position1 = str_value1.find(".")
          dot_position2 = str_value2.find(".")

      # 값 중 소수점이 없는 경우 0을 반환합니다.
          if dot_position1 == -1 or dot_position2 == -1:
              return 0

          # 소수점 이하 자릿수 계산
          decimal_places1 = len(str_value1) - dot_position1 - 1
          decimal_places2 = len(str_value2) - dot_position2 - 1

          # 소수점 이하 두 자리 수 중 최소값 찾기
          min_decimal_places = min(decimal_places1, decimal_places2)

          # 소수점 이하 자릿수가 일치하도록 카운트를 초기화
          matching_count = 0

          # 소수점 이하 문자 비교
          for i in range(1, min_decimal_places + 1):
              if str_value1[dot_position1 + i] == str_value2[dot_position2 + i]:
                  matching_count += 1
              else:
                  break

          return matching_count

      # 필요한 라이브러리 가져오기
      import numpy as np
      import matplotlib.pyplot as plt
      from sklearn.linear_model import PassiveAggressiveRegressor
      from sklearn.metrics import r2_score,mean_absolute_error,mean_squared_error
      import onnx
      import onnxruntime as ort
      from skl2onnx import convert_sklearn
      from skl2onnx.common.data_types import FloatTensorType
      from skl2onnx.common.data_types import DoubleTensorType
      from sys import argv

      # 모델 저장 경로를 정의합니다.
      data_path = argv[0]
      last_index = data_path.rfind("\\")+ 1
      data_path = data_path[0:last_index]

      # 회귀를 위한 합성 데이터 생성
      X = np.arange(0,100,1).reshape(-1,1)
      y = 4*X + 10*np.sin(X*0.5)

      model_name = "PassiveAggressiveRegressor"
      onnx_model_filename = data_path + "passive_aggressive_regressor"

      # PassiveAggressiveRegressor 모델 만들기
      regression_model = PassiveAgressiveRegressor()

      # 데이터에 모델 맞추기
      regression_model.fit(X, y.ravel())

      # 전체 데이터 집합에 대한 값 예측
      y_pred = regression_model.predict(X)

      # 모델의 성능 평가
      r2 = r2_score(y, y_pred)
      MSE = MEAN_SQUARE_ERROR(Y, Y_PED)
      MAE = MEAN_ABSOLUTE_ERROR(Y, Y_PED)

      print("\n"+model_name+" Original model (double)")
      print("R-squared (Coefficient of determination):", r2)
      print("Mean Absolute Error:", mae)
      print("Mean Squared Error:", mse)

      # ONNX 모델로 변환 (float)
      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.

      initial_type_float = [('float_input', FloatTensorType([None, X.shape[1]]))]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_float = convert_sklearn(regression_model, initial_types=initial_type_float, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_float.onnx"
      onnx.save_model(onnx_model_float, onnx_filename)

      print("\n"+model_name+" ONNX model (float)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.
      initial_type_float = X.astype(np.float32)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_float = onnx_session.run([output_name], {input_name: initial_type_float})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_float = r2_score(y, y_pred_onnx_float)
      mse_onnx_float = mean_squared_error(y, y_pred_onnx_float)
      mae_onnx_float = mean_absolute_error(y, y_pred_onnx_float)
      print("R-squared (Coefficient of determination)", r2_onnx_float)
      print("Mean Absolute Error:", mae_onnx_float)
      print("Mean Squared Error:", mse_onnx_float)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_float))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_float))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_float))
      print("float ONNX model precision: ",compare_decimal_places(mae, mae_onnx_float))

      # 그림 크기 설정
      plt.figure(figsize=(8, 5))
      # 원본 데이터와 회귀 데이터를 플로팅합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with float ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_float.png')

      # ONNX 모델로 변환 (double)
      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.

      initial_type_double = [('double_input', DoubleTensorType([None, X.shape[1]]))]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_double = convert_sklearn(regression_model, initial_types=initial_type_double, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_double.onnx"
      onnx.save_model(onnx_model_double, onnx_filename)

      print("\n"+model_name+" ONNX model (double)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.
      initial_type_double = X.astype(np.float64)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_double = onnx_session.run([output_name], {input_name: initial_type_double})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_double = r2_score(y, y_pred_onnx_double)
      mse_onnx_double = mean_squared_error(y, y_pred_onnx_double)
      mae_onnx_double = mean_absolute_error(y, y_pred_onnx_double)
      print("R-squared (Coefficient of determination)", r2_onnx_double)
      print("Mean Absolute Error:", mae_onnx_double)
      print("Mean Squared Error:", mse_onnx_double)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_double))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_double))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_double))
      print("double ONNX model precision: ",compare_decimal_places(mae, mae_onnx_double))

      # 그림 크기 설정
      plt.figure(figsize=(8, 5))
      # 원본 데이터와 회귀선을 그림으로 표시합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with double ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_double.png')

      출력:

      Python  PassiveAggressiveRegressor Original model (double)
      Python  R-squared (Coefficient of determination): 0.9894376841493092
      Python  Mean Absolute Error: 9.64524669506544
      Python  Mean Squared Error: 139.76857373191007
      Python  
      Python  PassiveAggressiveRegressor ONNX model (float)
      Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\passive_aggressive_regressor_float.onnx
      Python  Information about input tensors in ONNX:
      Python  1. Name: float_input, Data Type: tensor(float), Shape: [None, 1]
      Python  Information about output tensors in ONNX:
      Python  1. Name: variable, Data Type: tensor(float), Shape: [None, 1]
      Python  R-squared (Coefficient of determination) 0.9894376801868329
      Python  Mean Absolute Error: 9.645248834431873
      Python  Mean Squared Error: 139.76862616640122
      Python  R^2 matching decimal places:  8
      Python  MAE matching decimal places:  5
      Python  MSE matching decimal places:  3
      Python  float ONNX model precision:  5
      Python  
      Python  PassiveAggressiveRegressor ONNX model (double)
      Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\passive_aggressive_regressor_double.onnx
      Python  Information about input tensors in ONNX:
      Python  1. Name: double_input, Data Type: tensor(double), Shape: [None, 1]
      Python  Information about output tensors in ONNX:
      Python  1. Name: variable, Data Type: tensor(double), Shape: [None, 1]
      Python  R-squared (Coefficient of determination) 0.9894376841493092
      Python  Mean Absolute Error: 9.64524669506544
      Python  Mean Squared Error: 139.76857373191007
      Python  R^2 matching decimal places:  16
      Python  MAE matching decimal places:  14
      Python  MSE matching decimal places:  14
      Python  double ONNX model precision:  14
      

      Fig.58. PassiveAggressiveRegressor.py 결과(더블 ONNX)

      그림 58. PassiveAggressiveRegressor.py의 결과(더블 ONNX)


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

      이 코드는 저장된 passive_aggressive_regressor_float.onnxpassive_aggressive_regressor_double. onnx 모델을 실행하고 MQL5에서 회귀 메트릭을 사용하는 것을 보여 줍니다.

      //+------------------------------------------------------------------+
      //|                                   PassiveAggressiveRegressor.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"
      
      #define   ModelName          "PassiveAggressiveRegressor"
      #define   ONNXFilenameFloat  "passive_aggressive_regressor_float.onnx"
      #define   ONNXFilenameDouble "passive_aggressive_regressor_double.onnx"
      
      #resource ONNXFilenameFloat  as const uchar ExtModelFloat[];
      #resource ONNXFilenameDouble as const uchar ExtModelDouble[];
      
      #define   TestFloatModel  1
      #define   TestDoubleModel 2
      
      //+------------------------------------------------------------------+
      //| Calculate regression using float values                          |
      //+------------------------------------------------------------------+
      bool RunModelFloat(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         float input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=(float)input_vector[k];
      //--- prepare output tensor
         float output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      //+------------------------------------------------------------------+
      //| Calculate regression using double values                         |
      //+------------------------------------------------------------------+
      bool RunModelDouble(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         double input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=input_vector[k];
      //--- prepare output tensor
         double output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      
      //+------------------------------------------------------------------+
      //| Generate synthetic data                                          |
      //+------------------------------------------------------------------+
      bool GenerateData(const int n,vector &x,vector &y)
        {
         if(n<=0)
            return(false);
      //--- prepare arrays
         x.Resize(n);
         y.Resize(n);
      //---
         for(int i=0; i<n; i++)
           {
            x[i]=(double)1.0*i;
            y[i]=(double)(4*x[i] + 10*sin(x[i]*0.5));
           }
      //---
         return(true);
        }
      
      //+------------------------------------------------------------------+
      //| TestRegressionModel                                              |
      //+------------------------------------------------------------------+
      bool TestRegressionModel(const string model_name,const int model_type)
        {
      //---
         long  model=INVALID_HANDLE;
         ulong flags=ONNX_DEFAULT;
      
         if(model_type==TestFloatModel)
           {
            PrintFormat("\nTesting ONNX float: %s (%s)",model_name,ONNXFilenameFloat);
            model=OnnxCreateFromBuffer(ExtModelFloat,flags);
           }
         else
            if(model_type==TestDoubleModel)
              {
               PrintFormat("\nTesting ONNX double: %s (%s)",model_name,ONNXFilenameDouble);
               model=OnnxCreateFromBuffer(ExtModelDouble,flags);
              }
            else
              {
               PrintFormat("Model type is not incorrect.");
               return(false);
              }
      //--- check
         if(model==INVALID_HANDLE)
           {
            PrintFormat("model_name=%s OnnxCreate error %d",model_name,GetLastError());
            return(false);
           }
      //---
         vector x_values= {};
         vector y_true= {};
         vector y_predicted= {};
      //---
         int n=100;
         GenerateData(n,x_values,y_true);
      //---
         bool run_result=false;
         if(model_type==TestFloatModel)
           {
            run_result=RunModelFloat(model,x_values,y_predicted);
           }
         else
            if(model_type==TestDoubleModel)
              {
               run_result=RunModelDouble(model,x_values,y_predicted);
              }
      //---
         if(run_result)
           {
            PrintFormat("MQL5:   R-Squared (Coefficient of determination): %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_R2));
            PrintFormat("MQL5:   Mean Absolute Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MAE));
            PrintFormat("MQL5:   Mean Squared Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MSE));
           }
         else
            PrintFormat("Error %d",GetLastError());
      //--- release model
         OnnxRelease(model);
      //---
         return(true);
        }
      //+------------------------------------------------------------------+
      //| Script program start function                                    |
      //+------------------------------------------------------------------+
      int OnStart(void)
        {
      //--- test ONNX regression model for float
         TestRegressionModel(ModelName,TestFloatModel);
      //--- test ONNX regression model for double
         TestRegressionModel(ModelName,TestDoubleModel);
      //---
         return(0);
        }
      //+------------------------------------------------------------------+

      출력:

      PassiveAggressiveRegressor (EURUSD,H1)  Testing ONNX float: PassiveAggressiveRegressor (passive_aggressive_regressor_float.onnx)
      PassiveAggressiveRegressor (EURUSD,H1)  MQL5:   R-Squared (Coefficient of determination): 0.9894376801868329
      PassiveAggressiveRegressor (EURUSD,H1)  MQL5:   Mean Absolute Error: 9.6452488344318716
      PassiveAggressiveRegressor (EURUSD,H1)  MQL5:   Mean Squared Error: 139.7686261664012761
      PassiveAggressiveRegressor (EURUSD,H1)  
      PassiveAggressiveRegressor (EURUSD,H1)  Testing ONNX double: PassiveAggressiveRegressor (passive_aggressive_regressor_double.onnx)
      PassiveAggressiveRegressor (EURUSD,H1)  MQL5:   R-Squared (Coefficient of determination): 0.9894376841493092
      PassiveAggressiveRegressor (EURUSD,H1)  MQL5:   Mean Absolute Error: 9.6452466950654419
      PassiveAggressiveRegressor (EURUSD,H1)  MQL5:   Mean Squared Error: 139.7685737319100667
      

      파이썬의 원래 배정밀도 모델과 비교:

      Testing ONNX float: PassiveAggressiveRegressor (passive_aggressive_regressor_float.onnx)
      Python  Mean Absolute Error: 9.64524669506544
      MQL5:   Mean Absolute Error: 9.6452488344318716
              
      Testing ONNX double: PassiveAggressiveRegressor (passive_aggressive_regressor_double.onnx)
      Python  Mean Absolute Error: 9.64524669506544
      MQL5:   Mean Absolute Error: 9.6452466950654419

      ONNX float MAE의 정확도: 소수점 이하 5자리, 정확도 ONNX 더블 MAE: 소수점 이하 14자리.


      2.1.17.3. passive_aggressive_regressor_float.onnx 및 passive_aggressive_regressor_double.onnx의 ONNX 표현


      그림 59. Netron에서 passive_aggressive_regressor_float.onnx의 ONNX 표현

      그림 59. passive_aggressive_regressor_float.onnx의 ONNX 표현



      Fig.60. Netron에서 passive_aggressive_regressor_double.onnx의 ONNX 표현

      그림 60. 네트론에서 passive_aggressive_regressor_double.onnx의 ONNX 표현



      2.1.18. sklearn.linear_model.QuantileRegressor

      QuantileRegressor은 회귀 작업에서 대상 변수의 사분위수(특정 백분위수)를 추정하는 데 사용되는 머신 러닝 메서드입니다.

      회귀 작업에서 일반적으로 수행되는 것처럼 대상 변수의 평균값을 예측하는 대신 QuantileRegressor는 중앙값(50번째 백분위수) 또는 25번째 및 75번째 백분위수와 같이 지정된 사분위수에 해당하는 값을 예측합니다.

      QuantileRegressor 작동 방식:

      1. 데이터를 입력합니다: 피처(독립 변수)와 대상 변수(연속형)가 포함된 데이터 집합으로 시작합니다.
      2. 사분위수 초점: 대상 변수의 정확한 값을 예측하는 대신 QuantileRegressor는 대상 변수의 조건부 분포를 모델링하고 이 분포의 특정 사분위수에 대한 값을 예측합니다.
      3. 다양한 분위수에 대한 훈련: 사분위수 회귀 모델을 훈련하려면 원하는 각 사분위수에 대해 별도의 모델을 훈련해야 합니다. 이러한 각 모델은 해당 사분위수에 해당하는 값을 예측합니다.
      4. 사분위수 매개변수입니다: 이 메서드의 주요 매개 변수는 예측을 얻고자 하는 사분위수를 선택하는 것입니다. 예를 들어 중앙값에 대한 예측이 필요한 경우 50번째 백분위수를 기준으로 모델을 학습시켜야 합니다.
      5. 사분위수 예측: 학습 후 모델을 사용하여 새 데이터에서 지정된 사분위수에 해당하는 값을 예측할 수 있습니다.

      QuantileRegressor의 장점:

      • 유연성: QuantileRegressor는 다양한 사분위수를 예측할 수 있는 유연성을 제공하므로 분포의 다양한 백분위수가 중요한 작업에 유용할 수 있습니다.
      • 이상값에 대한 견고성: 사분위수에 근거한 접근 방식은 극단값의 영향을 많이 받을 수 있는 평균을 고려하지 않기 때문에 이상값에 대해 강력할 수 있습니다.

      QuantileRegressor의 한계:

      • 사분위수의 선택이 필요합니다: 최적의 사분위수를 선택하려면 하고자 하는 작업이 무엇인지에 대한 지식이 필요할 수 있습니다.
      • 계산 복잡성 증가: 서로 다른 사분위수에 대해 별도의 모델을 학습하면 작업의 계산 복잡성이 증가할 수 있습니다.

      QuantileRegressor는 대상 변수의 지정된 사분위수에 해당하는 값을 예측하도록 설계된 머신 러닝 메서드입니다. 이 메서드는 분포의 다양한 백분위수가 중요한 작업이나 데이터에 이상값이 포함될 수 있는 경우에 유용할 수 있습니다.


      2.1.18.1. QuantileRegressor 모델을 생성하고 float 및 double용 ONNX로 내보내는 코드

      이 코드는 sklearn.linear_model.QuantileRegressor 모델을 생성하고 합성 데이터로 학습시키고 모델을 ONNX 형식으로 저장하고 플로트 및 더블 입력 데이터를 모두 사용하여 예측을 수행합니다. 또한 원본 모델과 ONNX로 내보낸 모델 모두의 정확도를 평가합니다.

      # QuantileRegressor.py
      # 이 코드는 QuantileRegressor 모델을 학습하고 이를 ONNX 형식(float 및 double 모두)으로 내보내고 ONNX 모델을 사용하여 예측하는 과정을 보여 줍니다.
      # Copyright 2023, MetaQuotes Ltd.
      # https://www.mql5.com


      # 소수점 이하 자릿수를 비교학 위한 함수
      def compare_decimal_places(value1, value2):
      # 두 값을 모두 문자열로 변환
          str_value1 = str(value1)
          str_value2 = str(value2)

      # 문자열에서 소수점 위치 찾기
          dot_position1 = str_value1.find(".")
          dot_position2 = str_value2.find(".")

      # 값 중 소수점이 없는 경우 0을 반환합니다.
          if dot_position1 == -1 or dot_position2 == -1:
              return 0

          # 소수점 이하 자릿수 계산
          decimal_places1 = len(str_value1) - dot_position1 - 1
          decimal_places2 = len(str_value2) - dot_position2 - 1

          # 소수점 이하 두 자리 수 중 최소값 찾기
          min_decimal_places = min(decimal_places1, decimal_places2)

          # 소수점 이하 자릿수가 일치하도록 카운트를 초기화
          matching_count = 0

          # 소수점 이하 문자 비교
          for i in range(1, min_decimal_places + 1):
              if str_value1[dot_position1 + i] == str_value2[dot_position2 + i]:
                  matching_count += 1
              else:
                  break

          return matching_count

      # 필요한 라이브러리 가져오기
      import numpy as np
      import matplotlib.pyplot as plt
      from sklearn.linear_model import QuantileRegressor
      from sklearn.metrics import r2_score,mean_absolute_error,mean_squared_error
      import onnx
      import onnxruntime as ort
      from skl2onnx import convert_sklearn
      from skl2onnx.common.data_types import FloatTensorType
      from skl2onnx.common.data_types import DoubleTensorType
      from sys import argv

      # 모델 저장 경로를 정의합니다.
      data_path = argv[0]
      last_index = data_path.rfind("\\")+ 1
      data_path = data_path[0:last_index]

      # 회귀를 위한 합성 데이터 생성
      X = np.arange(0,100,1).reshape(-1,1)
      y = 4*X + 10*np.sin(X*0.5)

      model_name = "QuantileRegressor"
      onnx_model_filename = data_path + "quantile_regressor"

      # QuantileRegressor 모델 만들기
      regression_model = QuantileRegressor(solver='highs')

      # 데이터에 모델 맞추기
      regression_model.fit(X, y.ravel())

      # 전체 데이터 집합에 대한 값 예측
      y_pred = regression_model.predict(X)

      # 모델의 성능 평가
      r2 = r2_score(y, y_pred)
      MSE = MEAN_SQUARE_ERROR(Y, Y_PED)
      MAE = MEAN_ABSOLUTE_ERROR(Y, Y_PED)

      print("\n"+model_name+" Original model (double)")
      print("R-squared (Coefficient of determination):", r2)
      print("Mean Absolute Error:", mae)
      print("Mean Squared Error:", mse)

      # ONNX 모델로 변환 (float)
      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.

      initial_type_float = [('float_input', FloatTensorType([None, X.shape[1]])]]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_float = convert_sklearn(regression_model, initial_types=initial_type_float, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_float.onnx"
      onnx.save_model(onnx_model_float, onnx_filename)

      print("\n"+model_name+" ONNX model (float)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.
      initial_type_float = X.astype(np.float32)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_float = onnx_session.run([output_name], {input_name: initial_type_float})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_float = r2_score(y, y_pred_onnx_float)
      mse_onnx_float = mean_squared_error(y, y_pred_onnx_float)
      mae_onnx_float = mean_absolute_error(y, y_pred_onnx_float)
      print("R-squared (Coefficient of determination)", r2_onnx_float)
      print("Mean Absolute Error:", mae_onnx_float)
      print("Mean Squared Error:", mse_onnx_float)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_float))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_float))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_float))
      print("float ONNX model precision: ",compare_decimal_places(mae, mae_onnx_float))

      # 그림 크기 설정
      plt.figure(figsize=(8,5))
      # 원본 데이터와 회귀 데이터를 플로팅합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with float ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_float.png')

      # ONNX 모델로 변환 (double)
      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.

      initial_type_double = [('double_input', DoubleTensorType([None, X.shape[1]]))]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_double = convert_sklearn(regression_model, initial_types=initial_type_double, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_double.onnx"
      onnx.save_model(onnx_model_double, onnx_filename)

      print("\n"+model_name+" ONNX model (double)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.
      initial_type_double = X.astype(np.float64)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_double = onnx_session.run([output_name], {input_name: initial_type_double})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_double = r2_score(y, y_pred_onnx_double)
      mse_onnx_double = mean_squared_error(y, y_pred_onnx_double)
      mae_onnx_double = mean_absolute_error(y, y_pred_onnx_double)
      print("R-squared (Coefficient of determination)", r2_onnx_double)
      print("Mean Absolute Error:", mae_onnx_double)
      print("Mean Squared Error:", mse_onnx_double)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_double))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_double))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_double))
      print("double ONNX model precision: ",compare_decimal_places(mae, mae_onnx_double))

      # 그림 크기 설정
      plt.figure(figsize=(8,5))
      # 원본 데이터와 회귀 데이터를 플로팅합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with double ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_double.png')

      출력:

      Python  QuantileRegressor Original model (double)
      Python  R-squared (Coefficient of determination): 0.9959915738839231
      Python  Mean Absolute Error: 6.3693091850025185
      Python  Mean Squared Error: 53.0425343337143
      Python  
      Python  QuantileRegressor ONNX model (float)
      Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\quantile_regressor_float.onnx
      Python  Information about input tensors in ONNX:
      Python  1. Name: float_input, Data Type: tensor(float), Shape: [None, 1]
      Python  Information about output tensors in ONNX:
      Python  1. Name: variable, Data Type: tensor(float), Shape: [None, 1]
      Python  R-squared (Coefficient of determination) 0.9959915739158818
      Python  Mean Absolute Error: 6.3693091422201125
      Python  Mean Squared Error: 53.042533910812814
      Python  R^2 matching decimal places:  9
      Python  MAE matching decimal places:  7
      Python  MSE matching decimal places:  5
      Python  float ONNX model precision:  7
      Python  
      Python  QuantileRegressor ONNX model (double)
      Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\quantile_regressor_double.onnx
      Python  Information about input tensors in ONNX:
      Python  1. Name: double_input, Data Type: tensor(double), Shape: [None, 1]
      Python  Information about output tensors in ONNX:
      Python  1. Name: variable, Data Type: tensor(double), Shape: [None, 1]
      Python  R-squared (Coefficient of determination) 0.9959915738839231
      Python  Mean Absolute Error: 6.3693091850025185
      Python  Mean Squared Error: 53.0425343337143
      Python  R^2 matching decimal places:  16
      Python  MAE matching decimal places:  16
      Python  MSE matching decimal places:  13
      Python  double ONNX model precision:  16
      

      Fig.61. QuantileRegressor.py의 결과(float ONNX)

      그림 61. QuantileRegressor.py의 결과(float ONNX)


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

      이 코드는 저장된 quantile_regressor_float.onnxquantile_regressor_double.onnx를 실행하여 MQL5에서 회귀 메트릭을 사용하는 것을 보여줍니다.

      //+------------------------------------------------------------------+
      //|                                            QuantileRegressor.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"
      
      #define   ModelName          "QuantileRegressor"
      #define   ONNXFilenameFloat  "quantile_regressor_float.onnx"
      #define   ONNXFilenameDouble "quantile_regressor_double.onnx"
      
      #resource ONNXFilenameFloat  as const uchar ExtModelFloat[];
      #resource ONNXFilenameDouble as const uchar ExtModelDouble[];
      
      #define   TestFloatModel  1
      #define   TestDoubleModel 2
      
      //+------------------------------------------------------------------+
      //| Calculate regression using float values                          |
      //+------------------------------------------------------------------+
      bool RunModelFloat(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         float input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=(float)input_vector[k];
      //--- prepare output tensor
         float output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      //+------------------------------------------------------------------+
      //| Calculate regression using double values                         |
      //+------------------------------------------------------------------+
      bool RunModelDouble(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         double input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=input_vector[k];
      //--- prepare output tensor
         double output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      
      //+------------------------------------------------------------------+
      //| Generate synthetic data                                          |
      //+------------------------------------------------------------------+
      bool GenerateData(const int n,vector &x,vector &y)
        {
         if(n<=0)
            return(false);
      //--- prepare arrays
         x.Resize(n);
         y.Resize(n);
      //---
         for(int i=0; i<n; i++)
           {
            x[i]=(double)1.0*i;
            y[i]=(double)(4*x[i] + 10*sin(x[i]*0.5));
           }
      //---
         return(true);
        }
      
      //+------------------------------------------------------------------+
      //| TestRegressionModel                                              |
      //+------------------------------------------------------------------+
      bool TestRegressionModel(const string model_name,const int model_type)
        {
      //---
         long  model=INVALID_HANDLE;
         ulong flags=ONNX_DEFAULT;
      
         if(model_type==TestFloatModel)
           {
            PrintFormat("\nTesting ONNX float: %s (%s)",model_name,ONNXFilenameFloat);
            model=OnnxCreateFromBuffer(ExtModelFloat,flags);
           }
         else
            if(model_type==TestDoubleModel)
              {
               PrintFormat("\nTesting ONNX double: %s (%s)",model_name,ONNXFilenameDouble);
               model=OnnxCreateFromBuffer(ExtModelDouble,flags);
              }
            else
              {
               PrintFormat("Model type is not incorrect.");
               return(false);
              }
      //--- check
         if(model==INVALID_HANDLE)
           {
            PrintFormat("model_name=%s OnnxCreate error %d",model_name,GetLastError());
            return(false);
           }
      //---
         vector x_values= {};
         vector y_true= {};
         vector y_predicted= {};
      //---
         int n=100;
         GenerateData(n,x_values,y_true);
      //---
         bool run_result=false;
         if(model_type==TestFloatModel)
           {
            run_result=RunModelFloat(model,x_values,y_predicted);
           }
         else
            if(model_type==TestDoubleModel)
              {
               run_result=RunModelDouble(model,x_values,y_predicted);
              }
      //---
         if(run_result)
           {
            PrintFormat("MQL5:   R-Squared (Coefficient of determination): %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_R2));
            PrintFormat("MQL5:   Mean Absolute Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MAE));
            PrintFormat("MQL5:   Mean Squared Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MSE));
           }
         else
            PrintFormat("Error %d",GetLastError());
      //--- release model
         OnnxRelease(model);
      //---
         return(true);
        }
      //+------------------------------------------------------------------+
      //| Script program start function                                    |
      //+------------------------------------------------------------------+
      int OnStart(void)
        {
      //--- test ONNX regression model for float
         TestRegressionModel(ModelName,TestFloatModel);
      //--- test ONNX regression model for double
         TestRegressionModel(ModelName,TestDoubleModel);
      //---
         return(0);
        }
      //+------------------------------------------------------------------+

      출력:

      QuantileRegressor (EURUSD,H1)   Testing ONNX float: QuantileRegressor (quantile_regressor_float.onnx)
      QuantileRegressor (EURUSD,H1)   MQL5:   R-Squared (Coefficient of determination): 0.9959915739158818
      QuantileRegressor (EURUSD,H1)   MQL5:   Mean Absolute Error: 6.3693091422201169
      QuantileRegressor (EURUSD,H1)   MQL5:   Mean Squared Error: 53.0425339108128071
      QuantileRegressor (EURUSD,H1)   
      QuantileRegressor (EURUSD,H1)   Testing ONNX double: QuantileRegressor (quantile_regressor_double.onnx)
      QuantileRegressor (EURUSD,H1)   MQL5:   R-Squared (Coefficient of determination): 0.9959915738839231
      QuantileRegressor (EURUSD,H1)   MQL5:   Mean Absolute Error: 6.3693091850025185
      QuantileRegressor (EURUSD,H1)   MQL5:   Mean Squared Error: 53.0425343337142721
      

      파이썬의 원래 배정밀도 모델과 비교:

      Testing ONNX float: QuantileRegressor (quantile_regressor_float.onnx)
      Python  Mean Absolute Error: 6.3693091850025185
      MQL5:   Mean Absolute Error: 6.3693091422201169
      
      Testing ONNX double: QuantileRegressor (quantile_regressor_double.onnx)
      Python  Mean Absolute Error: 6.3693091850025185
      MQL5:   Mean Absolute Error: 6.3693091850025185

      ONNX float MAE의 정확도: 소수점 이하 7자리, ONNX 더블 MAE의 정확도: 소수점 이하 16자리.


      2.1.18.3. quantile_regressor_float.onnx 및 quantile_regressor_double.onnx의 ONNX 표현


      Fig.62. Netron에서 quantile_regressor_float.onnx의 ONNX 표현

      그림 62. 네트론에서 quantile_regressor_float.onnx의 ONNX 표현


      Fig.63. Netron에서 quantile_regressor_double.onnx의 ONNX 표현

      그림 63. 네트론에서 quantile_regressor_double.onnx의 ONNX 표현



      2.1.19. sklearn.linear_model.RANSACRegressor

      RANSACRegressor은 RANSAC(무작위 표본 합의) 메서드을 사용하여 회귀 문제를 해결하는 데 사용되는 머신 러닝 메서드입니다.

      RANSAC 메서드은 이상값이나 불완전성이 포함된 데이터를 처리하도록 설계되어 이상값의 영향을 배제함으로써 보다 강력한 회귀 모델을 만들 수 있습니다.

      RANSACRegressor의 작동 방식:

      1. 데이터를 입력합니다: 피처(독립 변수)와 대상 변수(연속형)가 포함된 데이터 집합으로 시작합니다.
      2. 무작위 하위 집합 선택: RANSAC은 회귀 모델을 훈련하는 데 사용되는 데이터의 무작위 하위 집합을 선택하는 것으로 시작합니다. 이러한 하위 집합을 "가설"이라고 합니다.
      3. 가설에 모델 맞추기: 선택한 각 가설에 대해 회귀 모델이 학습됩니다. RANSACRegressor의 경우 일반적으로 선형 회귀가 사용되며 모델은 데이터의 하위 집합에 맞춰집니다.
      4. 이상값 평가: 모델을 학습한 후 모든 데이터에 대한 적합도를 평가합니다. 예측값과 실제값 사이의 오차는 각 데이터 포인트에 대해 계산됩니다.
      5. 이상값 식별: 지정된 임계값을 초과하는 오류가 있는 데이터 요소는 이상값으로 간주됩니다. 이러한 이상값은 모델 학습에 영향을 미치고 결과를 왜곡할 수 있습니다.
      6. 모델 업데이트: 이상값으로 간주되지 않는 모든 데이터 포인트는 회귀 모델을 업데이트하는 데 사용됩니다. 이 과정은 다른 무작위 가설을 사용하여 여러 번 반복될 수 있습니다.
      7. 최종 모델: 여러 번 반복한 후 RANSACRegressor는 데이터 하위 집합에서 학습된 최상의 모델을 선택하고 이를 최종 회귀 모델로 반환합니다.

      RANSACRegressor의 장점:

      • 이상값 견고성: RANSACRegressor는 이상값을 학습에서 제외하기 때문에 이상값에 대한 강력한 메서드입니다.
      • 강력한 회귀: 이 메서드를 사용하면 데이터에 이상값이나 불완전성이 있는 경우 보다 신뢰할 수 있는 회귀 모델을 만들 수 있습니다.

      RANSACRegressor의 한계:

      • 오류 임계값에 대한 민감도: 이상값으로 간주되는 지점을 결정하기 위해 오류 임계값을 선택하려면 실험이 필요할 수 있습니다.
      • 가설 선택의 복잡성: 초기 단계에서 좋은 가설을 선택하는 것은 쉬운 일이 아닐 것입니다.

      RANSACRegressor는 RANSAC 메서드를 기반으로 회귀 문제에 사용되는 머신 러닝 메서드입니다. 이 메서드를 사용하면 데이터에 이상값이나 불완전성이 있는 경우 모델에 미치는 영향을 배제하여 보다 강력한 회귀 모델을 만들 수 있습니다.


      2.1.19.1. RANSACRegressor 모델을 생성하고 float 및 double용 ONNX로 내보내기 위한 코드

      이 코드는 sklearn.linear_model.RANSACRegressor 모델을 생성하고 합성 데이터로 학습시키고 모델을 ONNX 형식으로 저장하고 플로트 및 더블 입력 데이터를 모두 사용하여 예측을 수행합니다. 또한 원본 모델과 ONNX로 내보낸 모델 모두의 정확도를 평가합니다.

      # RANSACRegressor.py
      # 이 코드는 RANSACRegressor 모델을 학습하고 이를 ONNX 형식(float 및 double 모두)으로 내보내고 ONNX 모델을 사용하여 예측하는 과정을 보여 줍니다.
      # Copyright 2023, MetaQuotes Ltd.
      # https://www.mql5.com


      # 소수점 이하 자릿수를 비교학 위한 함수
      def compare_decimal_places(value1, value2):
      # 두 값을 모두 문자열로 변환
          str_value1 = str(value1)
          str_value2 = str(value2)

      # 문자열에서 소수점 위치 찾기
          dot_position1 = str_value1.find(".")
          dot_position2 = str_value2.find(".")

      # 값 중 소수점이 없는 경우 0을 반환합니다.
          if dot_position1 == -1 or dot_position2 == -1:
              return 0

          # 소수점 이하 자릿수 계산
          decimal_places1 = len(str_value1) - dot_position1 - 1
          decimal_places2 = len(str_value2) - dot_position2 - 1

          # 소수점 이하 두 자리 수 중 최소값 찾기
          min_decimal_places = min(decimal_places1, decimal_places2)

          # 소수점 이하 자릿수가 일치하도록 카운트를 초기화
          matching_count = 0

          # 소수점 이하 문자 비교
          for i in range(1, min_decimal_places + 1):
              if str_value1[dot_position1 + i] == str_value2[dot_position2 + i]:
                  matching_count += 1
              else:
                  break

          return matching_count

      # 필요한 라이브러리 가져오기
      import numpy as np
      import matplotlib.pyplot as plt
      from sklearn.linear_model import RANSACRegressor
      from sklearn.metrics import r2_score,mean_absolute_error,mean_squared_error
      import onnx
      import onnxruntime as ort
      from skl2onnx import convert_sklearn
      from skl2onnx.common.data_types import FloatTensorType
      from skl2onnx.common.data_types import DoubleTensorType
      from sys import argv

      # 모델 저장 경로를 정의합니다.
      data_path = argv[0]
      last_index = data_path.rfind("\\")+ 1
      data_path = data_path[0:last_index]

      # 회귀를 위한 합성 데이터 생성
      X = np.arange(0,100,1).reshape(-1,1)
      y = 4*X + 10*np.sin(X*0.5)

      model_name = "RANSACRegressor"
      onnx_model_filename = data_path + "ransac_regressor"

      # RANSACRegressor 모델 만들기
      regression_model = RANSACRegressor()

      # 데이터에 모델 맞추기
      regression_model.fit(X, y.ravel())

      # 전체 데이터 집합에 대한 값 예측
      y_pred = regression_model.predict(X)

      # 모델의 성능 평가
      r2 = r2_score(y, y_pred)
      MSE = MEAN_SQUARE_ERROR(Y, Y_PED)
      MAE = MEAN_ABSOLUTE_ERROR(Y, Y_PED)

      print("\n"+model_name+" Original model (double)")
      print("R-squared (Coefficient of determination):", r2)
      print("Mean Absolute Error:", mae)
      print("Mean Squared Error:", mse)

      # ONNX 모델로 변환 (float)
      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.

      initial_type_float = [('float_input', FloatTensorType([None, X.shape[1]])]]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_float = convert_sklearn(regression_model, initial_types=initial_type_float, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_float.onnx"
      onnx.save_model(onnx_model_float, onnx_filename)

      print("\n"+model_name+" ONNX model (float)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.
      initial_type_float = X.astype(np.float32)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_float = onnx_session.run([output_name], {input_name: initial_type_float})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_float = r2_score(y, y_pred_onnx_float)
      mse_onnx_float = mean_squared_error(y, y_pred_onnx_float)
      mae_onnx_float = mean_absolute_error(y, y_pred_onnx_float)
      print("R-squared (Coefficient of determination)", r2_onnx_float)
      print("Mean Absolute Error:", mae_onnx_float)
      print("Mean Squared Error:", mse_onnx_float)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_float))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_float))
      print("ONNX: MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_float))
      print("float ONNX model precision: ",compare_decimal_places(mae, mae_onnx_float))

      # 그림 크기 설정
      plt.figure(figsize=(8, 5))
      # 원본 데이터와 회귀선을 그림으로 표시합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with float ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_float.png')

      # ONNX 모델로 변환 (double)
      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.

      initial_type_double = [('double_input', DoubleTensorType([None, X.shape[1]]))]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_double = convert_sklearn(regression_model, initial_types=initial_type_double, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_double.onnx"
      onnx.save_model(onnx_model_double, onnx_filename)

      print("\n"+model_name+" ONNX model (double)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.
      initial_type_double = X.astype(np.float64)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_double = onnx_session.run([output_name], {input_name: initial_type_double})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_double = r2_score(y, y_pred_onnx_double)
      mse_onnx_double = mean_squared_error(y, y_pred_onnx_double)
      mae_onnx_double = mean_absolute_error(y, y_pred_onnx_double)
      print("R-squared (Coefficient of determination)", r2_onnx_double)
      print("Mean Absolute Error:", mae_onnx_double)
      print("Mean Squared Error:", mse_onnx_double)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_double))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_double))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_double))
      print("double ONNX model precision: ",compare_decimal_places(mae, mae_onnx_double))

      # 그림 크기 설정
      plt.figure(figsize=(8, 5))
      # 원본 데이터와 회귀선을 그림으로 표시합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with double ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_double.png')

      출력:

      Python  RANSACRegressor Original model (double)
      Python  R-squared (Coefficient of determination): 0.9962382642613388
      Python  Mean Absolute Error: 6.347737926336427
      Python  Mean Squared Error: 49.77814017128179
      Python  
      Python  RANSACRegressor ONNX model (float)
      Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\ransac_regressor_float.onnx
      Python  Information about input tensors in ONNX:
      Python  1. Name: float_input, Data Type: tensor(float), Shape: [None, 1]
      Python  Information about output tensors in ONNX:
      Python  1. Name: variable, Data Type: tensor(float), Shape: [None, 1]
      Python  R-squared (Coefficient of determination) 0.9962382641628886
      Python  Mean Absolute Error: 6.3477377671679385
      Python  Mean Squared Error: 49.77814147404787
      Python  R^2 matching decimal places:  9
      Python  MAE matching decimal places:  6
      Python  ONNX: MSE matching decimal places:  5
      Python  float ONNX model precision:  6
      Python  
      Python  RANSACRegressor ONNX model (double)
      Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\ransac_regressor_double.onnx
      Python  Information about input tensors in ONNX:
      Python  1. Name: double_input, Data Type: tensor(double), Shape: [None, 1]
      Python  Information about output tensors in ONNX:
      Python  1. Name: variable, Data Type: tensor(double), Shape: [None, 1]
      Python  R-squared (Coefficient of determination) 0.9962382642613388
      Python  Mean Absolute Error: 6.347737926336427
      Python  Mean Squared Error: 49.77814017128179
      Python  R^2 matching decimal places:  16
      Python  MAE matching decimal places:  15
      Python  MSE matching decimal places:  14
      Python  double ONNX model precision:  15
      

      Fig.64. RANSACRegressor.py의 결과(float ONNX)

      그림 64. RANSACRegressor.py의 결과(float ONNX)


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

      이 코드는 저장된 ransac_regressor_float.onnxransac_regressor_double.onnx 모델을 실행하고 MQL5에서 회귀 메트릭을 사용하는 것을 보여줍니다.

      //+------------------------------------------------------------------+
      //|                                              RANSACRegressor.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"
      
      #define   ModelName          "RANSACRegressor"
      #define   ONNXFilenameFloat  "ransac_regressor_float.onnx"
      #define   ONNXFilenameDouble "ransac_regressor_double.onnx"
      
      #resource ONNXFilenameFloat  as const uchar ExtModelFloat[];
      #resource ONNXFilenameDouble as const uchar ExtModelDouble[];
      
      #define   TestFloatModel  1
      #define   TestDoubleModel 2
      
      //+------------------------------------------------------------------+
      //| Calculate regression using float values                          |
      //+------------------------------------------------------------------+
      bool RunModelFloat(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         float input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=(float)input_vector[k];
      //--- prepare output tensor
         float output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      //+------------------------------------------------------------------+
      //| Calculate regression using double values                         |
      //+------------------------------------------------------------------+
      bool RunModelDouble(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         double input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=input_vector[k];
      //--- prepare output tensor
         double output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      
      //+------------------------------------------------------------------+
      //| Generate synthetic data                                          |
      //+------------------------------------------------------------------+
      bool GenerateData(const int n,vector &x,vector &y)
        {
         if(n<=0)
            return(false);
      //--- prepare arrays
         x.Resize(n);
         y.Resize(n);
      //---
         for(int i=0; i<n; i++)
           {
            x[i]=(double)1.0*i;
            y[i]=(double)(4*x[i] + 10*sin(x[i]*0.5));
           }
      //---
         return(true);
        }
      
      //+------------------------------------------------------------------+
      //| TestRegressionModel                                              |
      //+------------------------------------------------------------------+
      bool TestRegressionModel(const string model_name,const int model_type)
        {
      //---
         long  model=INVALID_HANDLE;
         ulong flags=ONNX_DEFAULT;
      
         if(model_type==TestFloatModel)
           {
            PrintFormat("\nTesting ONNX float: %s (%s)",model_name,ONNXFilenameFloat);
            model=OnnxCreateFromBuffer(ExtModelFloat,flags);
           }
         else
            if(model_type==TestDoubleModel)
              {
               PrintFormat("\nTesting ONNX double: %s (%s)",model_name,ONNXFilenameDouble);
               model=OnnxCreateFromBuffer(ExtModelDouble,flags);
              }
            else
              {
               PrintFormat("Model type is not incorrect.");
               return(false);
              }
      //--- check
         if(model==INVALID_HANDLE)
           {
            PrintFormat("model_name=%s OnnxCreate error %d",model_name,GetLastError());
            return(false);
           }
      //---
         vector x_values= {};
         vector y_true= {};
         vector y_predicted= {};
      //---
         int n=100;
         GenerateData(n,x_values,y_true);
      //---
         bool run_result=false;
         if(model_type==TestFloatModel)
           {
            run_result=RunModelFloat(model,x_values,y_predicted);
           }
         else
            if(model_type==TestDoubleModel)
              {
               run_result=RunModelDouble(model,x_values,y_predicted);
              }
      //---
         if(run_result)
           {
            PrintFormat("MQL5:   R-Squared (Coefficient of determination): %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_R2));
            PrintFormat("MQL5:   Mean Absolute Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MAE));
            PrintFormat("MQL5:   Mean Squared Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MSE));
           }
         else
            PrintFormat("Error %d",GetLastError());
      //--- release model
         OnnxRelease(model);
      //---
         return(true);
        }
      //+------------------------------------------------------------------+
      //| Script program start function                                    |
      //+------------------------------------------------------------------+
      int OnStart(void)
        {
      //--- test ONNX regression model for float
         TestRegressionModel(ModelName,TestFloatModel);
      //--- test ONNX regression model for double
         TestRegressionModel(ModelName,TestDoubleModel);
      //---
         return(0);
        }
      //+------------------------------------------------------------------+

      출력:

      RANSACRegressor (EURUSD,H1)     Testing ONNX float: RANSACRegressor (ransac_regressor_float.onnx)
      RANSACRegressor (EURUSD,H1)     MQL5:   R-Squared (Coefficient of determination): 0.9962382641628886
      RANSACRegressor (EURUSD,H1)     MQL5:   Mean Absolute Error: 6.3477377671679385
      RANSACRegressor (EURUSD,H1)     MQL5:   Mean Squared Error: 49.7781414740478638
      RANSACRegressor (EURUSD,H1)     
      RANSACRegressor (EURUSD,H1)     Testing ONNX double: RANSACRegressor (ransac_regressor_double.onnx)
      RANSACRegressor (EURUSD,H1)     MQL5:   R-Squared (Coefficient of determination): 0.9962382642613388
      RANSACRegressor (EURUSD,H1)     MQL5:   Mean Absolute Error: 6.3477379263364266
      RANSACRegressor (EURUSD,H1)     MQL5:   Mean Squared Error: 49.7781401712817768
      

      파이썬의 원래 배정밀도 모델과 비교:

      Testing ONNX float: RANSACRegressor (ransac_regressor_float.onnx)
      Python  Mean Absolute Error: 6.347737926336427
      MQL5:   Mean Absolute Error: 6.3477377671679385
           
      Testing ONNX double: RANSACRegressor (ransac_regressor_double.onnx)
      Python  Mean Absolute Error: 6.347737926336427
      MQL5:   Mean Absolute Error: 6.3477379263364266

      ONNX float MAE의 정확도: 소수점 이하 6자리, 정확도 ONNX double MAE: 소수점 이하 14자리.


      2.1.19.3. ransac_regressor_float.onnx 및 ransac_regressor_double.onnx의 ONNX 표현


      그림 65. Netron에서 ransac_regressor_float.onnx의 ONNX 표현

      그림 65. 넷트론에서 ransac_regressor_float.onnx의 ONNX 표현


      Fig.66. Netron에서 ransac_regressor_double.onnx의 ONNX 표현

      그림 66. 네트론에서 ransac_regressor_double.onnx의 ONNX 표현



      2.1.20. sklearn.linear_model.TheilSenRegressor

      Theil-Sen 회귀(Theil-Sen 추정기)는 독립 변수와 목표 변수 간의 선형 관계를 추정하는 데 사용되는 회귀 추정 메서드입니다.

      데이터에 이상값과 노이즈가 있는 경우 일반 선형 회귀에 비해 더 강력한 추정치를 제공합니다.

      Theil-Sen 회귀의 작동 방식:

      1. 포인트 선택: 처음에 Theil-Sen은 학습 데이터 세트에서 무작위 데이터 포인트 쌍을 선택합니다.
      2. 기울기 계산: 각 데이터 포인트 쌍에 대해 이 메서드는 해당 포인트를 통과하는 선의 기울기를 계산하여 기울기 집합을 생성합니다.
      3. 중앙값 기울기: 그런 다음 이 메서드는 기울기 집합에서 중앙값 기울기를 찾습니다. 이 중앙값 기울기는 선형 회귀 기울기의 추정치로 사용됩니다.
      4. 중앙값 편차: 이 메서드는 각 데이터 포인트에 대해 편차(실제 값과 중앙값 기울기를 기준으로 예측한 값의 차이)를 계산하고 이러한 편차의 중앙값을 찾습니다. 이렇게 하면 선형 회귀 절편 계수에 대한 추정치가 생성됩니다.
      5. 최종 추정치: 기울기 및 절편 계수의 최종 추정치는 선형 회귀 모델을 구축하는 데 사용됩니다.

      Theil-Sen 회귀의 장점:

      • 이상값 복원력: Theil-Sen 회귀는 일반 선형 회귀에 비해 이상값과 데이터 노이즈에 대해 더 강력합니다.
      • 덜 엄격한 가정: 이 메서드는 데이터 분포나 종속성 형태에 대한 엄격한 가정이 필요하지 않으므로 더 다양한 용도로 사용될 수 있습니다.
      • 다중 선형 데이터에 적합합니다: Theil-Sen 회귀는 독립 변수의 상관 관계가 높은 데이터(다중 공선성 문제)에서 잘 작동합니다.

      Theil-Sen 회귀의 한계:

      • 계산의 복잡성: 모든 데이터 포인트 쌍에 대한 중앙값 기울기를 계산하는 것은 특히 대규모 데이터 집합의 경우 시간이 많이 걸릴 수 있습니다.
      • 인터셉트 계수 추정: 중앙값 편차는 절편 계수를 추정하는 데 사용되며 이상값이 있을 경우에는 편향이 발생할 수 있습니다.

      Theil-Sen 회귀분석은 특히 이상값과 데이터 노이즈가 있는 경우 독립변수와 목표 변수 간의 선형 관계를 안정적으로 평가할 수 있는 회귀 추정 메서드입니다. 이 메서드는 실제 데이터 조건에서 안정적인 추정치가 필요할 때 유용합니다.


      2.1.20.1. TheilSenRegressor를 생성하고 float 및 double용 ONNX로 내보내는 코드

      이 코드는 sklearn.linear_model.TheilSenRegressor 모델을 생성하고, 합성 데이터로 학습시키고 모델을 ONNX 형식으로 저장하고 플로트 및 더블 입력 데이터를 모두 사용하여 예측을 수행합니다. 또한 원본 모델과 ONNX로 내보낸 모델 모두의 정확도를 평가합니다.

      # TheilSenRegressor.py
      # 이 코드는 TheilSenRegressor 모델을 학습하고 이를 ONNX 형식(float 및 double 모두)으로 내보내고 ONNX 모델을 사용하여 예측하는 과정을 보여줍니다.
      # Copyright 2023, MetaQuotes Ltd.
      # https://www.mql5.com


      # 소수점 이하 자릿수를 비교학 위한 함수
      def compare_decimal_places(value1, value2):
      # 두 값을 모두 문자열로 변환
          str_value1 = str(value1)
          str_value2 = str(value2)

      # 문자열에서 소수점 위치 찾기
          dot_position1 = str_value1.find(".")
          dot_position2 = str_value2.find(".")

      # 값 중 소수점이 없는 경우 0을 반환합니다.
          if dot_position1 == -1 or dot_position2 == -1:
              return 0

          # 소수점 이하 자릿수 계산
          decimal_places1 = len(str_value1) - dot_position1 - 1
          decimal_places2 = len(str_value2) - dot_position2 - 1

          # 소수점 이하 두 자리 수 중 최소값 찾기
          min_decimal_places = min(decimal_places1, decimal_places2)

          # 소수점 이하 자릿수가 일치하도록 카운트를 초기화
          matching_count = 0

          # 소수점 이하 문자 비교
          for i in range(1, min_decimal_places + 1):
              if str_value1[dot_position1 + i] == str_value2[dot_position2 + i]:
                  matching_count += 1
              else:
                  break

          return matching_count

      # 필요한 라이브러리 가져오기
      import numpy as np
      import matplotlib.pyplot as plt
      에서 sklearn.linear_model import TheilSenRegressor
      from sklearn.metrics import r2_score,mean_absolute_error,mean_squared_error
      import onnx
      import onnxruntime as ort
      from skl2onnx import convert_sklearn
      에서 skl2onnx.common.data_types import FloatTensorType
      from skl2onnx.common.data_types import DoubleTensorType
      from sys import argv

      # 모델 저장 경로를 정의합니다.
      data_path = argv[0]
      last_index = data_path.rfind("\\")+ 1
      data_path = data_path[0:last_index]

      # 회귀를 위한 합성 데이터 생성
      X = np.arange(0,100,1).reshape(-1,1)
      y = 4*X + 10*np.sin(X*0.5)

      model_name = "TheilSenRegressor"
      onnx_model_filename = data_path + "theil_sen_regressor"

      # TheilSen 회귀 모델 만들기
      regression_model = TheilSenRegressor()

      # 데이터에 모델 맞추기
      regression_model.fit(X, y.ravel())

      # 전체 데이터 집합에 대한 값 예측
      y_pred = regression_model.predict(X)

      # 모델의 성능 평가
      r2 = r2_score(y, y_pred)
      MSE = MEAN_SQUARE_ERROR(Y, Y_PED)
      MAE = MEAN_ABSOLUTE_ERROR(Y, Y_PED)

      print("\n"+model_name+" Original model (double)")
      print("R-squared (Coefficient of determination):", r2)
      print("Mean Absolute Error:", mae)
      print("Mean Squared Error:", mse)

      # ONNX 모델로 변환 (float)
      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.

      initial_type_float = [('float_input', FloatTensorType([None, X.shape[1]])]]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_float = convert_sklearn(regression_model, initial_types=initial_type_float, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_float.onnx"
      onnx.save_model(onnx_model_float, onnx_filename)

      print("\n"+model_name+" ONNX model (float)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.
      initial_type_float = X.astype(np.float32)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_float = onnx_session.run([output_name], {input_name: initial_type_float})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_float = r2_score(y, y_pred_onnx_float)
      mse_onnx_float = mean_squared_error(y, y_pred_onnx_float)
      mae_onnx_float = mean_absolute_error(y, y_pred_onnx_float)
      print("R-squared (Coefficient of determination)", r2_onnx_float)
      print("Mean Absolute Error:", mae_onnx_float)
      print("Mean Squared Error:", mse_onnx_float)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_float))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_float))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_float))
      print("float ONNX model precision: ",compare_decimal_places(mae, mae_onnx_float))

      # 그림 크기 설정
      plt.figure(figsize=(8,5))
      # 원본 데이터와 회귀 데이터를 플로팅합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with float ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_float.png')

      # ONNX 모델로 변환 (double)
      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.

      initial_type_double = [('double_input', DoubleTensorType([None, X.shape[1]]))]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_double = convert_sklearn(regression_model, initial_types=initial_type_double, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_double.onnx"
      onnx.save_model(onnx_model_double, onnx_filename)

      print("\n"+model_name+" ONNX model (double)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.
      initial_type_double = X.astype(np.float64)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_double = onnx_session.run([output_name], {input_name: initial_type_double})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_double = r2_score(y, y_pred_onnx_double)
      mse_onnx_double = mean_squared_error(y, y_pred_onnx_double)
      mae_onnx_double = mean_absolute_error(y, y_pred_onnx_double)
      print("R-squared (Coefficient of determination)", r2_onnx_double)
      print("Mean Absolute Error:", mae_onnx_double)
      print("Mean Squared Error:", mse_onnx_double)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_double))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_double))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_double))
      print("double ONNX model precision: ",compare_decimal_places(mae, mae_onnx_double))

      # 그림 크기 설정
      plt.figure(figsize=(8,5))
      # 원본 데이터와 회귀 데이터를 플로팅합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with double ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_double.png')

      출력:

      Python  TheilSenRegressor Original model (double)
      Python  R-squared (Coefficient of determination): 0.9962329196940459
      Python  Mean Absolute Error: 6.338686004537594
      Python  Mean Squared Error: 49.84886353898735
      Python  
      Python  TheilSenRegressor ONNX model (float)
      Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\theil_sen_regressor_float.onnx
      Python  Information about input tensors in ONNX:
      Python  1. Name: float_input, Data Type: tensor(float), Shape: [None, 1]
      Python  Information about output tensors in ONNX:
      Python  1. Name: variable, Data Type: tensor(float), Shape: [None, 1]
      Python  R-squared (Coefficient of determination) 0.996232919516505
      Python  Mean Absolute Error: 6.338686370832071
      Python  Mean Squared Error: 49.84886588834327
      Python  R^2 matching decimal places:  9
      Python  MAE matching decimal places:  6
      Python  MSE matching decimal places:  5
      Python  float ONNX model precision:  6
      Python  
      Python  TheilSenRegressor ONNX model (double)
      Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\theil_sen_regressor_double.onnx
      Python  Information about input tensors in ONNX:
      Python  1. Name: double_input, Data Type: tensor(double), Shape: [None, 1]
      Python  Information about output tensors in ONNX:
      Python  1. Name: variable, Data Type: tensor(double), Shape: [None, 1]
      Python  R-squared (Coefficient of determination) 0.9962329196940459
      Python  Mean Absolute Error: 6.338686004537594
      Python  Mean Squared Error: 49.84886353898735
      Python  R^2 matching decimal places:  16
      Python  MAE matching decimal places:  15
      Python  MSE matching decimal places:  14
      Python  double ONNX model precision:  15
      

      Fig.67. TheilSenRegressor.py의 결과(float ONNX)

      그림 67. TheilSenRegressor.py의 결과(float ONNX)


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

      이 코드는 저장된 theil_sen_regressor_float.onnxtheil_sen_regressor_double.onnx 모델을 실행하고 MQL5에서 회귀 메트릭을 사용하는 것을 보여 줍니다.

      //+------------------------------------------------------------------+
      //|                                            TheilSenRegressor.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"
      
      #define   ModelName          "TheilSenRegressor"
      #define   ONNXFilenameFloat  "theil_sen_regressor_float.onnx"
      #define   ONNXFilenameDouble "theil_sen_regressor_double.onnx"
      
      #resource ONNXFilenameFloat  as const uchar ExtModelFloat[];
      #resource ONNXFilenameDouble as const uchar ExtModelDouble[];
      
      #define   TestFloatModel  1
      #define   TestDoubleModel 2
      
      //+------------------------------------------------------------------+
      //| Calculate regression using float values                          |
      //+------------------------------------------------------------------+
      bool RunModelFloat(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         float input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=(float)input_vector[k];
      //--- prepare output tensor
         float output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      //+------------------------------------------------------------------+
      //| Calculate regression using double values                         |
      //+------------------------------------------------------------------+
      bool RunModelDouble(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         double input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=input_vector[k];
      //--- prepare output tensor
         double output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      
      //+------------------------------------------------------------------+
      //| Generate synthetic data                                          |
      //+------------------------------------------------------------------+
      bool GenerateData(const int n,vector &x,vector &y)
        {
         if(n<=0)
            return(false);
      //--- prepare arrays
         x.Resize(n);
         y.Resize(n);
      //---
         for(int i=0; i<n; i++)
           {
            x[i]=(double)1.0*i;
            y[i]=(double)(4*x[i] + 10*sin(x[i]*0.5));
           }
      //---
         return(true);
        }
      
      //+------------------------------------------------------------------+
      //| TestRegressionModel                                              |
      //+------------------------------------------------------------------+
      bool TestRegressionModel(const string model_name,const int model_type)
        {
      //---
         long  model=INVALID_HANDLE;
         ulong flags=ONNX_DEFAULT;
      
         if(model_type==TestFloatModel)
           {
            PrintFormat("\nTesting ONNX float: %s (%s)",model_name,ONNXFilenameFloat);
            model=OnnxCreateFromBuffer(ExtModelFloat,flags);
           }
         else
            if(model_type==TestDoubleModel)
              {
               PrintFormat("\nTesting ONNX double: %s (%s)",model_name,ONNXFilenameDouble);
               model=OnnxCreateFromBuffer(ExtModelDouble,flags);
              }
            else
              {
               PrintFormat("Model type is not incorrect.");
               return(false);
              }
      //--- check
         if(model==INVALID_HANDLE)
           {
            PrintFormat("model_name=%s OnnxCreate error %d",model_name,GetLastError());
            return(false);
           }
      //---
         vector x_values= {};
         vector y_true= {};
         vector y_predicted= {};
      //---
         int n=100;
         GenerateData(n,x_values,y_true);
      //---
         bool run_result=false;
         if(model_type==TestFloatModel)
           {
            run_result=RunModelFloat(model,x_values,y_predicted);
           }
         else
            if(model_type==TestDoubleModel)
              {
               run_result=RunModelDouble(model,x_values,y_predicted);
              }
      //---
         if(run_result)
           {
            PrintFormat("MQL5:   R-Squared (Coefficient of determination): %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_R2));
            PrintFormat("MQL5:   Mean Absolute Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MAE));
            PrintFormat("MQL5:   Mean Squared Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MSE));
           }
         else
            PrintFormat("Error %d",GetLastError());
      //--- release model
         OnnxRelease(model);
      //---
         return(true);
        }
      //+------------------------------------------------------------------+
      //| Script program start function                                    |
      //+------------------------------------------------------------------+
      int OnStart(void)
        {
      //--- test ONNX regression model for float
         TestRegressionModel(ModelName,TestFloatModel);
      //--- test ONNX regression model for double
         TestRegressionModel(ModelName,TestDoubleModel);
      //---
         return(0);
        }
      //+------------------------------------------------------------------+

      출력:

      TheilSenRegressor (EURUSD,H1)   Testing ONNX float: TheilSenRegressor (theil_sen_regressor_float.onnx)
      TheilSenRegressor (EURUSD,H1)   MQL5:   R-Squared (Coefficient of determination): 0.9962329195165051
      TheilSenRegressor (EURUSD,H1)   MQL5:   Mean Absolute Error: 6.3386863708320735
      TheilSenRegressor (EURUSD,H1)   MQL5:   Mean Squared Error: 49.8488658883432691
      TheilSenRegressor (EURUSD,H1)   
      TheilSenRegressor (EURUSD,H1)   Testing ONNX double: TheilSenRegressor (theil_sen_regressor_double.onnx)
      TheilSenRegressor (EURUSD,H1)   MQL5:   R-Squared (Coefficient of determination): 0.9962329196940459
      TheilSenRegressor (EURUSD,H1)   MQL5:   Mean Absolute Error: 6.3386860045375943
      TheilSenRegressor (EURUSD,H1)   MQL5:   Mean Squared Error: 49.8488635389873735
      

      파이썬의 원래 배정밀도 모델과 비교:

      Testing ONNX float: TheilSenRegressor (theil_sen_regressor_float.onnx)
      Python  Mean Absolute Error: 6.338686004537594
      MQL5:   Mean Absolute Error: 6.3386863708320735
              
      Testing ONNX double: TheilSenRegressor (theil_sen_regressor_double.onnx)
      Python  Mean Absolute Error: 6.338686004537594
      MQL5:   Mean Absolute Error: 6.3386860045375943

      ONNX float MAE의 정확도: 소수점 이하 6자리, 정확도 ONNX double MAE: 소수점 이하 15자리.


      2.1.20.3. theil_sen_regressor_float.onnx 및 theil_sen_regressor_double.onnx의 ONNX 표현


      Fig.68. Netron에서 theil_sen_regressor_float.onnx의 ONNX 표현

      그림 68. 네트론에서 theil_sen_regressor_float.onnx의 ONNX 표현


      Fig.69. Netron에서 theil_sen_regressor_double.onnx의 ONNX 표현

      그림 69. 네트론에서 theil_sen_regressor_double.onnx의 ONNX 표현



      2.1.21. sklearn.linear_model.LinearSVR

      LinearSVR (선형 서포트 벡터 회귀)는 SVM(서포트 벡터 머신) 방식을 기반으로 하는 회귀 작업용 머신 러닝 모델입니다.

      이 메서드는 선형 커널을 사용하여 피처와 대상 변수 간의 선형 관계를 찾는 데 사용됩니다.

      LinearSVR 작동 방식:

      1. 데이터를 입력합니다: LinearSVR은 피처(독립 변수)와 그에 해당하는 목표 변수 값을 포함하는 데이터 세트로 시작합니다.
      2. 선형 모델 선택하기: 이 모델은 선형 회귀 방정식으로 설명되는 피처와 대상 변수 사이에 선형 관계가 있다고 가정합니다.
      3. 모델 훈련: LinearSVR은 예측 오차와 허용 오차(엡실론)를 고려한 손실 함수를 최소화하여 모델 계수의 최적 값을 찾습니다.
      4. 예측 생성하기: 학습 후 모델은 발견된 계수를 기반으로 새로운 데이터의 목표 변수 값을 예측할 수 있습니다.

      LinearSVR의 장점:

      • 벡터 회귀를 지원합니다: LinearSVR은 서포트 벡터 머신 방식을 사용하여 허용 가능한 오차를 고려하면서 데이터 간 최적의 분리 거리를 찾을 수 있습니다.
      • 여러 피처을 지원합니다: 이 모델은 여러 피처를 처리하고 고차원의 데이터를 처리할 수 있습니다.
      • 정규화: 선형SVR은 정규화를 통해 과적합을 방지하고 보다 안정적인 예측을 보장합니다.

      LinearSVR의 한계:

      • 선형성: LinearSVR은 피처와 대상 변수 간의 선형 관계를 사용하여 제약을 받습니다. 복잡하고 비선형적인 관계의 경우 모델이 충분히 유연하지 않을 수 있습니다.
      • 이상값에 대한 민감도: 이 모델은 데이터의 이상값과 허용 오차(엡실론)에 민감할 수 있습니다.
      • 복잡한 관계를 캡처할 수 없습니다: 다른 선형 모델과 마찬가지로 LinearSVR은 피처와 대상 변수 간의 복잡한 비선형 관계를 캡처할 수 없습니다.

      LinearSVR은 서포트 벡터 머신 방식을 활용하여 피처와 대상 변수 간의 선형 관계를 찾는 회귀 머신 러닝 모델입니다. 정규화를 지원하며 허용 가능한 오류를 제어하는 것이 필수적인 작업에 사용될 수 있습니다. 그러나 이 모델은 선형 의존성으로 인해 제한적이며 이상값에 민감할 수 있습니다.


      2.1.21.1. LinearSVR 모델을 생성하고 float 및 double용 ONNX로 내보내기 위한 코드

      이 코드는 sklearn.linear_model.LinearSVR 모델을 생성하고, 합성 데이터로 학습시키고 모델을 ONNX 형식으로 저장하고 플로트 및 더블 입력 데이터를 모두 사용하여 예측을 수행합니다. 또한 원본 모델과 ONNX로 내보낸 모델 모두의 정확도를 평가합니다.

      # LinearSVR.py
      # 이 코드는 LinearSVR 모델을 학습하고 ONNX 형식(float 및 double 모두)으로 내보내고 ONNX 모델을 사용하여 예측하는 과정을 보여 줍니다.
      # Copyright 2023, MetaQuotes Ltd.
      # https://www.mql5.com


      # 소수점 이하 자릿수를 비교학 위한 함수
      def compare_decimal_places(value1, value2):
      # 두 값을 모두 문자열로 변환
          str_value1 = str(value1)
          str_value2 = str(value2)

      # 문자열에서 소수점 위치 찾기
          dot_position1 = str_value1.find(".")
          dot_position2 = str_value2.find(".")

      # 값 중 소수점이 없는 경우 0을 반환합니다.
          if dot_position1 == -1 or dot_position2 == -1:
              return 0

          # 소수점 이하 자릿수 계산
          decimal_places1 = len(str_value1) - dot_position1 - 1
          decimal_places2 = len(str_value2) - dot_position2 - 1

          # 소수점 이하 두 자리 수 중 최소값 찾기
          min_decimal_places = min(decimal_places1, decimal_places2)

          # 소수점 이하 자릿수가 일치하도록 카운트를 초기화
          matching_count = 0

          # 소수점 이하 문자 비교
          for i in range(1, min_decimal_places + 1):
              if str_value1[dot_position1 + i] == str_value2[dot_position2 + i]:
                  matching_count += 1
              else:
                  break

          return matching_count

      # 필요한 라이브러리 가져오기
      import numpy as np
      import matplotlib.pyplot as plt
      에서 sklearn.svm import LinearSVR
      from sklearn.metrics import r2_score,mean_absolute_error,mean_squared_error
      import onnx
      import
      onnxruntime as ort
      from skl2onnx import convert_sklearn
      from skl2onnx.common.data_types import FloatTensorType
      from skl2onnx.common.data_types import DoubleTensorType
      from sys import argv

      # 모델 저장 경로를 정의합니다.
      data_path = argv[0]
      last_index = data_path.rfind("\\")+ 1
      data_path = data_path[0:last_index]

      # 회귀를 위한 합성 데이터 생성
      X = np.arange(0,100,1).reshape(-1,1)
      y = 4*X + 10*np.sin(X*0.5)

      model_name = "LinearSVR"
      onnx_model_filename = data_path + "linear_svr"

      # 선형 SVR 모델 생성
      linear_svr_model = LinearSVR()

      # 데이터에 모델 맞추기
      linear_svr_model.fit(X, y.ravel())

      # 전체 데이터 집합에 대한 값 예측
      y_pred = linear_svr_model.predict(X)

      # 모델의 성능 평가
      r2 = r2_score(y, y_pred)
      MSE = MEAN_SQUARE_ERROR(Y, Y_PED)
      MAE = MEAN_ABSOLUTE_ERROR(Y, Y_PED)

      print("\n"+model_name+" Original model (double)")
      print("R-squared (Coefficient of determination):", r2)
      print("Mean Absolute Error:", mae)
      print("Mean Squared Error:", mse)

      # ONNX 모델로 변환 (float)
      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.

      initial_type_float = [('float_input', FloatTensorType([None, X.shape[1]])]]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_float = convert_sklearn(linear_svr_model, initial_types=initial_type_float, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_float.onnx"
      onnx.save_model(onnx_model_float, onnx_filename)

      print("\n"+model_name+" ONNX model (float)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.
      initial_type_float = X.astype(np.float32)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_float = onnx_session.run([output_name], {input_name: initial_type_float})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_float = r2_score(y, y_pred_onnx_float)
      mse_onnx_float = mean_squared_error(y, y_pred_onnx_float)
      mae_onnx_float = mean_absolute_error(y, y_pred_onnx_float)
      print("R-squared (Coefficient of determination)", r2_onnx_float)
      print("Mean Absolute Error:", mae_onnx_float)
      print("Mean Squared Error:", mse_onnx_float)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_float))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_float))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_float))
      print("float ONNX model precision: ",compare_decimal_places(mae, mae_onnx_float))

      # 그림 크기 설정
      plt.figure(figsize=(8, 5))
      # 원본 데이터와 회귀선을 그림으로 표시합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with float ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_float.png')

      # ONNX 모델로 변환 (double)
      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.

      initial_type_double = [('double_input', DoubleTensorType([None, X.shape[1]]))]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_double = convert_sklearn(linear_svr_model, initial_types=initial_type_double, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_double.onnx"
      onnx.save_model(onnx_model_double, onnx_filename)

      print("\n"+model_name+" ONNX model (double)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.
      initial_type_double = X.astype(np.float64)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_double = onnx_session.run([output_name], {input_name: initial_type_double})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_double = r2_score(y, y_pred_onnx_double)
      mse_onnx_double = mean_squared_error(y, y_pred_onnx_double)
      mae_onnx_double = mean_absolute_error(y, y_pred_onnx_double)
      print("R-squared (Coefficient of determination)", r2_onnx_double)
      print("Mean Absolute Error:", mae_onnx_double)
      print("Mean Squared Error:", mse_onnx_double)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_double))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_double))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_double))
      print("double ONNX model precision: ",compare_decimal_places(mae, mae_onnx_double))

      # 그림 크기 설정
      plt.figure(figsize=(8,5))
      # 원본 데이터와 회귀선을 그림으로 표시합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with double ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_double.png')

      출력:

      Python  LinearSVR Original model (double)
      Python  R-squared (Coefficient of determination): 0.9944935515149387
      Python  Mean Absolute Error: 7.026852359381935
      Python  Mean Squared Error: 72.86550241109444
      Python  
      Python  LinearSVR ONNX model (float)
      Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\linear_svr_float.onnx
      Python  Information about input tensors in ONNX:
      Python  1. Name: float_input, Data Type: tensor(float), Shape: [None, 1]
      Python  Information about output tensors in ONNX:
      Python  1. Name: variable, Data Type: tensor(float), Shape: [None, 1]
      Python  R-squared (Coefficient of determination) 0.9944935580726729
      Python  Mean Absolute Error: 7.026849848037511
      Python  Mean Squared Error: 72.86541563418206
      Python  R^2 matching decimal places:  8
      Python  MAE matching decimal places:  4
      Python  MSE matching decimal places:  3
      Python  float ONNX model precision:  4
      Python  
      Python  LinearSVR ONNX model (double)
      Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\linear_svr_double.onnx
      Python  Information about input tensors in ONNX:
      Python  1. Name: double_input, Data Type: tensor(double), Shape: [None, 1]
      Python  Information about output tensors in ONNX:
      Python  1. Name: variable, Data Type: tensor(double), Shape: [None, 1]
      Python  R-squared (Coefficient of determination) 0.9944935515149387
      Python  Mean Absolute Error: 7.026852359381935
      Python  Mean Squared Error: 72.86550241109444
      Python  R^2 matching decimal places:  16
      Python  MAE matching decimal places:  15
      Python  MSE matching decimal places:  14
      Python  double ONNX model precision:  15
      

      Fig.70. LinearSVR.py의 결과(float ONNX)

      그림 70. LinearSVR.py의 결과(float ONNX)


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

      이 코드는 저장된 linear_svr_float.onnx linear_svr_double.onnx를 실행하고 MQL5에서 회귀 메트릭을 사용하는 것을 보여줍니다.

      //+------------------------------------------------------------------+
      //|                                                    LinearSVR.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"
      
      #define   ModelName          "LinearSVR"
      #define   ONNXFilenameFloat  "linear_svr_float.onnx"
      #define   ONNXFilenameDouble "linear_svr_double.onnx"
      
      #resource ONNXFilenameFloat  as const uchar ExtModelFloat[];
      #resource ONNXFilenameDouble as const uchar ExtModelDouble[];
      
      #define   TestFloatModel  1
      #define   TestDoubleModel 2
      
      //+------------------------------------------------------------------+
      //| Calculate regression using float values                          |
      //+------------------------------------------------------------------+
      bool RunModelFloat(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         float input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=(float)input_vector[k];
      //--- prepare output tensor
         float output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      //+------------------------------------------------------------------+
      //| Calculate regression using double values                         |
      //+------------------------------------------------------------------+
      bool RunModelDouble(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         double input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=input_vector[k];
      //--- prepare output tensor
         double output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      
      //+------------------------------------------------------------------+
      //| Generate synthetic data                                          |
      //+------------------------------------------------------------------+
      bool GenerateData(const int n,vector &x,vector &y)
        {
         if(n<=0)
            return(false);
      //--- prepare arrays
         x.Resize(n);
         y.Resize(n);
      //---
         for(int i=0; i<n; i++)
           {
            x[i]=(double)1.0*i;
            y[i]=(double)(4*x[i] + 10*sin(x[i]*0.5));
           }
      //---
         return(true);
        }
      
      //+------------------------------------------------------------------+
      //| TestRegressionModel                                              |
      //+------------------------------------------------------------------+
      bool TestRegressionModel(const string model_name,const int model_type)
        {
      //---
         long  model=INVALID_HANDLE;
         ulong flags=ONNX_DEFAULT;
      
         if(model_type==TestFloatModel)
           {
            PrintFormat("\nTesting ONNX float: %s (%s)",model_name,ONNXFilenameFloat);
            model=OnnxCreateFromBuffer(ExtModelFloat,flags);
           }
         else
            if(model_type==TestDoubleModel)
              {
               PrintFormat("\nTesting ONNX double: %s (%s)",model_name,ONNXFilenameDouble);
               model=OnnxCreateFromBuffer(ExtModelDouble,flags);
              }
            else
              {
               PrintFormat("Model type is not incorrect.");
               return(false);
              }
      //--- check
         if(model==INVALID_HANDLE)
           {
            PrintFormat("model_name=%s OnnxCreate error %d",model_name,GetLastError());
            return(false);
           }
      //---
         vector x_values= {};
         vector y_true= {};
         vector y_predicted= {};
      //---
         int n=100;
         GenerateData(n,x_values,y_true);
      //---
         bool run_result=false;
         if(model_type==TestFloatModel)
           {
            run_result=RunModelFloat(model,x_values,y_predicted);
           }
         else
            if(model_type==TestDoubleModel)
              {
               run_result=RunModelDouble(model,x_values,y_predicted);
              }
      //---
         if(run_result)
           {
            PrintFormat("MQL5:   R-Squared (Coefficient of determination): %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_R2));
            PrintFormat("MQL5:   Mean Absolute Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MAE));
            PrintFormat("MQL5:   Mean Squared Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MSE));
           }
         else
            PrintFormat("Error %d",GetLastError());
      //--- release model
         OnnxRelease(model);
      //---
         return(true);
        }
      //+------------------------------------------------------------------+
      //| Script program start function                                    |
      //+------------------------------------------------------------------+
      int OnStart(void)
        {
      //--- test ONNX regression model for float
         TestRegressionModel(ModelName,TestFloatModel);
      //--- test ONNX regression model for double
         TestRegressionModel(ModelName,TestDoubleModel);
      //---
         return(0);
        }
      //+------------------------------------------------------------------+

      출력:

      LinearSVR (EURUSD,H1)   Testing ONNX float: LinearSVR (linear_svr_float.onnx)
      LinearSVR (EURUSD,H1)   MQL5:   R-Squared (Coefficient of determination): 0.9944935580726729
      LinearSVR (EURUSD,H1)   MQL5:   Mean Absolute Error: 7.0268498480375108
      LinearSVR (EURUSD,H1)   MQL5:   Mean Squared Error: 72.8654156341820567
      LinearSVR (EURUSD,H1)   
      LinearSVR (EURUSD,H1)   Testing ONNX double: LinearSVR (linear_svr_double.onnx)
      LinearSVR (EURUSD,H1)   MQL5:   R-Squared (Coefficient of determination): 0.9944935515149387
      LinearSVR (EURUSD,H1)   MQL5:   Mean Absolute Error: 7.0268523593819374
      LinearSVR (EURUSD,H1)   MQL5:   Mean Squared Error: 72.8655024110944680
      

      파이썬의 원래 배정밀도 모델과 비교:

      Testing ONNX float: LinearSVR (linear_svr_float.onnx)
      Python  Mean Absolute Error: 7.026852359381935
      MQL5:   Mean Absolute Error: 7.0268498480375108
         
      Testing ONNX double: LinearSVR (linear_svr_double.onnx)
      Python  Mean Absolute Error: 7.026852359381935
      MQL5:   Mean Absolute Error: 7.0268523593819374

      ONNX float MAE의 정확도: 소수점 이하 4자리, ONNX double MAE의 정확도: 소수점 이하 14자리.


      2.1.21.3. linear_svr_float.onnx 및 linear_svr_double.onnx의 ONNX 표현


      Fig.71. Netron에서 linear_svr_float.onnx의 ONNX 표현

      그림 71. 네트론에서 linear_svr_float.onnx의 ONNX 표현


      Fig.72. Netron에서 linear_svr_double.onnx의 ONNX 표현

      그림 72. 네트론에서 linear_svr_double.onnx의 ONNX 표현


      2.1.22. sklearn.neural_network.MLPRegressor

      MLPRegressor(다중 계층 퍼셉트론 레귤레이터)는 회귀 작업에 인공 신경망을 활용하는 머신 러닝 모델입니다.

      목표 변수의 연속 값을 예측하도록 학습된 여러 계층의 뉴런(입력, 숨겨진 계층, 출력 계층 포함)으로 구성된 다층 신경망입니다.

      MLPRegressor의 작동 방식:

      1. 데이터 입력: 피처(독립 변수)와 그에 해당하는 목표 변수 값이 포함된 데이터 집합으로 시작합니다.
      2. 다층 신경망 만들기: MLPRegressor는 여러 개의 숨겨진 뉴런 레이어로 구성된 다층 신경망을 사용합니다. 이러한 뉴런은 가중치 연결 및 활성화 함수를 통해 연결됩니다.
      3. 모델 훈련: MLPRegressor는 네트워크의 예측과 실제 목표 변수 값 사이의 차이를 측정하는 손실 함수를 최소화하기 위해 가중치와 편향을 조정하여 신경망을 훈련합니다. 이는 역전파 알고리즘을 통해 이루어집니다.
      4. 예측 생성하기: 학습 후 모델은 새로운 데이터에 대한 목표 변수 값을 예측할 수 있습니다.

      MLPRegressor의 장점:

      • 유연성: 다층 신경망은 피처와 대상 변수 간의 복잡한 비선형 관계를 모델링할 수 있습니다.
      • 다용도성: MLPRegressor는 시계열 문제, 함수 근사치 등 다양한 회귀 작업에 사용될 수 있습니다.
      • 일반화 능력: 신경망은 데이터를 통해 학습하고 학습 데이터에서 발견한 종속성을 새로운 데이터로 일반화할 수 있습니다.

      MLPRegressor의 한계:

      • 기본 모델의 복잡성: 대규모 신경망은 계산 비용이 많이 들고 학습을 위해 방대한 데이터가 필요할 수 있습니다.
      • 하이퍼패러미터 튜닝: 최적의 하이퍼패러미터(레이어 수, 각 레이어의 뉴런 수, 학습 속도 등)를 선택하려면 실험이 필요할 수 있습니다.
      • 과적합에 대한 취약성: 대규모 신경망은 데이터가 충분하지 않거나 정규화가 불충분할 경우 과적합이 발생하기 쉽습니다.

      MLPRegressor는 다층 신경망을 기반으로 하는 강력한 머신러닝 모델로 다양한 회귀 작업에 사용될 수 있습니다. 이 모델은 유연하지만 최적의 결과를 얻으려면 대량의 데이터에 대한 세심한 튜닝과 훈련이 필요합니다.


      2.1.22.1. MLPRegressor 모델을 생성하고 float 및 double용 ONNX로 내보내기 위한 코드

      이 코드는 sklearn.neural_network.MLPRegressor 모델을 생성하고 합성 데이터로 학습시키고 모델을 ONNX 형식으로 저장하고 플로트 및 더블 입력 데이터를 모두 사용하여 예측을 수행합니다. 또한 원본 모델과 ONNX로 내보낸 모델 모두의 정확도를 평가합니다.

      # MLPRegressor.py
      # 이 코드는 MLPRegressor 모델을 학습하고 이를 ONNX 형식(float 및 double 모두)으로 내보내고 ONNX 모델을 사용하여 예측하는 과정을 보여 줍니다.
      # Copyright 2023, MetaQuotes Ltd.
      # https://www.mql5.com


      # 소수점 이하 자릿수를 비교학 위한 함수
      def compare_decimal_places(value1, value2):
      # 두 값을 모두 문자열로 변환
          str_value1 = str(value1)
          str_value2 = str(value2)

      # 문자열에서 소수점 위치 찾기
          dot_position1 = str_value1.find(".")
          dot_position2 = str_value2.find(".")

      # 값 중 소수점이 없는 경우 0을 반환합니다.
          if dot_position1 == -1 or dot_position2 == -1:
              return 0

          # 소수점 이하 자릿수 계산
          decimal_places1 = len(str_value1) - dot_position1 - 1
          decimal_places2 = len(str_value2) - dot_position2 - 1

          # 소수점 이하 두 자리 수 중 최소값 찾기
          min_decimal_places = min(decimal_places1, decimal_places2)

          # 소수점 이하 자릿수가 일치하도록 카운트를 초기화
          matching_count = 0

          # 소수점 이하 문자 비교
          for i in range(1, min_decimal_places + 1):
              if str_value1[dot_position1 + i] == str_value2[dot_position2 + i]:
                  matching_count += 1
              else:
                  break

          return matching_count

      # 필요한 라이브러리 가져오기
      import numpy as np
      import matplotlib.pyplot as plt
      에서 sklearn.neural_network import MLPRegressor
      from sklearn.metrics import r2_score,mean_absolute_error,mean_squared_error
      import onnx
      import onnxruntime as ort
      from skl2onnx import convert_sklearn
      from skl2onnx.common.data_types import FloatTensorType
      from skl2onnx.common.data_types import DoubleTensorType
      from sys import argv

      # 모델 저장 경로를 정의합니다.
      data_path = argv[0]
      last_index = data_path.rfind("\\")+ 1
      data_path = data_path[0:last_index]

      # 회귀를 위한 합성 데이터 생성
      X = np.arange(0,100,1).reshape(-1,1)
      y = 4*X + 10*np.sin(X*0.5)

      모델_이름 = "MLPRegressor"
      onnx_model_filename = data_path + "mlp_regressor"

      # MLP 회귀 모델 만들기
      mlp_regressor_model = MLPRegressor(hidden_layer_sizes=(100, 50), activation='relu', max_iter=1000)

      # 데이터에 모델 맞추기
      mlp_regressor_model.fit(X, y.ravel())

      # 전체 데이터 집합에 대한 값 예측
      y_pred = mlp_regressor_model.predict(X)

      # 모델의 성능 평가
      r2 = r2_score(y, y_pred)
      MSE = MEAN_SQUARE_ERROR(Y, Y_PED)
      MAE = MEAN_ABSOLUTE_ERROR(Y, Y_PED)

      print("\n"+model_name+" Original model (double)")
      print("R-squared (Coefficient of determination):", r2)
      print("Mean Absolute Error:", mae)
      print("Mean Squared Error:", mse)

      # ONNX 모델로 변환 (float)
      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.

      initial_type_float = [('float_input', FloatTensorType([None, X.shape[1]])]]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_float = convert_sklearn(mlp_regressor_model, initial_types=initial_type_float, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_float.onnx"
      onnx.save_model(onnx_model_float, onnx_filename)

      print("\n"+model_name+" ONNX model (float)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.
      initial_type_float = X.astype(np.float32)
      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.
      initial_type_double = X.astype(np.float64)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_float = onnx_session.run([output_name], {input_name: initial_type_float})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_float = r2_score(y, y_pred_onnx_float)
      mse_onnx_float = mean_squared_error(y, y_pred_onnx_float)
      mae_onnx_float = mean_absolute_error(y, y_pred_onnx_float)
      print("R-squared (Coefficient of determination)", r2_onnx_float)
      print("Mean Absolute Error:", mae_onnx_float)
      print("Mean Squared Error:", mse_onnx_float)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_float))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_float))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_float))
      print("float ONNX model precision: ",compare_decimal_places(mae, mae_onnx_float))

      # 그림 크기 설정
      plt.figure(figsize=(8,5))
      # 원본 데이터와 회귀 데이터를 플로팅합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with float ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_float.png')

      # ONNX 모델로 변환 (double)
      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.
      initial_type_double = [('double_input', DoubleTensorType([None, X.shape[1]]))]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_double = convert_sklearn(mlp_regressor_model, initial_types=initial_type_double, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_double.onnx"
      onnx.save_model(onnx_model_double, onnx_filename)

      print("\n"+model_name+" ONNX model (double)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.
      initial_type_double = X.astype(np.float64)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_double = onnx_session.run([output_name], {input_name: initial_type_double})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_double = r2_score(y, y_pred_onnx_double)
      mse_onnx_double = mean_squared_error(y, y_pred_onnx_double)
      mae_onnx_double = mean_absolute_error(y, y_pred_onnx_double)
      print("R-squared (Coefficient of determination)", r2_onnx_double)
      print("Mean Absolute Error:", mae_onnx_double)
      print("Mean Squared Error:", mse_onnx_double)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_double))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_double))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_double))
      print("double ONNX model precision: ",compare_decimal_places(mae, mae_onnx_double))

      # 그림 크기 설정
      plt.figure(figsize=(8,5))
      # 원본 데이터와 회귀 데이터를 플로팅합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with double ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_double.png')

      출력:

      Python  MLPRegressor Original model (double)
      Python  R-squared (Coefficient of determination): 0.9874070836467945
      Python  Mean Absolute Error: 10.62249788982753
      Python  Mean Squared Error: 166.63901957615224
      Python  
      Python  MLPRegressor ONNX model (float)
      Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\mlp_regressor_float.onnx
      Python  Information about input tensors in ONNX:
      Python  1. Name: float_input, Data Type: tensor(float), Shape: [None, 1]
      Python  Information about output tensors in ONNX:
      Python  1. Name: variable, Data Type: tensor(float), Shape: [None, 1]
      Python  R-squared (Coefficient of determination) 0.9874070821340352
      Python  Mean Absolute Error: 10.62249972216809
      Python  Mean Squared Error: 166.63903959413219
      Python  R^2 matching decimal places:  8
      Python  MAE matching decimal places:  5
      Python  MSE matching decimal places:  4
      Python  float ONNX model precision:  5
      Python  
      Python  MLPRegressor ONNX model (double)
      Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\mlp_regressor_double.onnx
      Python  Information about input tensors in ONNX:
      Python  1. Name: double_input, Data Type: tensor(double), Shape: [None, 1]
      Python  Information about output tensors in ONNX:
      Python  1. Name: variable, Data Type: tensor(double), Shape: [None, 1]
      Python  R-squared (Coefficient of determination) 0.9874070836467945
      Python  Mean Absolute Error: 10.622497889827532
      Python  Mean Squared Error: 166.63901957615244
      Python  R^2 matching decimal places:  16
      Python  MAE matching decimal places:  14
      Python  MSE matching decimal places:  12
      Python  double ONNX model precision:  14
      

      그림 73. MLPRegressor.py의 결과(float ONNX)

      그림 73. MLPRegressor.py의 결과(float ONNX)


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

      이 코드는 저장된 mlp_regressor_float.onnxmlp_regressor_double.onnx 모델을 실행하고 MQL5에서 회귀 메트릭을 사용하는 것을 보여줍니다.

      //+------------------------------------------------------------------+
      //|                                                 MLPRegressor.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"
      
      #define   ModelName          "MLPRegressor"
      #define   ONNXFilenameFloat  "mlp_regressor_float.onnx"
      #define   ONNXFilenameDouble "mlp_regressor_double.onnx"
      
      #resource ONNXFilenameFloat  as const uchar ExtModelFloat[];
      #resource ONNXFilenameDouble as const uchar ExtModelDouble[];
      
      #define   TestFloatModel  1
      #define   TestDoubleModel 2
      
      //+------------------------------------------------------------------+
      //| Calculate regression using float values                          |
      //+------------------------------------------------------------------+
      bool RunModelFloat(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         float input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=(float)input_vector[k];
      //--- prepare output tensor
         float output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      //+------------------------------------------------------------------+
      //| Calculate regression using double values                         |
      //+------------------------------------------------------------------+
      bool RunModelDouble(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         double input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=input_vector[k];
      //--- prepare output tensor
         double output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      
      //+------------------------------------------------------------------+
      //| Generate synthetic data                                          |
      //+------------------------------------------------------------------+
      bool GenerateData(const int n,vector &x,vector &y)
        {
         if(n<=0)
            return(false);
      //--- prepare arrays
         x.Resize(n);
         y.Resize(n);
      //---
         for(int i=0; i<n; i++)
           {
            x[i]=(double)1.0*i;
            y[i]=(double)(4*x[i] + 10*sin(x[i]*0.5));
           }
      //---
         return(true);
        }
      
      //+------------------------------------------------------------------+
      //| TestRegressionModel                                              |
      //+------------------------------------------------------------------+
      bool TestRegressionModel(const string model_name,const int model_type)
        {
      //---
         long  model=INVALID_HANDLE;
         ulong flags=ONNX_DEFAULT;
      
         if(model_type==TestFloatModel)
           {
            PrintFormat("\nTesting ONNX float: %s (%s)",model_name,ONNXFilenameFloat);
            model=OnnxCreateFromBuffer(ExtModelFloat,flags);
           }
         else
            if(model_type==TestDoubleModel)
              {
               PrintFormat("\nTesting ONNX double: %s (%s)",model_name,ONNXFilenameDouble);
               model=OnnxCreateFromBuffer(ExtModelDouble,flags);
              }
            else
              {
               PrintFormat("Model type is not incorrect.");
               return(false);
              }
      //--- check
         if(model==INVALID_HANDLE)
           {
            PrintFormat("model_name=%s OnnxCreate error %d",model_name,GetLastError());
            return(false);
           }
      //---
         vector x_values= {};
         vector y_true= {};
         vector y_predicted= {};
      //---
         int n=100;
         GenerateData(n,x_values,y_true);
      //---
         bool run_result=false;
         if(model_type==TestFloatModel)
           {
            run_result=RunModelFloat(model,x_values,y_predicted);
           }
         else
            if(model_type==TestDoubleModel)
              {
               run_result=RunModelDouble(model,x_values,y_predicted);
              }
      //---
         if(run_result)
           {
            PrintFormat("MQL5:   R-Squared (Coefficient of determination): %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_R2));
            PrintFormat("MQL5:   Mean Absolute Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MAE));
            PrintFormat("MQL5:   Mean Squared Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MSE));
           }
         else
            PrintFormat("Error %d",GetLastError());
      //--- release model
         OnnxRelease(model);
      //---
         return(true);
        }
      //+------------------------------------------------------------------+
      //| Script program start function                                    |
      //+------------------------------------------------------------------+
      int OnStart(void)
        {
      //--- test ONNX regression model for float
         TestRegressionModel(ModelName,TestFloatModel);
      //--- test ONNX regression model for double
         TestRegressionModel(ModelName,TestDoubleModel);
      //---
         return(0);
        }
      //+------------------------------------------------------------------+

      출력:

      MLPRegressor (EURUSD,H1)        Testing ONNX float: MLPRegressor (mlp_regressor_float.onnx)
      MLPRegressor (EURUSD,H1)        MQL5:   R-Squared (Coefficient of determination): 0.9875198695654352
      MLPRegressor (EURUSD,H1)        MQL5:   Mean Absolute Error: 10.5596681685341309
      MLPRegressor (EURUSD,H1)        MQL5:   Mean Squared Error: 165.1465507645494597
      MLPRegressor (EURUSD,H1)        
      MLPRegressor (EURUSD,H1)        Testing ONNX double: MLPRegressor (mlp_regressor_double.onnx)
      MLPRegressor (EURUSD,H1)        MQL5:   R-Squared (Coefficient of determination): 0.9875198617341387
      MLPRegressor (EURUSD,H1)        MQL5:   Mean Absolute Error: 10.5596715833884609
      MLPRegressor (EURUSD,H1)        MQL5:   Mean Squared Error: 165.1466543942046599
      

      파이썬의 원래 배정밀도 모델과 비교:

      Testing ONNX float: MLPRegressor (mlp_regressor_float.onnx)
      Python  Mean Absolute Error: 10.62249788982753
      MQL5:   Mean Absolute Error: 10.6224997221680901
      
      Testing ONNX double: MLPRegressor (mlp_regressor_double.onnx)
      Python  Mean Absolute Error: 10.62249788982753
      MQL5:   Mean Absolute Error: 10.6224978898275282

      ONNX float MAE의 정확도: 소수점 이하 5자리, 정확도 ONNX 더블 MAE: 소수점 이하 13자리.


      2.1.22.3. mlp_regressor_float.onnx 및 mlp_regressor_double.onnx의 ONNX 표현


      Fig.74. 네트론에서 mlp_regressor_float.onnx의 ONNX 표현

      그림 74. 네트론에서 mlp_regressor_float.onnx의 ONNX 표현


      Fig.75. 넷트론에서 mlp_regressor_double.onnx의 ONNX 표현

      그림 75. 네트론에서 mlp_regressor_double.onnx의 ONNX 표현



      2.1.23. sklearn.cross_decomposition.PLSRegression

      PLSRegression (Partial Least Squares Regression)는 회귀 문제를 해결하는 데 사용되는 머신 러닝 메서드입니다.

      이 메서드는 PLS 메서드의 일부로 한 세트는 예측 변수 역할을 하고 다른 세트는 대상 변수 역할을 하는 두 변수 세트 간의 관계를 분석하고 모델링하는 데 적용됩니다.

      PLSRegression의 작동 방식:

      1. 데이터 입력: X와 Y로 레이블이 지정된 두 개의 데이터 집합으로 시작합니다. X 집합에는 독립 변수(예측 변수)가, Y 집합에는 대상 변수(종속 변수)가 포함됩니다.
      2. 선형 조합 선택: PLS회귀는 집합 X와 Y에서 이들 사이의 공분산을 최대화하는 선형 조합(구성 요소)을 식별합니다. 이러한 구성 요소를 PLS 구성 요소라고 합니다.
      3. 공분산 최대화: 이를 통해 예측 변수와 목표 변수 간의 가장 유익한 관계를 추출할 수 있습니다. PLS회귀의 주요 목적은 X와 Y 간의 공분산을 최대화하는 PLS 구성 요소를 찾는 것입니다.
      4. 모델 훈련: PLS 구성 요소가 찾아지면 이를 사용하여 X를 기반으로 Y 값을 예측하는 모델을 만들 수 있습니다.
      5. 예측 생성하기: 학습 후에는 해당 X 값을 사용하여 새로운 데이터의 Y 값을 예측하는 데 모델에 사용될 수 있습니다.

      PLS회귀의 장점:

      • 상관관계 분석: PLS회귀를 사용하면 두 변수 세트 간의 상관관계를 분석하고 모델링할 수 있으므로 예측 변수와 대상 변수 간의 관계를 이해하는 데 유용할 수 있습니다.
      • 차원 축소: 이 메서드는 가장 중요한 PLS 구성 요소를 식별하여 데이터의 차원을 줄이는 데에도 사용할 수 있습니다.

      PLS회귀의 한계:

      • 구성 요소 수 선택에 대한 민감도: 최적의 PLS 성분 수를 선택하려면 약간의 실험이 필요할 수 있습니다.
      • 데이터 구조에 대한 종속성: PLS회귀 결과는 데이터의 구조와 데이터 간의 상관관계에 따라 크게 달라질 수 있습니다.

      PLS회귀는 두 변수 세트 간의 상관관계를 분석하고 모델링하는 데 사용되는 머신러닝 메서드로 한 세트는 예측 변수 역할을 하고 다른 세트는 대상 변수 역할을 합니다. 이 메서드를 사용하면 데이터 내의 관계를 연구할 수 있으며 데이터 차원을 줄이고 예측 변수를 기반으로 목표 변수 값을 예측하는 데 유용할 수 있습니다.


      2.1.23.1. float 및 double 용 PLSRegression 모델을 생성하고 이를 ONNX로 내보내기 위한 코드

      이 코드는 sklearn.cross_decomposition.PLSRegression 모델을 생성하고, 합성 데이터로 학습시키고 모델을 ONNX 형식으로 저장하고 플로트 및 더블 입력 데이터를 모두 사용하여 예측을 수행합니다. 또한 원본 모델과 ONNX로 내보낸 모델 모두의 정확도를 평가합니다.

      # PLSRegression.py
      # 이 코드는 PLSRegression 모델을 학습하고 이를 ONNX 형식(float 및 double 모두)으로 내보내고 ONNX 모델을 사용하여 예측하는 과정을 보여 줍니다.
      # Copyright 2023, MetaQuotes Ltd.
      # https://www.mql5.com


      # 소수점 이하 자릿수를 비교학 위한 함수
      def compare_decimal_places(value1, value2):
      # 두 값을 모두 문자열로 변환
          str_value1 = str(value1)
          str_value2 = str(value2)

      # 문자열에서 소수점 위치 찾기
          dot_position1 = str_value1.find(".")
          dot_position2 = str_value2.find(".")

      # 값 중 소수점이 없는 경우 0을 반환합니다.
          if dot_position1 == -1 or dot_position2 == -1:
              return 0

          # 소수점 이하 자릿수 계산
          decimal_places1 = len(str_value1) - dot_position1 - 1
          decimal_places2 = len(str_value2) - dot_position2 - 1

          # 소수점 이하 두 자리 수 중 최소값 찾기
          min_decimal_places = min(decimal_places1, decimal_places2)

          # 소수점 이하 자릿수가 일치하도록 카운트를 초기화합니다.

          matching_count = 0

          # 소수점 이하 문자 비교
          for i in range(1, min_decimal_places + 1):
              if str_value1[dot_position1 + i] == str_value2[dot_position2 + i]:
                  matching_count += 1
              else:
                  break

          return matching_count

      # 필요한 라이브러리 가져오기
      import numpy as np
      import matplotlib.pyplot as plt
      from sklearn.cross_decomposition import PLSRegression
      from sklearn.metrics import r2_score,mean_absolute_error,mean_squared_error
      import onnx
      import onnxruntime as ort
      from skl2onnx import convert_sklearn
      from skl2onnx.common.data_types import FloatTensorType
      from skl2onnx.common.data_types import DoubleTensorType
      from sys import argv

      # 모델 저장 경로를 정의합니다.
      data_path = argv[0]
      last_index = data_path.rfind("\\")+ 1
      data_path = data_path[0:last_index]

      # 회귀를 위한 합성 데이터 생성
      X = np.arange(0,100,1).reshape(-1,1)
      y = 4*X + 10*np.sin(X*0.5)

      model_name = "PLSRegression"
      onnx_model_filename = data_path + "pls_regression"

      # PLSRegression 모델 만들기
      pls_model = PLSRegression(n_components=1)

      # 데이터에 모델 맞추기
      pls_model.fit(X, y)

      # 전체 데이터 집합에 대한 값 예측
      y_pred = pls_model.predict(X)

      # 모델의 성능 평가
      r2 = r2_score(y, y_pred)
      MSE = MEAN_SQUARE_ERROR(Y, Y_PED)
      MAE = MEAN_ABSOLUTE_ERROR(Y, Y_PED)

      print("\n"+model_name+" Original model (double)")
      print("R-squared (Coefficient of determination):", r2)
      print("Mean Absolute Error:", mae)
      print("Mean Squared Error:", mse)

      # ONNX 모델로 변환 (float)
      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.

      initial_type_float = [('float_input', FloatTensorType([None, X.shape[1]])]]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_float = convert_sklearn(pls_model, initial_types=initial_type_float, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_float.onnx"
      onnx.save_model(onnx_model_float, onnx_filename)

      print("\n"+model_name+" ONNX model (float)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.
      initial_type_float = X.astype(np.float32)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_float = onnx_session.run([output_name], {input_name: initial_type_float})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_float = r2_score(y, y_pred_onnx_float)
      mse_onnx_float = mean_squared_error(y, y_pred_onnx_float)
      mae_onnx_float = mean_absolute_error(y, y_pred_onnx_float)
      print("R-squared (Coefficient of determination)", r2_onnx_float)
      print("Mean Absolute Error:", mae_onnx_float)
      print("Mean Squared Error:", mse_onnx_float)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_float))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_float))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_float))
      print("float ONNX model precision: ",compare_decimal_places(mae, mae_onnx_float))

      # 그림 크기 설정
      plt.figure(figsize=(8,5))
      # 원본 데이터와 회귀선을 그림으로 표시합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with float ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_float.png')

      # ONNX 모델로 변환 (double)
      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.

      initial_type_double = [('double_input', DoubleTensorType([None, X.shape[1]]))]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_double = convert_sklearn(pls_model, initial_types=initial_type_double, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_double.onnx"
      onnx.save_model(onnx_model_double, onnx_filename)

      print("\n"+model_name+" ONNX model (double)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.
      initial_type_double = X.astype(np.float64)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_double = onnx_session.run([output_name], {input_name: initial_type_double})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_double = r2_score(y, y_pred_onnx_double)
      mse_onnx_double = mean_squared_error(y, y_pred_onnx_double)
      mae_onnx_double = mean_absolute_error(y, y_pred_onnx_double)
      print("R-squared (Coefficient of determination)", r2_onnx_double)
      print("Mean Absolute Error:", mae_onnx_double)
      print("Mean Squared Error:", mse_onnx_double)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_double))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_double))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_double))
      print("double ONNX model precision: ",compare_decimal_places(mae, mae_onnx_double))

      # 그림 크기 설정
      plt.figure(figsize=(8,5))
      # 원본 데이터와 회귀선을 그림으로 표시합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with double ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_double.png')

      출력:

      Python  PLSRegression Original model (double)
      Python  R-squared (Coefficient of determination): 0.9962382642613388
      Python  Mean Absolute Error: 6.3477379263364275
      Python  Mean Squared Error: 49.778140171281805
      Python  
      Python  PLSRegression ONNX model (float)
      Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\pls_regression_float.onnx
      Python  Information about input tensors in ONNX:
      Python  1. Name: float_input, Data Type: tensor(float), Shape: [None, 1]
      Python  Information about output tensors in ONNX:
      Python  1. Name: variable, Data Type: tensor(float), Shape: [None, 1]
      Python  R-squared (Coefficient of determination) 0.9962382638567003
      Python  Mean Absolute Error: 6.3477379221400145
      Python  Mean Squared Error: 49.778145525764096
      Python  R^2 matching decimal places:  8
      Python  MAE matching decimal places:  8
      Python  MSE matching decimal places:  5
      Python  float ONNX model precision:  8
      Python  
      Python  PLSRegression ONNX model (double)
      Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\pls_regression_double.onnx
      Python  Information about input tensors in ONNX:
      Python  1. Name: double_input, Data Type: tensor(double), Shape: [None, 1]
      Python  Information about output tensors in ONNX:
      Python  1. Name: variable, Data Type: tensor(double), Shape: [None, 1]
      Python  R-squared (Coefficient of determination) 0.9962382642613388
      Python  Mean Absolute Error: 6.3477379263364275
      Python  Mean Squared Error: 49.778140171281805
      Python  R^2 matching decimal places:  16
      Python  MAE matching decimal places:  16
      Python  MSE matching decimal places:  15
      Python  double ONNX model precision:  16
      

      Fig.76. PLSRegression.py의 결과(float ONNX)

      그림 76. PLSRegression.py의 결과(float ONNX)


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

      이 코드는 저장된 pls_regression_float.onnxpls_regression_double.onnx 모델을 실행하고 MQL5에서 회귀 메트릭을 사용하는 것을 보여 줍니다.

      //+------------------------------------------------------------------+
      //|                                                PLSRegression.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"
      
      #define   ModelName          "PLSRegression"
      #define   ONNXFilenameFloat  "pls_regression_float.onnx"
      #define   ONNXFilenameDouble "pls_regression_double.onnx"
      
      #resource ONNXFilenameFloat  as const uchar ExtModelFloat[];
      #resource ONNXFilenameDouble as const uchar ExtModelDouble[];
      
      #define   TestFloatModel  1
      #define   TestDoubleModel 2
      
      //+------------------------------------------------------------------+
      //| Calculate regression using float values                          |
      //+------------------------------------------------------------------+
      bool RunModelFloat(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         float input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=(float)input_vector[k];
      //--- prepare output tensor
         float output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      //+------------------------------------------------------------------+
      //| Calculate regression using double values                         |
      //+------------------------------------------------------------------+
      bool RunModelDouble(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         double input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=input_vector[k];
      //--- prepare output tensor
         double output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      
      //+------------------------------------------------------------------+
      //| Generate synthetic data                                          |
      //+------------------------------------------------------------------+
      bool GenerateData(const int n,vector &x,vector &y)
        {
         if(n<=0)
            return(false);
      //--- prepare arrays
         x.Resize(n);
         y.Resize(n);
      //---
         for(int i=0; i<n; i++)
           {
            x[i]=(double)1.0*i;
            y[i]=(double)(4*x[i] + 10*sin(x[i]*0.5));
           }
      //---
         return(true);
        }
      
      //+------------------------------------------------------------------+
      //| TestRegressionModel                                              |
      //+------------------------------------------------------------------+
      bool TestRegressionModel(const string model_name,const int model_type)
        {
      //---
         long  model=INVALID_HANDLE;
         ulong flags=ONNX_DEFAULT;
      
         if(model_type==TestFloatModel)
           {
            PrintFormat("\nTesting ONNX float: %s (%s)",model_name,ONNXFilenameFloat);
            model=OnnxCreateFromBuffer(ExtModelFloat,flags);
           }
         else
            if(model_type==TestDoubleModel)
              {
               PrintFormat("\nTesting ONNX double: %s (%s)",model_name,ONNXFilenameDouble);
               model=OnnxCreateFromBuffer(ExtModelDouble,flags);
              }
            else
              {
               PrintFormat("Model type is not incorrect.");
               return(false);
              }
      //--- check
         if(model==INVALID_HANDLE)
           {
            PrintFormat("model_name=%s OnnxCreate error %d",model_name,GetLastError());
            return(false);
           }
      //---
         vector x_values= {};
         vector y_true= {};
         vector y_predicted= {};
      //---
         int n=100;
         GenerateData(n,x_values,y_true);
      //---
         bool run_result=false;
         if(model_type==TestFloatModel)
           {
            run_result=RunModelFloat(model,x_values,y_predicted);
           }
         else
            if(model_type==TestDoubleModel)
              {
               run_result=RunModelDouble(model,x_values,y_predicted);
              }
      //---
         if(run_result)
           {
            PrintFormat("MQL5:   R-Squared (Coefficient of determination): %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_R2));
            PrintFormat("MQL5:   Mean Absolute Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MAE));
            PrintFormat("MQL5:   Mean Squared Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MSE));
           }
         else
            PrintFormat("Error %d",GetLastError());
      //--- release model
         OnnxRelease(model);
      //---
         return(true);
        }
      //+------------------------------------------------------------------+
      //| Script program start function                                    |
      //+------------------------------------------------------------------+
      int OnStart(void)
        {
      //--- test ONNX regression model for float
         TestRegressionModel(ModelName,TestFloatModel);
      //--- test ONNX regression model for double
         TestRegressionModel(ModelName,TestDoubleModel);
      //---
         return(0);
        }
      //+------------------------------------------------------------------+

      출력:

      PLSRegression (EURUSD,H1)       Testing ONNX float: PLSRegression (pls_regression_float.onnx)
      PLSRegression (EURUSD,H1)       MQL5:   R-Squared (Coefficient of determination): 0.9962382638567003
      PLSRegression (EURUSD,H1)       MQL5:   Mean Absolute Error: 6.3477379221400145
      PLSRegression (EURUSD,H1)       MQL5:   Mean Squared Error: 49.7781455257640815
      PLSRegression (EURUSD,H1)       
      PLSRegression (EURUSD,H1)       Testing ONNX double: PLSRegression (pls_regression_double.onnx)
      PLSRegression (EURUSD,H1)       MQL5:   R-Squared (Coefficient of determination): 0.9962382642613388
      PLSRegression (EURUSD,H1)       MQL5:   Mean Absolute Error: 6.3477379263364275
      PLSRegression (EURUSD,H1)       MQL5:   Mean Squared Error: 49.7781401712817839
      

      파이썬의 원래 배정밀도 모델과 비교:

      Testing ONNX float: PLSRegression (pls_regression_float.onnx)
      Python  Mean Absolute Error: 6.3477379263364275
      MQL5:   Mean Absolute Error: 6.3477379221400145
             
      Testing ONNX double: PLSRegression (pls_regression_double.onnx)
      Python  Mean Absolute Error: 6.3477379263364275
      MQL5:   Mean Absolute Error: 6.3477379263364275

      ONNX float MAE의 정확도: 소수점 이하 8자리, 정확도 ONNX 더블 MAE: 소수점 이하 16자리.


      2.1.23.3. pls_regression_float.onnx 및 pls_regression_double.onnx의 ONNX 표현


      Fig.77. Netron에서 pls_regression_float.onnx의 ONNX 표현

      그림 77. 네트론에서 pls_regression_float.onnx의 ONNX 표현


      Fig.78. Netron에서 pls_regression_double.onnx의 ONNX 표현

      그림 78. 네트론에서 pls_regression_double.onnx의 ONNX 표현



      2.1.24. sklearn.linear_model.TweedieRegressor

      TweedieRegressor는 트위디 분포를 사용하여 회귀 문제를 해결하기 위해 고안된 회귀 메서드입니다. 트위디 분포는 다양한 분산 구조를 가진 데이터를 포함하여 광범위한 데이터를 설명할 수 있는 확률 분포입니다. TweedieRegressor는 대상 변수가 트위디 분포와 일치하는 특성을 가진 회귀 작업에 적용됩니다.

      TweedieRegressor의 작동 방식:

      1. 타겟 변수 및 트위디 배포: TweedieRegressor는 대상 변수가 트위디 분포를 따른다고 가정합니다. 트위디 분포는 분포의 모양과 분산 정도를 결정하는 매개변수 'p'에 따라 달라집니다.
      2. 모델 훈련: TweedieRegressor는 독립 변수(피처)를 기반으로 목표 변수를 예측하기 위해 회귀 모델을 학습합니다. 이 모델은 트위디 분포에 해당하는 데이터의 가능성을 최대화합니다.
      3. 'p' 매개변수 선택: 'p' 매개변수를 선택하는 것은 TweedieRegressor를 사용할 때 매우 중요한 부분입니다. 이 매개변수는 분포의 모양과 분산을 정의합니다. 예를 들어 p=1은 푸아송 분포에 해당하고 p=2는 정규 분포에 해당하며 서로 다른 'p' 값은 서로 다른 유형의 데이터에 해당합니다.
      4. 혁신적인 응답: 때로는 모델에 훈련 전에 응답(목표 변수)의 변환이 필요할 수 있습니다. 이 변환은 'p' 매개변수와 관련이 있으며 트위디 분포를 따르기 위해 로그 함수 또는 기타 변환을 포함할 수 있습니다.

      TweedieRegressor의 장점:

      • 다양한 분산이 있는 데이터를 모델링할 수 있습니다: 트위디 분포는 다양한 분산 구조를 가진 데이터에 적응할 수 있어 분산이 다양할 수 있는 실제 데이터에 유용합니다.
      • 다양한 'p' 매개변수: 다양한 'p' 값을 선택할 수 있는 기능을 통해 다양한 데이터 유형을 모델링할 수 있습니다.

      TweedieRegressor의 한계:

      • 'p' 매개변수 선택의 복잡성: 올바른 'p' 값을 선택하려면 데이터에 대한 지식과 실험이 필요할 수 있습니다.
      • 트위디 배포를 준수합니다: TweedieRegressor를 성공적으로 적용하려면 대상 변수가 트위디 분포와 일치해야 합니다. 규정을 준수하지 않으면 모델 성능이 저하될 수 있습니다.

      TweedieRegressor는 트위디 분포를 사용하여 다양한 분산 구조를 가진 데이터를 모델링하는 회귀 메서드입니다. 이 메서드는 대상 변수가 트위디 분포를 따르는 회귀 작업에 유용하며 더 나은 데이터 적응을 위해 다양한 'p' 매개변수 값으로 조정할 수 있습니다.


      2.1.24.1. TweedieRegressor 모델을 생성하고 float 및 double용 ONNX로 내보내기 위한 코드

      이 코드는 sklearn.linear_model.TweedieRegressor 모델을 생성하고 합성 데이터로 학습시키고 모델을 ONNX 형식으로 저장하고 플로트 및 더블 입력 데이터를 모두 사용하여 예측을 수행합니다. 또한 원본 모델과 ONNX로 내보낸 모델 모두의 정확도를 평가합니다.

      #TweedieRegressor.py
      # 이 코드는 TweedieRegressor 모델을 학습하고, ONNX 형식(float 및 double 모두)으로 내보내고 ONNX 모델을 사용하여 예측하는 과정을 보여줍니다.
      # Copyright 2023, MetaQuotes Ltd.
      # https://www.mql5.com


      # 소수점 이하 자릿수를 비교학 위한 함수
      def compare_decimal_places(value1, value2):
      # 두 값을 모두 문자열로 변환
          str_value1 = str(value1)
          str_value2 = str(value2)

      # 문자열에서 소수점 위치 찾기
          dot_position1 = str_value1.find(".")
          dot_position2 = str_value2.find(".")

      # 값 중 소수점이 없는 경우 0을 반환합니다.
          dot_position1 == -1 또는 dot_position2 ==-1:
              return 0

          # 소수점 이하 자릿수 계산
          decimal_places1 = len(str_value1) - dot_position1 - 1
          decimal_places2 = len(str_value2) - dot_position2 - 1

          # 소수점 이하 두 자리 수 중 최소값 찾기
          min_decimal_places = min(decimal_places1, decimal_places2)

          # 소수점 이하 자릿수가 일치하도록 카운트를 초기화
          matching_count = 0

          # 소수점 이하 문자 비교
          for i in range(1, min_decimal_places + 1):
              if str_value1[dot_position1 + i] == str_value2[dot_position2 + i]:
                  matching_count += 1
              else:
                  break

          return matching_count

      # 필요한 라이브러리 가져오기
      import numpy as np
      import matplotlib.pyplot as plt
      에서 sklearn.linear_model import TweedieRegressor
      from sklearn.metrics import r2_score,mean_absolute_error,mean_squared_error
      import onnx
      import onnxruntime as ort
      from skl2onnx import convert_sklearn
      from skl2onnx.common.data_types import FloatTensorType
      from skl2onnx.common.data_types import DoubleTensorType
      from sys import argv

      # 모델 저장 경로를 정의합니다.
      data_path = argv[0]
      last_index = data_path.rfind("\\")+ 1
      data_path = data_path[0:last_index]

      # 회귀를 위한 합성 데이터 생성
      X = np.arange(0,100,1).reshape(-1,1)
      y = 4*X + 10*np.sin(X*0.5)

      model_name = "TweedieRegressor"
      onnx_model_filename = data_path + "tweedie_regressor"

      # 트위디 회귀 모델 만들기
      regression_model = TweedieRegressor()

      # 데이터에 모델 맞추기
      regression_model.fit(X, y.ravel())

      # 전체 데이터 집합에 대한 값 예측
      y_pred = regression_model.predict(X)

      # 모델의 성능 평가
      r2 = r2_score(y, y_pred)
      MSE = MEAN_SQUARE_ERROR(Y, Y_PED)
      MAE = MEAN_ABSOLUTE_ERROR(Y, Y_PED)

      print("\n"+model_name+" Original model (double)")
      print("R-squared (Coefficient of determination):", r2)
      print("Mean Absolute Error:", mae)
      print("Mean Squared Error:", mse)

      # ONNX 모델로 변환 (float)
      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.

      initial_type_float = [('float_input', FloatTensorType([None, X.shape[1]])]]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_float = convert_sklearn(regression_model, initial_types=initial_type_float, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_float.onnx"
      onnx.save_model(onnx_model_float, onnx_filename)

      print("\n"+model_name+" ONNX model (float)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.
      initial_type_float = X.astype(np.float32)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_float = onnx_session.run([output_name], {input_name: initial_type_float})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_float = r2_score(y, y_pred_onnx_float)
      mse_onnx_float = mean_squared_error(y, y_pred_onnx_float)
      mae_onnx_float = mean_absolute_error(y, y_pred_onnx_float)
      print("R-squared (Coefficient of determination)", r2_onnx_float)
      print("Mean Absolute Error:", mae_onnx_float)
      print("Mean Squared Error:", mse_onnx_float)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_float))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_float))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_float))
      print("float ONNX model precision: ",compare_decimal_places(mae, mae_onnx_float))

      # 그림 크기 설정
      plt.figure(figsize=(8,5))
      # 원본 데이터와 회귀선을 그림으로 표시합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with float ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_float.png')

      # ONNX 모델로 변환 (double)
      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.

      initial_type_double = [('double_input', DoubleTensorType([None, X.shape[1]]))]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_double = convert_sklearn(regression_model, initial_types=initial_type_double, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_double.onnx"
      onnx.save_model(onnx_model_double, onnx_filename)

      print("\n"+model_name+" ONNX model (double)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.
      initial_type_double = X.astype(np.float64)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_double = onnx_session.run([output_name], {input_name: initial_type_double})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_double = r2_score(y, y_pred_onnx_double)
      mse_onnx_double = mean_squared_error(y, y_pred_onnx_double)
      mae_onnx_double = mean_absolute_error(y, y_pred_onnx_double)
      print("R-squared (Coefficient of determination)", r2_onnx_double)
      print("Mean Absolute Error:", mae_onnx_double)
      print("Mean Squared Error:", mse_onnx_double)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_double))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_double))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_double))
      print("double ONNX model precision: ",compare_decimal_places(mae, mae_onnx_double))

      # 그림 크기 설정
      plt.figure(figsize=(8,5))
      # 원본 데이터와 회귀선을 그림으로 표시합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with double ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_double.png')

      출력:

      2023.10.31 11:39:36.223 Python  TweedieRegressor Original model (double)
      2023.10.31 11:39:36.223 Python  R-squared (Coefficient of determination): 0.9962368328117072
      2023.10.31 11:39:36.223 Python  Mean Absolute Error: 6.342397897667562
      2023.10.31 11:39:36.223 Python  Mean Squared Error: 49.797082198408745
      2023.10.31 11:39:36.223 Python  
      2023.10.31 11:39:36.223 Python  TweedieRegressor ONNX model (float)
      2023.10.31 11:39:36.223 Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\tweedie_regressor_float.onnx
      2023.10.31 11:39:36.253 Python  Information about input tensors in ONNX:
      2023.10.31 11:39:36.253 Python  1. Name: float_input, Data Type: tensor(float), Shape: [None, 1]
      2023.10.31 11:39:36.253 Python  Information about output tensors in ONNX:
      2023.10.31 11:39:36.253 Python  1. Name: variable, Data Type: tensor(float), Shape: [None, 1]
      2023.10.31 11:39:36.253 Python  R-squared (Coefficient of determination) 0.9962368338709323
      2023.10.31 11:39:36.253 Python  Mean Absolute Error: 6.342397072978867
      2023.10.31 11:39:36.253 Python  Mean Squared Error: 49.797068181938165
      2023.10.31 11:39:36.253 Python  R^2 matching decimal places:  8
      2023.10.31 11:39:36.253 Python  MAE matching decimal places:  6
      2023.10.31 11:39:36.253 Python  MSE matching decimal places:  4
      2023.10.31 11:39:36.253 Python  float ONNX model precision:  6
      2023.10.31 11:39:36.613 Python  
      2023.10.31 11:39:36.613 Python  TweedieRegressor ONNX model (double)
      2023.10.31 11:39:36.613 Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\tweedie_regressor_double.onnx
      2023.10.31 11:39:36.613 Python  Information about input tensors in ONNX:
      2023.10.31 11:39:36.613 Python  1. Name: double_input, Data Type: tensor(double), Shape: [None, 1]
      2023.10.31 11:39:36.613 Python  Information about output tensors in ONNX:
      2023.10.31 11:39:36.628 Python  1. Name: variable, Data Type: tensor(double), Shape: [None, 1]
      2023.10.31 11:39:36.628 Python  R-squared (Coefficient of determination) 0.9962368328117072
      2023.10.31 11:39:36.628 Python  Mean Absolute Error: 6.342397897667562
      2023.10.31 11:39:36.628 Python  Mean Squared Error: 49.797082198408745
      2023.10.31 11:39:36.628 Python  R^2 matching decimal places:  16
      2023.10.31 11:39:36.628 Python  MAE matching decimal places:  15
      2023.10.31 11:39:36.628 Python  MSE matching decimal places:  15
      2023.10.31 11:39:36.628 Python  double ONNX model precision:  15
      

      Fig.79. TweedieRegressor.py 결과(float ONNX)

      그림 79. TweedieRegressor.py의 결과(float ONNX)


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

      이 코드는 저장된 tweedie_regressor_float.onnxtweedie_regressor_double.onnx 모델을 실행하고 MQL5에서 회귀 메트릭을 사용하는 것을 보여 줍니다.

      //+------------------------------------------------------------------+
      //|                                             TweedieRegressor.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"
      
      #define   ModelName          "TweedieRegressor"
      #define   ONNXFilenameFloat  "tweedie_regressor_float.onnx"
      #define   ONNXFilenameDouble "tweedie_regressor_double.onnx"
      
      #resource ONNXFilenameFloat  as const uchar ExtModelFloat[];
      #resource ONNXFilenameDouble as const uchar ExtModelDouble[];
      
      #define   TestFloatModel  1
      #define   TestDoubleModel 2
      
      //+------------------------------------------------------------------+
      //| Calculate regression using float values                          |
      //+------------------------------------------------------------------+
      bool RunModelFloat(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         float input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=(float)input_vector[k];
      //--- prepare output tensor
         float output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      //+------------------------------------------------------------------+
      //| Calculate regression using double values                         |
      //+------------------------------------------------------------------+
      bool RunModelDouble(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         double input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=input_vector[k];
      //--- prepare output tensor
         double output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      
      //+------------------------------------------------------------------+
      //| Generate synthetic data                                          |
      //+------------------------------------------------------------------+
      bool GenerateData(const int n,vector &x,vector &y)
        {
         if(n<=0)
            return(false);
      //--- prepare arrays
         x.Resize(n);
         y.Resize(n);
      //---
         for(int i=0; i<n; i++)
           {
            x[i]=(double)1.0*i;
            y[i]=(double)(4*x[i] + 10*sin(x[i]*0.5));
           }
      //---
         return(true);
        }
      
      //+------------------------------------------------------------------+
      //| TestRegressionModel                                              |
      //+------------------------------------------------------------------+
      bool TestRegressionModel(const string model_name,const int model_type)
        {
      //---
         long  model=INVALID_HANDLE;
         ulong flags=ONNX_DEFAULT;
      
         if(model_type==TestFloatModel)
           {
            PrintFormat("\nTesting ONNX float: %s (%s)",model_name,ONNXFilenameFloat);
            model=OnnxCreateFromBuffer(ExtModelFloat,flags);
           }
         else
            if(model_type==TestDoubleModel)
              {
               PrintFormat("\nTesting ONNX double: %s (%s)",model_name,ONNXFilenameDouble);
               model=OnnxCreateFromBuffer(ExtModelDouble,flags);
              }
            else
              {
               PrintFormat("Model type is not incorrect.");
               return(false);
              }
      //--- check
         if(model==INVALID_HANDLE)
           {
            PrintFormat("model_name=%s OnnxCreate error %d",model_name,GetLastError());
            return(false);
           }
      //---
         vector x_values= {};
         vector y_true= {};
         vector y_predicted= {};
      //---
         int n=100;
         GenerateData(n,x_values,y_true);
      //---
         bool run_result=false;
         if(model_type==TestFloatModel)
           {
            run_result=RunModelFloat(model,x_values,y_predicted);
           }
         else
            if(model_type==TestDoubleModel)
              {
               run_result=RunModelDouble(model,x_values,y_predicted);
              }
      //---
         if(run_result)
           {
            PrintFormat("MQL5:   R-Squared (Coefficient of determination): %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_R2));
            PrintFormat("MQL5:   Mean Absolute Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MAE));
            PrintFormat("MQL5:   Mean Squared Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MSE));
           }
         else
            PrintFormat("Error %d",GetLastError());
      //--- release model
         OnnxRelease(model);
      //---
         return(true);
        }
      //+------------------------------------------------------------------+
      //| Script program start function                                    |
      //+------------------------------------------------------------------+
      int OnStart(void)
        {
      //--- test ONNX regression model for float
         TestRegressionModel(ModelName,TestFloatModel);
      //--- test ONNX regression model for double
         TestRegressionModel(ModelName,TestDoubleModel);
      //---
         return(0);
        }
      //+------------------------------------------------------------------+

      출력:

      2023.10.31 11:42:20.113 TweedieRegressor (EURUSD,H1)    Testing ONNX float: TweedieRegressor (tweedie_regressor_float.onnx)
      2023.10.31 11:42:20.119 TweedieRegressor (EURUSD,H1)    MQL5:   R-Squared (Coefficient of determination): 0.9962368338709323
      2023.10.31 11:42:20.119 TweedieRegressor (EURUSD,H1)    MQL5:   Mean Absolute Error: 6.3423970729788666
      2023.10.31 11:42:20.119 TweedieRegressor (EURUSD,H1)    MQL5:   Mean Squared Error: 49.7970681819381653
      2023.10.31 11:42:20.125 TweedieRegressor (EURUSD,H1)    
      2023.10.31 11:42:20.125 TweedieRegressor (EURUSD,H1)    Testing ONNX double: TweedieRegressor (tweedie_regressor_double.onnx)
      2023.10.31 11:42:20.130 TweedieRegressor (EURUSD,H1)    MQL5:   R-Squared (Coefficient of determination): 0.9962368328117072
      2023.10.31 11:42:20.130 TweedieRegressor (EURUSD,H1)    MQL5:   Mean Absolute Error: 6.3423978976675608
      2023.10.31 11:42:20.130 TweedieRegressor (EURUSD,H1)    MQL5:   Mean Squared Error: 49.7970821984087593
      

      파이썬의 원래 배정밀도 모델과 비교:

      Testing ONNX float: TweedieRegressor (tweedie_regressor_float.onnx)
      Python  Mean Absolute Error: 6.342397897667562
      MQL5:   Mean Absolute Error: 6.3423970729788666
      
      Testing ONNX double: TweedieRegressor (tweedie_regressor_double.onnx)
      Python  Mean Absolute Error: 6.342397897667562
      MQL5:   Mean Absolute Error: 6.3423978976675608
      

      ONNX float MAE의 정확도: 소수점 이하 6자리, 정확도 ONNX double MAE: 소수점 이하 14자리.


      2.1.24.3. tweedie_regressor_float.onnx 및 tweedie_regressor_double.onnx의 ONNX 표현


      그림 80. 넷트론에서 tweedie_regressor_float.onnx의 ONNX 표현

      그림 80. 네트론에서 tweedie_regressor_float.onnx의 ONNX 표현


      Fig.81. 네트론에서 tweedie_regressor_double.onnx의 ONNX 표현

      그림 81. 네트론에서 tweedie_regressor_double.onnx의 ONNX 표현



      2.1.25. sklearn.linear_model.PoissonRegressor

      PoissonRegressor는 푸아송 분포를 기반으로 회귀 작업을 해결하는 데 적용되는 머신러닝 메서드입니다.

      이 메서드는 종속 변수(대상 변수)가 정해진 기간 또는 정해진 공간 간격 내에 발생한 이벤트 수를 나타내는 카운트 데이터일 때 적합합니다. PoissonRegressor는 예측 변수(독립 변수)와 목표 변수 간의 관계가 푸아송 분포를 따른다고 가정하여 관계를 모델링합니다.

      PoissonRegressor의 작동 방식:

      1. 데이터 입력: 피처(독립 변수)와 이벤트 수를 나타내는 대상 변수가 포함된 데이터 세트부터 시작합니다.
      2. 푸아송 분포: PoissonRegressor 메서듣는 대상 변수가 푸아송 분포를 따른다고 가정하여 모델링합니다. 푸아송 분포는 주어진 시간 간격 또는 공간 범위 내에서 고정된 평균 강도로 발생하는 이벤트를 모델링하는 데 적합합니다.
      3. 모델 훈련: PoissonRegressor는 예측 변수를 고려하여 포아송 분포의 파라미터를 추정하는 모델을 학습합니다. 모델은 푸아송 분포에 해당하는 확률 함수를 사용하여 관측된 데이터에 가장 잘 맞는 모델을 찾으려고 시도합니다.
      4. 카운트 값 예측: 학습 후에는 모델을 사용하여 새로운 데이터의 카운트 값(이벤트 수)을 예측할 수 있으며 이러한 예측 역시 푸아송 분포를 따릅니다.

      PoissonRegressor의 장점:

      • 카운트 데이터에 적합합니다: PoissonRegressor는 대상 변수가 주문 수, 통화 수 등과 같이 카운트 데이터를 나타내는 작업에 적합합니다.
      • 분포의 특이성: 이 모델은 푸아송 분포를 따르기 때문에 이 분포로 잘 설명되는 데이터의 경우 더 정확할 수 있습니다.

      PoissonRegressor의 한계:

      • 카운트 데이터에만 적합합니다: PoissonRegressor는 대상 변수가 연속적이고 카운트가 없는 회귀 분석에는 적합하지 않습니다.
      • 피처 선택에 따라 달라집니다: 모델의 품질은 피처의 선택과 엔지니어링에 따라 크게 달라질 수 있습니다.

      PoissonRegressor는 대상 변수가 카운트 데이터를 나타내며 포아송 분포를 사용하여 모델링된 경우 회귀 작업을 해결하는 데 사용되는 머신러닝 메서드입니다. 이 메서드는 특정 시간 또는 공간 간격 내에서 고정된 강도로 발생하는 이벤트와 관련된 작업에 유용합니다.


      2.1.25.1. PoissonRegressor 모델을 생성하고 float 및 double용 ONNX로 내보내기 위한 코드

      이 코드는 sklearn.linear_model.PoissonRegressor 모델을 생성하고 합성 데이터로 학습시키고 모델을 ONNX 형식으로 저장하고 플로트 및 더블 입력 데이터를 모두 사용하여 예측을 수행합니다. 또한 원본 모델과 ONNX로 내보낸 모델 모두의 정확도를 평가합니다.

      # PoissonRegressor.py
      # 이 코드는 PoissonRegressor 모델을 학습하고 이를 ONNX 형식(float 및 double 모두)으로 내보내고 ONNX 모델을 사용하여 예측하는 과정을 보여 줍니다.
      # Copyright 2023, MetaQuotes Ltd.
      # https://www.mql5.com


      # 함수를 사용하여 일치하는 소수점 이하 자릿수를 비교합니다.

      def compare_decimal_places(value1, value2):
      # 두 값을 모두 문자열로 변환
          str_value1 = str(value1)
          str_value2 = str(value2)

      # 문자열에서 소수점 위치 찾기
          dot_position1 = str_value1.find(".")
          dot_position2 = str_value2.find(".")

      # 값 중 소수점이 없는 경우 0을 반환합니다.
          if dot_position1 == -1 or dot_position2 == -1:
              return 0

          # 소수점 이하 자릿수 계산
          decimal_places1 = len(str_value1) - dot_position1 - 1
          decimal_places2 = len(str_value2) - dot_position2 - 1

          # 소수점 이하 두 자리 수 중 최소값 찾기
          min_decimal_places = min(decimal_places1, decimal_places2)

          # 소수점 이하 자릿수가 일치하도록 카운트를 초기화
          matching_count = 0

          # 소수점 이하 문자 비교
          for i in range(1, min_decimal_places + 1):
              if str_value1[dot_position1 + i] == str_value2[dot_position2 + i]:
                  matching_count += 1
              else:
                  break

          return matching_count

      # 필요한 라이브러리 가져오기
      import numpy as np
      import matplotlib.pyplot as plt
      from sklearn.linear_model import PoissonRegressor
      from sklearn.metrics import r2_score,mean_absolute_error,mean_squared_error
      import onnx
      import onnxruntime as ort
      from skl2onnx import convert_sklearn
      from skl2onnx.common.data_types import FloatTensorType
      from skl2onnx.common.data_types import DoubleTensorType
      from sys import argv

      # 모델 저장 경로를 정의합니다.
      data_path = argv[0]
      last_index = data_path.rfind("\\")+ 1
      data_path = data_path[0:last_index]

      # 회귀를 위한 합성 데이터 생성
      X = np.arange(0,100,1).reshape(-1,1)
      y = 4*X + 10*np.sin(X*0.5)

      model_name = "PoissonRegressor"
      onnx_model_filename = data_path + "poisson_regressor"

      # PoissonRegressor 모델 생성
      regression_model = PoissonRegressor()

      # 데이터에 모델 맞추기
      regression_model.fit(X, y.ravel())

      # 전체 데이터 집합에 대한 값 예측
      y_pred = regression_model.predict(X)

      # 모델의 성능 평가
      r2 = r2_score(y, y_pred)
      MSE = MEAN_SQUARE_ERROR(Y, Y_PED)
      MAE = MEAN_ABSOLUTE_ERROR(Y, Y_PED)

      print("\n"+model_name+" Original model (double)")
      print("R-squared (Coefficient of determination):", r2)
      print("Mean Absolute Error:", mae)
      print("Mean Squared Error:", mse)

      # ONNX 모델로 변환 (float)
      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.

      initial_type_float = [('float_input', FloatTensorType([None, X.shape[1]])]]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_float = convert_sklearn(regression_model, initial_types=initial_type_float, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_float.onnx"
      onnx.save_model(onnx_model_float, onnx_filename)

      print("\n"+model_name+" ONNX model (float)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.
      initial_type_float = X.astype(np.float32)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_float = onnx_session.run([output_name], {input_name: initial_type_float})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_float = r2_score(y, y_pred_onnx_float)
      mse_onnx_float = mean_squared_error(y, y_pred_onnx_float)
      mae_onnx_float = mean_absolute_error(y, y_pred_onnx_float)
      print("R-squared (Coefficient of determination)", r2_onnx_float)
      print("Mean Absolute Error:", mae_onnx_float)
      print("Mean Squared Error:", mse_onnx_float)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_float))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_float))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_float))
      print("float ONNX model precision: ",compare_decimal_places(mae, mae_onnx_float))

      # 그림 크기 설정
      plt.figure(figsize=(8,5))
      # 원본 데이터와 회귀선을 그림으로 표시합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with float ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_float.png')

      # ONNX 모델로 변환 (double)
      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.

      initial_type_double = [('double_input', DoubleTensorType([None, X.shape[1]]))]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_double = convert_sklearn(regression_model, initial_types=initial_type_double, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_double.onnx"
      onnx.save_model(onnx_model_double, onnx_filename)

      print("\n"+model_name+" ONNX model (double)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.
      initial_type_double = X.astype(np.float64)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_double = onnx_session.run([output_name], {input_name: initial_type_double})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_double = r2_score(y, y_pred_onnx_double)
      mse_onnx_double = mean_squared_error(y, y_pred_onnx_double)
      mae_onnx_double = mean_absolute_error(y, y_pred_onnx_double)
      print("R-squared (Coefficient of determination)", r2_onnx_double)
      print("Mean Absolute Error:", mae_onnx_double)
      print("Mean Squared Error:", mse_onnx_double)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_double))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_double))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_double))
      print("double ONNX model precision: ",compare_decimal_places(mae, mae_onnx_double))

      # 그림 크기 설정
      plt.figure(figsize=(8,5))
      # 원본 데이터와 회귀선을 그림으로 표시합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with double ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_double.png')

      출력:

      Python  PoissonRegressor Original model (double)
      Python  R-squared (Coefficient of determination): 0.9204304782362495
      Python  Mean Absolute Error: 27.59790466048524
      Python  Mean Squared Error: 1052.9242570153044
      Python  
      Python  PoissonRegressor ONNX model (float)
      Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\poisson_regressor_float.onnx
      Python  Information about input tensors in ONNX:
      Python  1. Name: float_input, Data Type: tensor(float), Shape: [None, 1]
      Python  Information about output tensors in ONNX:
      Python  1. Name: variable, Data Type: tensor(float), Shape: [None, 1]
      Python  R-squared (Coefficient of determination) 0.9204305082536851
      Python  Mean Absolute Error: 27.59790825165078
      Python  Mean Squared Error: 1052.9238598018305
      Python  R^2 matching decimal places:  6
      Python  MAE matching decimal places:  5
      Python  MSE matching decimal places:  2
      Python  float ONNX model precision:  5
      Python  
      Python  PoissonRegressor ONNX model (double)
      Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\poisson_regressor_double.onnx
      Python  Information about input tensors in ONNX:
      Python  1. Name: double_input, Data Type: tensor(double), Shape: [None, 1]
      Python  Information about output tensors in ONNX:
      Python  1. Name: variable, Data Type: tensor(double), Shape: [None, 1]
      Python  R-squared (Coefficient of determination) 0.9204304782362495
      Python  Mean Absolute Error: 27.59790466048524
      Python  Mean Squared Error: 1052.9242570153044
      Python  R^2 matching decimal places:  16
      Python  MAE matching decimal places:  14
      Python  MSE matching decimal places:  13
      Python  double ONNX model precision:  14
      

      Fig.82. 포아송 레그레서.py(float ONNX)의 결과

      그림 82. PoissonRegressor.py 결과(float ONNX)


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

      이 코드는 저장된 poisson_regressor_float.onnxpoisson_regressor_double.onnx 모델을 실행하고 MQL5에서 회귀 메트릭을 사용하는 것을 보여줍니다.

      //+------------------------------------------------------------------+
      //|                                             PoissonRegressor.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"
      
      #define   ModelName          "PoissonRegressor"
      #define   ONNXFilenameFloat  "poisson_regressor_float.onnx"
      #define   ONNXFilenameDouble "poisson_regressor_double.onnx"
      
      #resource ONNXFilenameFloat  as const uchar ExtModelFloat[];
      #resource ONNXFilenameDouble as const uchar ExtModelDouble[];
      
      #define   TestFloatModel  1
      #define   TestDoubleModel 2
      
      //+------------------------------------------------------------------+
      //| Calculate regression using float values                          |
      //+------------------------------------------------------------------+
      bool RunModelFloat(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         float input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=(float)input_vector[k];
      //--- prepare output tensor
         float output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      //+------------------------------------------------------------------+
      //| Calculate regression using double values                         |
      //+------------------------------------------------------------------+
      bool RunModelDouble(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         double input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=input_vector[k];
      //--- prepare output tensor
         double output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      
      //+------------------------------------------------------------------+
      //| Generate synthetic data                                          |
      //+------------------------------------------------------------------+
      bool GenerateData(const int n,vector &x,vector &y)
        {
         if(n<=0)
            return(false);
      //--- prepare arrays
         x.Resize(n);
         y.Resize(n);
      //---
         for(int i=0; i<n; i++)
           {
            x[i]=(double)1.0*i;
            y[i]=(double)(4*x[i] + 10*sin(x[i]*0.5));
           }
      //---
         return(true);
        }
      
      //+------------------------------------------------------------------+
      //| TestRegressionModel                                              |
      //+------------------------------------------------------------------+
      bool TestRegressionModel(const string model_name,const int model_type)
        {
      //---
         long  model=INVALID_HANDLE;
         ulong flags=ONNX_DEFAULT;
      
         if(model_type==TestFloatModel)
           {
            PrintFormat("\nTesting ONNX float: %s (%s)",model_name,ONNXFilenameFloat);
            model=OnnxCreateFromBuffer(ExtModelFloat,flags);
           }
         else
            if(model_type==TestDoubleModel)
              {
               PrintFormat("\nTesting ONNX double: %s (%s)",model_name,ONNXFilenameDouble);
               model=OnnxCreateFromBuffer(ExtModelDouble,flags);
              }
            else
              {
               PrintFormat("Model type is not incorrect.");
               return(false);
              }
      //--- check
         if(model==INVALID_HANDLE)
           {
            PrintFormat("model_name=%s OnnxCreate error %d",model_name,GetLastError());
            return(false);
           }
      //---
         vector x_values= {};
         vector y_true= {};
         vector y_predicted= {};
      //---
         int n=100;
         GenerateData(n,x_values,y_true);
      //---
         bool run_result=false;
         if(model_type==TestFloatModel)
           {
            run_result=RunModelFloat(model,x_values,y_predicted);
           }
         else
            if(model_type==TestDoubleModel)
              {
               run_result=RunModelDouble(model,x_values,y_predicted);
              }
      //---
         if(run_result)
           {
            PrintFormat("MQL5:   R-Squared (Coefficient of determination): %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_R2));
            PrintFormat("MQL5:   Mean Absolute Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MAE));
            PrintFormat("MQL5:   Mean Squared Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MSE));
           }
         else
            PrintFormat("Error %d",GetLastError());
      //--- release model
         OnnxRelease(model);
      //---
         return(true);
        }
      //+------------------------------------------------------------------+
      //| Script program start function                                    |
      //+------------------------------------------------------------------+
      int OnStart(void)
        {
      //--- test ONNX regression model for float
         TestRegressionModel(ModelName,TestFloatModel);
      //--- test ONNX regression model for double
         TestRegressionModel(ModelName,TestDoubleModel);
      //---
         return(0);
        }
      //+------------------------------------------------------------------+

      출력:

      PoissonRegressor (EURUSD,H1)    Testing ONNX float: PoissonRegressor (poisson_regressor_float.onnx)
      PoissonRegressor (EURUSD,H1)    MQL5:   R-Squared (Coefficient of determination): 0.9204305082536851
      PoissonRegressor (EURUSD,H1)    MQL5:   Mean Absolute Error: 27.5979082516507788
      PoissonRegressor (EURUSD,H1)    MQL5:   Mean Squared Error: 1052.9238598018305311
      PoissonRegressor (EURUSD,H1)    
      PoissonRegressor (EURUSD,H1)    Testing ONNX double: PoissonRegressor (poisson_regressor_double.onnx)
      PoissonRegressor (EURUSD,H1)    MQL5:   R-Squared (Coefficient of determination): 0.9204304782362493
      PoissonRegressor (EURUSD,H1)    MQL5:   Mean Absolute Error: 27.5979046604852343
      PoissonRegressor (EURUSD,H1)    MQL5:   Mean Squared Error: 1052.9242570153051020
      

      파이썬의 원래 배정밀도 모델과 비교:

      Testing ONNX float: PoissonRegressor (poisson_regressor_float.onnx)
      Python  Mean Absolute Error: 27.59790466048524
      MQL5:   Mean Absolute Error: 27.5979082516507788
          
      Testing ONNX double: PoissonRegressor (poisson_regressor_double.onnx)
      Python  Mean Absolute Error: 27.59790466048524
      MQL5:   Mean Absolute Error: 27.5979046604852343

      ONNX float MAE의 정확도: 소수점 이하 5자리, 정확도 ONNX 더블 MAE: 소수점 이하 13자리.


      2.1.25.3. ONNX representation of the poisson_regressor_float.onnx and poisson_regressor_double.onnx


      Fig.83. Netron에서 poisson_regressor_float.onnx의 ONNX 표현

      그림 83. 네트론에서 poisson_regressor_float.onnx의 ONNX 표현


      Fig.84. Netron에서 poisson_regressor_double.onnx의 ONNX 표현

      그림 84. 네트론에서 poisson_regressor_double.onnx의 ONNX 표현



      2.1.26. sklearn.neighbors.RadiusNeighborsRegressor

      RadiusNeighborsRegressor 회귀 작업에 사용되는 머신 러닝 메서드입니다. 이는 피처 공간에서 가장 가까운 이웃을 기반으로 대상 변수의 값을 예측하도록 설계된 k-NN(k-Nearest Neighbors) 메서드의 변형입니다. 그러나 k-NN 메서드에서와 같이 고정된 수의 이웃 대신 RadiusNeighborsRegressor는 고정된 반경을 사용하여 각 샘플에 대한 이웃을 결정합니다.

      RadiusNeighborsRegressor의 작동 방식:
      1. 데이터 입력: 피처(독립 변수)와 대상 변수(연속형)가 포함된 데이터 집합으로 시작합니다.
      2. 반경 설정하기: 피처 공간에서 각 샘플에 대해 가장 가까운 이웃을 결정하기 위해 고정 반경을 설정해야 하는 RadiusNeighborsRegressor.
      3. 이웃 정의: 각 샘플에 대해 지정된 반경 내의 모든 데이터 포인트가 결정되어 해당 샘플의 이웃이 됩니다.
      4. 가중 평균: 각 샘플의 목표 변수 값을 예측하기 위해 이웃 샘플의 목표 변수 값을 사용합니다. 이 작업은 샘플 간의 거리에 따라 가중치가 달라지는 가중 평균을 사용하여 수행되는 경우가 많습니다.
      5. 예측: 학습 후에는 이 모델을 사용하여 피처 공간에서 가장 가까운 이웃을 기반으로 새로운 데이터에 대한 목표 변수의 값을 예측할 수 있습니다.
      RadiusNeighborsRegressor의 장점:
      • 다용도성: 특히 반경에 따라 이웃의 수가 크게 달라질 수 있는 회귀 작업에 RadiusNeighborsRegressor를 사용할 수 있습니다.
      • 이상값에 대한 복원력: 이웃 기반 접근 방식은 모델에서 가까운 데이터 요소만 고려하기 때문에 이상값에 탄력적으로 대응할 수 있습니다.
      RadiusNeighborsRegressor의 한계:
      • 반경 선택에 따라 달라집니다: 올바른 반경을 선택하려면 조정과 실험이 필요할 수 있습니다.
      • 계산의 복잡성: 대규모 데이터 세트를 처리하려면 상당한 컴퓨팅 리소스가 필요할 수 있습니다.
      RadiusNeighborsRegressor는 반경이 고정된 k-Nearest Neighbors 메서드을 기반으로 하는 회귀 작업에 사용되는 머신 러닝 메서드입니다. 이 메서드는 반경에 따라 이웃의 수가 달라질 수 있는 상황이나 데이터에 이상값이 포함된 경우에 유용할 수 있습니다.


      2.1.26.1. RadiusNeighborsRegressor를 생성하고 float 및 double으로 ONNX로 내보내기 위한 코드

      이 코드는 sklearn.neighbors.RadiusNeighborsRegressor 모델을 생성하고 합성 데이터로 학습시키고 모델을 ONNX 형식으로 저장하고 플로트 및 더블 입력 데이터를 모두 사용하여 예측을 수행합니다. 또한 원본 모델과 ONNX로 내보낸 모델 모두의 정확도를 평가합니다.

      # RadiusNeighborsRegressor.py
      # 이 코드는 RadiusNeighborsRegressor 모델을 훈련하고 이를 ONNX 형식(float 및 double 모두)으로 내보내고 ONNX 모델을 사용하여 예측하는 과정을 보여 줍니다.
      # Copyright 2023, MetaQuotes Ltd.
      # https://www.mql5.com

      # 소수점 이하 자릿수를 비교학 위한 함수
      def compare_decimal_places(value1, value2):
      # 두 값을 모두 문자열로 변환
          str_value1 = str(value1)
          str_value2 = str(value2)

      # 문자열에서 소수점 위치 찾기
          dot_position1 = str_value1.find(".")
          dot_position2 = str_value2.find(".")

      # 값 중 소수점이 없는 경우 0을 반환합니다.
          if dot_position1 == -1 or dot_position2 == -1:
              return 0

          # 소수점 이하 자릿수 계산
          decimal_places1 = len(str_value1) - dot_position1 - 1
          decimal_places2 = len(str_value2) - dot_position2 - 1

          # 소수점 이하 두 자리 수 중 최소값 찾기
          min_decimal_places = min(decimal_places1, decimal_places2)

          # 소수점 이하 자릿수가 일치하도록 카운트를 초기화
          matching_count = 0

          # 소수점 이하 문자 비교
          for i in range(1, min_decimal_places + 1):
              if str_value1[dot_position1 + i] == str_value2[dot_position2 + i]:
                  matching_count += 1
              else:
                  break

          return matching_count

      # 필요한 라이브러리 가져오기
      import numpy as np
      import matplotlib.pyplot as plt
      에서 sklearn.neighbors import RadiusNeighborsRegressor
      from sklearn.metrics import r2_score,mean_absolute_error,mean_squared_error
      import onnx
      import onnxruntime as ort
      from skl2onnx import convert_sklearn
      from skl2onnx.common.data_types import FloatTensorType
      from skl2onnx.common.data_types import DoubleTensorType
      from sys import argv

      # 모델 저장 경로를 정의합니다.
      data_path = argv[0]
      last_index = data_path.rfind("\\")+ 1
      data_path = data_path[0:last_index]

      # 회귀를 위한 합성 데이터 생성
      X = np.arange(0,100,1).reshape(-1,1)
      y = 4*X + 10*np.sin(X*0.5)

      model_name = "RadiusNeighborsRegressor"
      onnx_model_filename = data_path + "radius_neighbors_regressor"

      # RadiusNeighborsRegressor 모델 만들기
      regression_model = RadiusNeighborsRegressor()

      # 데이터에 모델 맞추기
      regression_model.fit(X, y.ravel())

      # 전체 데이터 집합에 대한 값 예측
      y_pred = regression_model.predict(X)

      # 모델의 성능 평가
      r2 = r2_score(y, y_pred)
      MSE = MEAN_SQUARE_ERROR(Y, Y_PED)
      MAE = MEAN_ABSOLUTE_ERROR(Y, Y_PED)

      print("\n"+model_name+" Original model (double)")
      print("R-squared (Coefficient of determination):", r2)
      print("Mean Absolute Error:", mae)
      print("Mean Squared Error:", mse)

      # ONNX 모델로 변환 (float)
      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.

      initial_type_float = [('float_input', FloatTensorType([None, X.shape[1]])]]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_float = convert_sklearn(regression_model, initial_types=initial_type_float, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_float.onnx"
      onnx.save_model(onnx_model_float, onnx_filename)

      print("\n"+model_name+" ONNX model (float)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.
      initial_type_float = X.astype(np.float32)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_float = onnx_session.run([output_name], {input_name: initial_type_float})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_float = r2_score(y, y_pred_onnx_float)
      mse_onnx_float = mean_squared_error(y, y_pred_onnx_float)
      mae_onnx_float = mean_absolute_error(y, y_pred_onnx_float)
      print("R-squared (Coefficient of determination)", r2_onnx_float)
      print("Mean Absolute Error:", mae_onnx_float)
      print("Mean Squared Error:", mse_onnx_float)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_float))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_float))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_float))
      print("float ONNX model precision: ",compare_decimal_places(mae, mae_onnx_float))

      # 그림 크기 설정
      plt.figure(figsize=(8, 5))
      # 원본 데이터와 회귀선을 그림으로 표시합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with float ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_float.png')

      # ONNX 모델로 변환 (double)
      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.

      initial_type_double = [('double_input', DoubleTensorType([None, X.shape[1]]))]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_double = convert_sklearn(regression_model, initial_types=initial_type_double, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_double.onnx"
      onnx.save_model(onnx_model_double, onnx_filename)

      print("\n"+model_name+" ONNX model (double)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.
      initial_type_double = X.astype(np.float64)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_double = onnx_session.run([output_name], {input_name: initial_type_double})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_double = r2_score(y, y_pred_onnx_double)
      mse_onnx_double = mean_squared_error(y, y_pred_onnx_double)
      mae_onnx_double = mean_absolute_error(y, y_pred_onnx_double)
      print("R-squared (Coefficient of determination)", r2_onnx_double)
      print("Mean Absolute Error:", mae_onnx_double)
      print("Mean Squared Error:", mse_onnx_double)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_double))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_double))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_double))
      print("double ONNX model precision: ",compare_decimal_places(mae, mae_onnx_double))

      # 그림 크기 설정
      plt.figure(figsize=(8, 5))
      # 원본 데이터와 회귀선을 그림으로 표시합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with double ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_double.png')

      출력:

      Python  RadiusNeighborsRegressor Original model (double)
      Python  R-squared (Coefficient of determination): 0.9999521132921395
      Python  Mean Absolute Error: 0.591458244376554
      Python  Mean Squared Error: 0.6336732353950723
      Python  
      Python  RadiusNeighborsRegressor ONNX model (float)
      Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\radius_neighbors_regressor_float.onnx
      Python  Information about input tensors in ONNX:
      Python  1. Name: float_input, Data Type: tensor(float), Shape: [None, 1]
      Python  Information about output tensors in ONNX:
      Python  1. Name: variable, Data Type: tensor(float), Shape: [None, 1]
      Python  R-squared (Coefficient of determination) 0.9999999999999971
      Python  Mean Absolute Error: 4.393654615473253e-06
      Python  Mean Squared Error: 3.829042036424747e-11
      Python  R^2 matching decimal places:  4
      Python  MAE matching decimal places:  0
      Python  MSE matching decimal places:  0
      Python  float ONNX model precision:  0
      Python  
      Python  RadiusNeighborsRegressor ONNX model (double)
      Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\radius_neighbors_regressor_double.onnx
      Python  Information about input tensors in ONNX:
      Python  1. Name: double_input, Data Type: tensor(double), Shape: [None, 1]
      Python  Information about output tensors in ONNX:
      Python  1. Name: variable, Data Type: tensor(double), Shape: [None, 1]
      Python  R-squared (Coefficient of determination) 1.0
      Python  Mean Absolute Error: 0.0
      Python  Mean Squared Error: 0.0
      Python  R^2 matching decimal places:  0
      Python  MAE matching decimal places:  0
      Python  MSE matching decimal places:  0
      Python  double ONNX model precision:  0
      

      Fig.85. RadiusNeighborsRegressor.py의 결과 (float ONNX)

      그림 85. RadiusNeighborsRegressor.py의 결과(float ONNX)


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

      이 코드는 저장된 radius_neighbors_regressor_float.onnxradius_neighbors_regressor_double.onnx 모델을 실행하고 MQL5에서 회귀 메트릭을 사용하는 것을 보여 줍니다.

      //+------------------------------------------------------------------+
      //|                                     RadiusNeighborsRegressor.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"
      
      #define   ModelName          "RadiusNeighborsRegressor"
      #define   ONNXFilenameFloat  "radius_neighbors_regressor_float.onnx"
      #define   ONNXFilenameDouble "radius_neighbors_regressor_double.onnx"
      
      #resource ONNXFilenameFloat  as const uchar ExtModelFloat[];
      #resource ONNXFilenameDouble as const uchar ExtModelDouble[];
      
      #define   TestFloatModel  1
      #define   TestDoubleModel 2
      
      //+------------------------------------------------------------------+
      //| Calculate regression using float values                          |
      //+------------------------------------------------------------------+
      bool RunModelFloat(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         float input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=(float)input_vector[k];
      //--- prepare output tensor
         float output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      //+------------------------------------------------------------------+
      //| Calculate regression using double values                         |
      //+------------------------------------------------------------------+
      bool RunModelDouble(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         double input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=input_vector[k];
      //--- prepare output tensor
         double output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      
      //+------------------------------------------------------------------+
      //| Generate synthetic data                                          |
      //+------------------------------------------------------------------+
      bool GenerateData(const int n,vector &x,vector &y)
        {
         if(n<=0)
            return(false);
      //--- prepare arrays
         x.Resize(n);
         y.Resize(n);
      //---
         for(int i=0; i<n; i++)
           {
            x[i]=(double)1.0*i;
            y[i]=(double)(4*x[i] + 10*sin(x[i]*0.5));
           }
      //---
         return(true);
        }
      
      //+------------------------------------------------------------------+
      //| TestRegressionModel                                              |
      //+------------------------------------------------------------------+
      bool TestRegressionModel(const string model_name,const int model_type)
        {
      //---
         long  model=INVALID_HANDLE;
         ulong flags=ONNX_DEFAULT;
      
         if(model_type==TestFloatModel)
           {
            PrintFormat("\nTesting ONNX float: %s (%s)",model_name,ONNXFilenameFloat);
            model=OnnxCreateFromBuffer(ExtModelFloat,flags);
           }
         else
            if(model_type==TestDoubleModel)
              {
               PrintFormat("\nTesting ONNX double: %s (%s)",model_name,ONNXFilenameDouble);
               model=OnnxCreateFromBuffer(ExtModelDouble,flags);
              }
            else
              {
               PrintFormat("Model type is not incorrect.");
               return(false);
              }
      //--- check
         if(model==INVALID_HANDLE)
           {
            PrintFormat("model_name=%s OnnxCreate error %d",model_name,GetLastError());
            return(false);
           }
      //---
         vector x_values= {};
         vector y_true= {};
         vector y_predicted= {};
      //---
         int n=100;
         GenerateData(n,x_values,y_true);
      //---
         bool run_result=false;
         if(model_type==TestFloatModel)
           {
            run_result=RunModelFloat(model,x_values,y_predicted);
           }
         else
            if(model_type==TestDoubleModel)
              {
               run_result=RunModelDouble(model,x_values,y_predicted);
              }
      //---
         if(run_result)
           {
            PrintFormat("MQL5:   R-Squared (Coefficient of determination): %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_R2));
            PrintFormat("MQL5:   Mean Absolute Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MAE));
            PrintFormat("MQL5:   Mean Squared Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MSE));
           }
         else
            PrintFormat("Error %d",GetLastError());
      //--- release model
         OnnxRelease(model);
      //---
         return(true);
        }
      //+------------------------------------------------------------------+
      //| Script program start function                                    |
      //+------------------------------------------------------------------+
      int OnStart(void)
        {
      //--- test ONNX regression model for float
         TestRegressionModel(ModelName,TestFloatModel);
      //--- test ONNX regression model for double
         TestRegressionModel(ModelName,TestDoubleModel);
      //---
         return(0);
        }
      //+------------------------------------------------------------------+

      출력:

      RadiusNeighborsRegressor (EURUSD,H1)    Testing ONNX float: RadiusNeighborsRegressor (radius_neighbors_regressor_float.onnx)
      RadiusNeighborsRegressor (EURUSD,H1)    MQL5:   R-Squared (Coefficient of determination): 0.9999999999999971
      RadiusNeighborsRegressor (EURUSD,H1)    MQL5:   Mean Absolute Error: 0.0000043936546155
      RadiusNeighborsRegressor (EURUSD,H1)    MQL5:   Mean Squared Error: 0.0000000000382904
      RadiusNeighborsRegressor (EURUSD,H1)    
      RadiusNeighborsRegressor (EURUSD,H1)    Testing ONNX double: RadiusNeighborsRegressor (radius_neighbors_regressor_double.onnx)
      RadiusNeighborsRegressor (EURUSD,H1)    MQL5:   R-Squared (Coefficient of determination): 1.0000000000000000
      RadiusNeighborsRegressor (EURUSD,H1)    MQL5:   Mean Absolute Error: 0.0000000000000000
      RadiusNeighborsRegressor (EURUSD,H1)    MQL5:   Mean Squared Error: 0.0000000000000000
      

      2.1.26.3. radius_neighbors_regressor_float.onnx 및 radius_neighbors_regressor_double.onnx의 ONNX 표현입니다.


      그림 86. Netron에서 radius_neighbors_regressor_float.onnx의 ONNX 표현

      그림 86. 네트론에서 radius_neighbors_regressor_float.onnx의 ONNX 표현


      그림 87. 네트론에서 radius_neighbors_regressor_double.onnx의 ONNX 표현

      그림 87. 네트론에서 radius_neighbors_regressor_double.onnx의 ONNX 표현



      2.1.27. sklearn.neighbors.KNeighborsRegressor

      KNeighborsRegressor 회귀 작업에 사용되는 머신 러닝 메서드입니다.

      k-Nearest Neighbors(k-NN) 알고리즘의 범주에 속하며 훈련 데이터 세트에서 객체 간의 근접성(유사도)을 기반으로 목표 변수의 수치 값을 예측하는 데 사용됩니다.

      KNeighborsRegressor의 작동 방식:

      1. 데이터 입력: 피처(독립 변수) 및 대상 변수의 해당 값을 포함한 초기 데이터 집합으로 시작합니다.
      2. 이웃 수(k)를 선택합니다: 예측할 때 고려할 가장 가까운 이웃의 수(k)를 선택해야 합니다. 이 숫자는 모델의 하이퍼파라미터 중 하나입니다.
      3. 근접성 계산하기: 새 데이터(예측이 필요한 포인트)의 경우 이 데이터와 학습 데이터 세트의 모든 객체 간의 거리 또는 유사성이 계산됩니다.
      4. 가장 가까운 이웃 k개 선택: 학습 데이터 세트에서 새 데이터에 가장 가까운 k개의 객체가 선택됩니다.
      5. 예측: 회귀 작업의 경우 새 데이터에 대한 목표 변수의 값을 예측하는 것은 가장 가까운 이웃 k개의 목표 변수의 평균값으로 계산됩니다.

      KNeighborsRegressor의 장점:

      • 사용 편의성: KNeighborsRegressor는 복잡한 데이터 전처리가 필요 없는 간단한 알고리즘입니다.
      • 비파라미터 특성: 이 메서드은 피처과 대상 변수 간에 특정한 피처적인 형태의 종속성을 가정하지 않으므로 다양한 관계를 모델링할 수 있습니다.
      • 재현성: 예측은 데이터 근접성을 기반으로 하므로 KNeighborsRegressor의 결과를 재현할 수 있습니다.

      KNeighborsRegressor의 한계:

      • 계산의 복잡성: 학습 데이터 세트의 모든 지점까지의 거리를 계산하는 것은 대량의 데이터의 경우 계산 비용이 많이 들 수 있습니다.
      • 이웃 수 선택에 대한 민감도: 최적의 k 값을 선택하려면 튜닝이 필요하며 모델 성능에 큰 영향을 미칠 수 있습니다.
      • 소음에 대한 민감도: 이 메서드는 데이터 노이즈와 이상값에 민감할 수 있습니다.

      KNeighborsRegressor는 대상 변수를 예측하기 위해 객체의 이웃을 고려해야 하는 회귀 작업에 유용합니다. 피처와 대상 변수 간의 관계가 비선형적이고 복잡한 상황에서 특히 유용할 수 있습니다.


      2.1.27.1. KNeighborsRegressor 모델을 생성하고 float 및 double용 ONNX로 내보내는 코드

      이 코드는 sklearn.neighbors.KNeighborsRegressor 모델을 생성하고, 합성 데이터로 학습시키고, 모델을 ONNX 형식으로 저장하고 플로트 및 더블 입력 데이터를 모두 사용하여 예측을 수행합니다. 또한 원본 모델과 ONNX로 내보낸 모델 모두의 정확도를 평가합니다.

      # KNeighborsRegressor.py
      # 이 코드는 KNeighborsRegressor 모델을 학습하고 이를 ONNX 형식(float 및 double 모두)으로 내보내고 ONNX 모델을 사용하여 예측하는 과정을 보여 줍니다.
      # Copyright 2023, MetaQuotes Ltd.
      # https://www.mql5.com


      # 소수점 이하 자릿수를 비교학 위한 함수
      def compare_decimal_places(value1, value2):
      # 두 값을 모두 문자열로 변환
          str_value1 = str(value1)
          str_value2 = str(value2)

      # 문자열에서 소수점 위치 찾기
          dot_position1 = str_value1.find(".")
          dot_position2 = str_value2.find(".")

      # 값 중 소수점이 없는 경우 0을 반환합니다.
          if dot_position1 == -1 or dot_position2 == -1:
              return 0

          # 소수점 이하 자릿수 계산
          decimal_places1 = len(str_value1) - dot_position1 - 1
          decimal_places2 = len(str_value2) - dot_position2 - 1

          # 소수점 이하 두 자리 수 중 최소값 찾기
          min_decimal_places = min(decimal_places1, decimal_places2)

          # 소수점 이하 자릿수가 일치하도록 카운트를 초기화
          matching_count = 0

          # 소수점 이하 문자 비교
          for i in range(1, min_decimal_places + 1):
              if str_value1[dot_position1 + i] == str_value2[dot_position2 + i]:
                  matching_count += 1
              else:
                  break

          return matching_count

      # 필요한 라이브러리 가져오기
      import numpy as np
      import matplotlib.pyplot as plt
      에서 sklearn.neighbors import KNeighborsRegressor
      from sklearn.metrics import r2_score,mean_absolute_error,mean_squared_error
      import onnx
      import onnxruntime as ort
      from skl2onnx import convert_sklearn
      from skl2onnx.common.data_types import FloatTensorType
      from skl2onnx.common.data_types import DoubleTensorType
      from sys import argv

      # 모델 저장 경로를 정의합니다.
      data_path = argv[0]
      last_index = data_path.rfind("\\")+ 1
      data_path = data_path[0:last_index]

      # 회귀를 위한 합성 데이터 생성
      X = np.arange(0,100,1).reshape(-1,1)
      y = 4*X + 10*np.sin(X*0.5)

      model_name = "KNeighborsRegressor"
      onnx_model_filename = data_path + "kneighbors_regressor"

      이웃 회귀 모델 만들기 # KNeighbor 회귀 모델 만들기
      kneighbors_model = KNeighborsRegressor(n_neighbors=5)

      # 데이터에 모델 맞추기
      kneighbors_model.fit(X, y.ravel())

      # 전체 데이터 집합에 대한 값 예측
      y_pred = kneighbors_model.predict(X)

      # 모델의 성능 평가
      r2 = r2_score(y, y_pred)
      MSE = MEAN_SQUARE_ERROR(Y, Y_PED)
      MAE = MEAN_ABSOLUTE_ERROR(Y, Y_PED)

      print("\n"+model_name+" Original model (double)")
      print("R-squared (Coefficient of determination):", r2)
      print("Mean Absolute Error:", mae)
      print("Mean Squared Error:", mse)

      # ONNX 모델로 변환 (float)
      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.

      initial_type_float = [('float_input', FloatTensorType([None, X.shape[1]])]]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_float = convert_sklearn(kneighbors_model, initial_types=initial_type_float, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_float.onnx"
      onnx.save_model(onnx_model_float, onnx_filename)

      print("\n"+model_name+" ONNX model (float)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.
      initial_type_float = X.astype(np.float32)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_float = onnx_session.run([output_name], {input_name: initial_type_float})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_float = r2_score(y, y_pred_onnx_float)
      mse_onnx_float = mean_squared_error(y, y_pred_onnx_float)
      mae_onnx_float = mean_absolute_error(y, y_pred_onnx_float)
      print("R-squared (Coefficient of determination)", r2_onnx_float)
      print("Mean Absolute Error:", mae_onnx_float)
      print("Mean Squared Error:", mse_onnx_float)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_float))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_float))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_float))
      print("float ONNX model precision: ",compare_decimal_places(mae, mae_onnx_float))

      # 그림 크기 설정
      plt.figure(figsize=(8,5))
      # 원본 데이터와 회귀선을 그림으로 표시합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with float ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_float.png')

      # ONNX 모델로 변환 (double)
      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.

      initial_type_double = [('double_input', DoubleTensorType([None, X.shape[1]]))]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_double = convert_sklearn(kneighbors_model, initial_types=initial_type_double, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_double.onnx"
      onnx.save_model(onnx_model_double, onnx_filename)

      print("\n"+model_name+" ONNX model (double)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.
      initial_type_double = X.astype(np.float64)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_double = onnx_session.run([output_name], {input_name: initial_type_double})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_double = r2_score(y, y_pred_onnx_double)
      mse_onnx_double = mean_squared_error(y, y_pred_onnx_double)
      mae_onnx_double = mean_absolute_error(y, y_pred_onnx_double)
      print("R-squared (Coefficient of determination)", r2_onnx_double)
      print("Mean Absolute Error:", mae_onnx_double)
      print("Mean Squared Error:", mse_onnx_double)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_double))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_double))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_double))
      print("double ONNX model precision: ",compare_decimal_places(mae, mae_onnx_double))

      # 그림 크기 설정
      plt.figure(figsize=(8,5))
      # 원본 데이터와 회귀선을 그림으로 표시합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with double ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_double.png')

      출력:

      Python  KNeighborsRegressor Original model (double)
      Python  R-squared (Coefficient of determination): 0.9995599863346534
      Python  Mean Absolute Error: 1.7414210057117578
      Python  Mean Squared Error: 5.822594523532273
      Python  
      Python  KNeighborsRegressor ONNX model (float)
      Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\kneighbors_regressor_float.onnx
      Python  Information about input tensors in ONNX:
      Python  1. Name: float_input, Data Type: tensor(float), Shape: [None, 1]
      Python  Information about output tensors in ONNX:
      Python  1. Name: variable, Data Type: tensor(float), Shape: [None, 1]
      Python  R-squared (Coefficient of determination) 0.9995599867417418
      Python  Mean Absolute Error: 1.7414195457976402
      Python  Mean Squared Error: 5.8225891366283875
      Python  R^2 matching decimal places:  9
      Python  MAE matching decimal places:  4
      Python  MSE matching decimal places:  4
      Python  float ONNX model precision:  4
      Python  
      Python  KNeighborsRegressor ONNX model (double)
      Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\kneighbors_regressor_double.onnx
      Python  Information about input tensors in ONNX:
      Python  1. Name: double_input, Data Type: tensor(double), Shape: [None, 1]
      Python  Information about output tensors in ONNX:
      Python  1. Name: variable, Data Type: tensor(double), Shape: [None, 1]
      Python  R-squared (Coefficient of determination) 0.9995599863346534
      Python  Mean Absolute Error: 1.7414210057117583
      Python  Mean Squared Error: 5.822594523532269
      Python  R^2 matching decimal places:  16
      Python  MAE matching decimal places:  14
      Python  MSE matching decimal places:  13
      Python  double ONNX model precision:  14
      

      Fig.88. KNeighborsRegressor.py의 결과 (float ONNX)

      그림 88. KNeighborsRegressor.py의 결과(float ONNX)



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

      이 코드는 저장된 kneighbor_regressor_float.onnxkneighbor_regressor_double.onnx 모델을 실행하고 MQL5에서 회귀 메트릭을 사용하는 것을 보여 줍니다.

      //+------------------------------------------------------------------+
      //|                                          KNeighborsRegressor.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"
      
      #define   ModelName          "KNeighborsRegressor"
      #define   ONNXFilenameFloat  "kneighbors_regressor_float.onnx"
      #define   ONNXFilenameDouble "kneighbors_regressor_double.onnx"
      
      #resource ONNXFilenameFloat  as const uchar ExtModelFloat[];
      #resource ONNXFilenameDouble as const uchar ExtModelDouble[];
      
      #define   TestFloatModel  1
      #define   TestDoubleModel 2
      
      //+------------------------------------------------------------------+
      //| Calculate regression using float values                          |
      //+------------------------------------------------------------------+
      bool RunModelFloat(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         float input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=(float)input_vector[k];
      //--- prepare output tensor
         float output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      //+------------------------------------------------------------------+
      //| Calculate regression using double values                         |
      //+------------------------------------------------------------------+
      bool RunModelDouble(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         double input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=input_vector[k];
      //--- prepare output tensor
         double output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      
      //+------------------------------------------------------------------+
      //| Generate synthetic data                                          |
      //+------------------------------------------------------------------+
      bool GenerateData(const int n,vector &x,vector &y)
        {
         if(n<=0)
            return(false);
      //--- prepare arrays
         x.Resize(n);
         y.Resize(n);
      //---
         for(int i=0; i<n; i++)
           {
            x[i]=(double)1.0*i;
            y[i]=(double)(4*x[i] + 10*sin(x[i]*0.5));
           }
      //---
         return(true);
        }
      
      //+------------------------------------------------------------------+
      //| TestRegressionModel                                              |
      //+------------------------------------------------------------------+
      bool TestRegressionModel(const string model_name,const int model_type)
        {
      //---
         long  model=INVALID_HANDLE;
         ulong flags=ONNX_DEFAULT;
      
         if(model_type==TestFloatModel)
           {
            PrintFormat("\nTesting ONNX float: %s (%s)",model_name,ONNXFilenameFloat);
            model=OnnxCreateFromBuffer(ExtModelFloat,flags);
           }
         else
            if(model_type==TestDoubleModel)
              {
               PrintFormat("\nTesting ONNX double: %s (%s)",model_name,ONNXFilenameDouble);
               model=OnnxCreateFromBuffer(ExtModelDouble,flags);
              }
            else
              {
               PrintFormat("Model type is not incorrect.");
               return(false);
              }
      //--- check
         if(model==INVALID_HANDLE)
           {
            PrintFormat("model_name=%s OnnxCreate error %d",model_name,GetLastError());
            return(false);
           }
      //---
         vector x_values= {};
         vector y_true= {};
         vector y_predicted= {};
      //---
         int n=100;
         GenerateData(n,x_values,y_true);
      //---
         bool run_result=false;
         if(model_type==TestFloatModel)
           {
            run_result=RunModelFloat(model,x_values,y_predicted);
           }
         else
            if(model_type==TestDoubleModel)
              {
               run_result=RunModelDouble(model,x_values,y_predicted);
              }
      //---
         if(run_result)
           {
            PrintFormat("MQL5:   R-Squared (Coefficient of determination): %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_R2));
            PrintFormat("MQL5:   Mean Absolute Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MAE));
            PrintFormat("MQL5:   Mean Squared Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MSE));
           }
         else
            PrintFormat("Error %d",GetLastError());
      //--- release model
         OnnxRelease(model);
      //---
         return(true);
        }
      //+------------------------------------------------------------------+
      //| Script program start function                                    |
      //+------------------------------------------------------------------+
      int OnStart(void)
        {
      //--- test ONNX regression model for float
         TestRegressionModel(ModelName,TestFloatModel);
      //--- test ONNX regression model for double
         TestRegressionModel(ModelName,TestDoubleModel);
      //---
         return(0);
        }
      //+------------------------------------------------------------------+

      출력:

      KNeighborsRegressor (EURUSD,H1) Testing ONNX float: KNeighborsRegressor (kneighbors_regressor_float.onnx)
      KNeighborsRegressor (EURUSD,H1) MQL5:   R-Squared (Coefficient of determination): 0.9995599860116634
      KNeighborsRegressor (EURUSD,H1) MQL5:   Mean Absolute Error: 1.7414200607817711
      KNeighborsRegressor (EURUSD,H1) MQL5:   Mean Squared Error: 5.8225987975798184
      KNeighborsRegressor (EURUSD,H1) 
      KNeighborsRegressor (EURUSD,H1) Testing ONNX double: KNeighborsRegressor (kneighbors_regressor_double.onnx)
      KNeighborsRegressor (EURUSD,H1) MQL5:   R-Squared (Coefficient of determination): 0.9995599863346534
      KNeighborsRegressor (EURUSD,H1) MQL5:   Mean Absolute Error: 1.7414210057117601
      KNeighborsRegressor (EURUSD,H1) MQL5:   Mean Squared Error: 5.8225945235322705
      

      파이썬의 원래 배정밀도 모델과 비교:

      Testing ONNX float: KNeighborsRegressor (kneighbors_regressor_float.onnx)
      Python  Mean Absolute Error: 1.7414210057117578
      MQL5:   Mean Absolute Error: 1.7414200607817711
       
      Testing ONNX double: KNeighborsRegressor (kneighbors_regressor_double.onnx)
      Python  Mean Absolute Error: 1.7414210057117578
      MQL5:   Mean Absolute Error: 1.7414210057117601

      ONNX float MAE의 정확도: 소수점 이하 5자리, 정확도 ONNX 더블 MAE: 소수점 이하 13자리.


      2.1.27.3. kneighbors_regressor_float.onnx 및 kneighbors_regressor_double.onnx의 ONNX 표현


      Fig.89. 네트론에서 kneighbors_regressor_float.onnx의 ONNX 표현

      그림 89. 네트론에서 kneighbors_regressor_float.onnx의 ONNX 표현


      Fig.90. Netron에서 kneighbors_regressor_double.onnx의 ONNX 표현

      그림 90. 네트론에서 kneighbors_regressor_double.onnx의 ONNX 표현



      2.1.28. sklearn.gaussian_process.GaussianProcessRegressor

      GaussianProcessRegressor은 예측의 불확실성을 모델링할 수 있는 회귀 작업에 사용되는 머신 러닝 메서드입니다.

      가우스 프로세스(GP)는 베이지안 머신러닝의 강력한 도구로 복잡한 함수를 모델링하고 불확실성을 고려하면서 목표 변수 값을 예측하는 데 사용됩니다.

      GaussianProcessRegressor의 작동 방식:

      1. 데이터 입력: 피처(독립 변수) 및 대상 변수의 해당 값을 포함한 초기 데이터 집합으로 시작합니다.
      2. 가우스 프로세스 모델링: 가우스 프로세스는 가우스(정규) 분포로 설명되는 무작위 변수의 집합인 가우스 프로세스를 사용합니다. GP는 각 데이터 포인트의 평균값뿐만 아니라 이러한 포인트 간의 공분산(또는 유사성)도 모델링합니다.
      3. 공분산 함수 선택하기: GP의 중요한 측면은 데이터 포인트 간의 상호 연결성 및 강도를 결정하는 공분산 함수(또는 커널)을 선택핳는 것입니다. 데이터와 작업의 특성에 따라 다양한 공분산 함수가 사용될 수 있습니다.
      4. 모델 훈련: GaussianProcessRegressor는 학습 데이터를 사용하여 GP를 학습시킵니다. 학습하는 동안 모델은 공분산 함수의 매개 변수를 조정하고 예측의 불확실성을 평가합니다.
      5. 예측: 학습 후에는 이 모델을 사용하여 새로운 데이터의 목표 변수 값을 예측할 수 있습니다. GP의 중요한 기능은 평균값뿐만 아니라 예측의 신뢰 수준을 추정하는 신뢰 구간도 예측한다는 점입니다.

      GaussianProcessRegressor의 장점:

      • 불확실성 모델링: GP를 사용하면 예측의 불확실성을 고려할 수 있으므로 예측 값의 신뢰도를 파악하는 것이 중요한 작업에 유용합니다.
      • 유연성: GP는 다양한 함수를 모델링할 수 있으며 공분산 함수를 다양한 데이터 유형에 맞게 조정할 수 있습니다.
      • 하이퍼파라미터가 거의 없습니다: GP는 하이퍼파라미터 수가 상대적으로 적어 모델 튜닝을 간소화합니다.

      GaussianProcessRegressor의 한계:

      • 계산의 복잡성: GP는 특히 대량의 데이터를 처리하는 경우 계산 비용이 많이 들 수 있습니다.
      • 고차원 공간에서의 비효율성: GP는 차원성의 저주로 인해 많은 피처가 있는 작업에서 효율성이 떨어질 수 있습니다.

      GaussianProcessRegressor는 불확실성을 모델링하고 신뢰할 수 있는 예측을 제공하는 것이 중요한 회귀 작업에 유용합니다. 이 메서드는 베이지안 머신 러닝과 메타 분석에 자주 사용됩니다.


      2.1.28.1. GaussianProcessRegressor 모델을 생성하고 float 및 double용 ONNX로 내보내기 위한 코드

      이 코드는 sklearn.gaussian_process.GaussianProcessRegressor 모델을 생성하고 합성 데이터로 학습시키고 모델을 ONNX 형식으로 저장하고 플로트 및 더블 입력 데이터를 모두 사용하여 예측을 수행합니다. 또한 원본 모델과 ONNX로 내보낸 모델 모두의 정확도를 평가합니다.

      # GaussianProcessRegressor.py
      # 이 코드는 sklearn.gaussian_process.GaussianProcessRegressor 모델을 학습하고 이를 ONNX 형식(float 및 double 모두)으로 내보내고 ONNX 모델을 사용하여 예측하는 과정을 보여 줍니다.
      # Copyright 2023, MetaQuotes Ltd.
      # https://www.mql5.com


      # 소수점 이하 자릿수를 비교학 위한 함수
      def compare_decimal_places(value1, value2):
      # 두 값을 모두 문자열로 변환
          str_value1 = str(value1)
          str_value2 = str(value2)

          #문자열에서 소수점 위치 찾기

          dot_position1 = str_value1.find(".")
          dot_position2 = str_value2.find(".")

      # 값 중 소수점이 없는 경우 0을 반환합니다.
          dot_position1 == -1 또는 dot_position2 ==-1:
              return 0

          # 소수점 이하 자릿수 계산
          decimal_places1 = len(str_value1) - dot_position1 - 1
          decimal_places2 = len(str_value2) - dot_position2 - 1

          # 소수점 이하 두 자리 수 중 최소값 찾기
          min_decimal_places = min(decimal_places1, decimal_places2)

          # 소수점 이하 자릿수가 일치하도록 카운트를 초기화
          matching_count = 0

          # 소수점 이하 문자 비교
          for i in range(1, min_decimal_places + 1):
              if str_value1[dot_position1 + i] == str_value2[dot_position2 + i]:
                  matching_count += 1
              else:
                  break

          return matching_count

      # 필요한 라이브러리 가져오기
      import numpy as np
      import matplotlib.pyplot as plt
      from sklearn.gaussian_process import GaussianProcessRegressor
      에서 sklearn.gaussian_process.kernels import RBF
      from sklearn.metrics import r2_score,mean_absolute_error,mean_squared_error
      import onnx
      import onnxruntime as ort
      from skl2onnx import convert_sklearn
      from skl2onnx.common.data_types import FloatTensorType
      from skl2onnx.common.data_types import DoubleTensorType
      from sys import argv

      # 모델 저장 경로를 정의합니다.
      data_path = argv[0]
      last_index = data_path.rfind("\\")+ 1
      data_path = data_path[0:last_index]

      # 회귀를 위한 합성 데이터 생성
      X = np.arange(0,100,1).reshape(-1,1)
      y = 4*X + 10*np.sin(X*0.5)

      model_name = "GaussianProcessRegressor"
      onnx_model_filename = data_path + "gaussian_process_regressor"

      #GaussianProcessRegressor 모델을 생성합니다.
      kernel = 1.0 * RBF()
      gp_model = GaussianProcessRegressor(kernel=kernel, n_restarts_optimizer=10)

      # 데이터에 모델 맞추기
      gp_model.fit(X, y)

      # 전체 데이터 집합에 대한 값 예측
      y_pred = gp_model.predict(X, return_std=False)

      # 모델의 성능 평가
      r2 = r2_score(y, y_pred)
      MSE = MEAN_SQUARE_ERROR(Y, Y_PED)
      MAE = MEAN_ABSOLUTE_ERROR(Y, Y_PED)

      print("\n"+model_name+" Original model (double)")
      print("R-squared (Coefficient of determination):", r2)
      print("Mean Absolute Error:", mae)
      print("Mean Squared Error:", mse)

      # ONNX 모델로 변환 (float)
      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.

      initial_type_float = [('float_input', FloatTensorType([None, X.shape[1]])]]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_float = convert_sklearn(gp_model, initial_types=initial_type_float, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_float.onnx"
      onnx.save_model(onnx_model_float, onnx_filename)

      print("\n"+model_name+" ONNX model (float)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.
      initial_type_float = X.astype(np.float32)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_float = onnx_session.run([output_name], {input_name: initial_type_float})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_float = r2_score(y, y_pred_onnx_float)
      mse_onnx_float = mean_squared_error(y, y_pred_onnx_float)
      mae_onnx_float = mean_absolute_error(y, y_pred_onnx_float)
      print("R-squared (Coefficient of determination)", r2_onnx_float)
      print("Mean Absolute Error:", mae_onnx_float)
      print("Mean Squared Error:", mse_onnx_float)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_float))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_float))
      print("ONNX: MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_float))
      print("float ONNX model precision: ",compare_decimal_places(mae, mae_onnx_float))

      # 그림 크기 설정
      plt.figure(figsize=(8,5))
      # 원본 데이터와 회귀선을 그림으로 표시합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with float ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_float.png')

      # ONNX 모델로 변환 (double)
      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.

      initial_type_double = [('double_input', DoubleTensorType([None, X.shape[1]]))]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_double = convert_sklearn(gp_model, initial_types=initial_type_double, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_double.onnx"
      onnx.save_model(onnx_model_double, onnx_filename)

      print("\n"+model_name+" ONNX model (double)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.
      initial_type_double = X.astype(np.float64)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_double = onnx_session.run([output_name], {input_name: initial_type_double})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_double = r2_score(y, y_pred_onnx_double)
      mse_onnx_double = mean_squared_error(y, y_pred_onnx_double)
      mae_onnx_double = mean_absolute_error(y, y_pred_onnx_double)
      print("R-squared (Coefficient of determination)", r2_onnx_double)
      print("Mean Absolute Error:", mae_onnx_double)
      print("Mean Squared Error:", mse_onnx_double)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_double))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_double))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_double))
      print("double ONNX model precision: ",compare_decimal_places(mae, mae_onnx_double))

      # 그림 크기 설정
      plt.figure(figsize=(8,5))
      # 원본 데이터와 회귀선을 그림으로 표시합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with double ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_double.png')

      출력:

      Python  GaussianProcessRegressor Original model (double)
      Python  R-squared (Coefficient of determination): 1.0
      Python  Mean Absolute Error: 3.504041501400934e-13
      Python  Mean Squared Error: 1.6396606443650807e-25
      Python  
      Python  GaussianProcessRegressor ONNX model (float)
      Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\gaussian_process_regressor_float.onnx
      Python  Information about input tensors in ONNX:
      Python  1. Name: float_input, Data Type: tensor(float), Shape: [None, 1]
      Python  Information about output tensors in ONNX:
      Python  1. Name: GPmean, Data Type: tensor(float), Shape: [None, 1]
      Python  R-squared (Coefficient of determination) 0.9999999999999936
      Python  Mean Absolute Error: 6.454076974495848e-06
      Python  Mean Squared Error: 8.493606782250733e-11
      Python  R^2 matching decimal places:  0
      Python  MAE matching decimal places:  0
      Python  MSE matching decimal places:  0
      Python  float ONNX model precision:  0
      Python  
      Python  GaussianProcessRegressor ONNX model (double)
      Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\gaussian_process_regressor_double.onnx
      Python  Information about input tensors in ONNX:
      Python  1. Name: double_input, Data Type: tensor(double), Shape: [None, 1]
      Python  Information about output tensors in ONNX:
      Python  1. Name: GPmean, Data Type: tensor(double), Shape: [None, 1]
      Python  R-squared (Coefficient of determination) 1.0
      Python  Mean Absolute Error: 3.504041501400934e-13
      Python  Mean Squared Error: 1.6396606443650807e-25
      Python  R^2 matching decimal places:  1
      Python  MAE matching decimal places:  19
      Python  MSE matching decimal places:  20
      Python  double ONNX model precision:  19
      

      Fig.91. GaussianProcessRegressor.py의 결과 (float ONNX)

      그림 91. GaussianProcessRegressor.py의 결과(float ONNX)


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

      이 코드는 저장된 gaussian_process_regressor_float.onnxgaussian_process_regressor_double.onnx 모델을 실행하고 MQL5에서 회귀 메트릭을 사용하는 것을 보여 줍니다.

      //+------------------------------------------------------------------+
      //|                                     GaussianProcessRegressor.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"
      
      #define   ModelName          "GaussianProcessRegressor"
      #define   ONNXFilenameFloat  "gaussian_process_regressor_float.onnx"
      #define   ONNXFilenameDouble "gaussian_process_regressor_double.onnx"
      
      #resource ONNXFilenameFloat  as const uchar ExtModelFloat[];
      #resource ONNXFilenameDouble as const uchar ExtModelDouble[];
      
      #define   TestFloatModel  1
      #define   TestDoubleModel 2
      
      //+------------------------------------------------------------------+
      //| Calculate regression using float values                          |
      //+------------------------------------------------------------------+
      bool RunModelFloat(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         float input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=(float)input_vector[k];
      //--- prepare output tensor
         float output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      //+------------------------------------------------------------------+
      //| Calculate regression using double values                         |
      //+------------------------------------------------------------------+
      bool RunModelDouble(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         double input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=input_vector[k];
      //--- prepare output tensor
         double output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      
      //+------------------------------------------------------------------+
      //| Generate synthetic data                                          |
      //+------------------------------------------------------------------+
      bool GenerateData(const int n,vector &x,vector &y)
        {
         if(n<=0)
            return(false);
      //--- prepare arrays
         x.Resize(n);
         y.Resize(n);
      //---
         for(int i=0; i<n; i++)
           {
            x[i]=(double)1.0*i;
            y[i]=(double)(4*x[i] + 10*sin(x[i]*0.5));
           }
      //---
         return(true);
        }
      
      //+------------------------------------------------------------------+
      //| TestRegressionModel                                              |
      //+------------------------------------------------------------------+
      bool TestRegressionModel(const string model_name,const int model_type)
        {
      //---
         long  model=INVALID_HANDLE;
         ulong flags=ONNX_DEFAULT;
      
         if(model_type==TestFloatModel)
           {
            PrintFormat("\nTesting ONNX float: %s (%s)",model_name,ONNXFilenameFloat);
            model=OnnxCreateFromBuffer(ExtModelFloat,flags);
           }
         else
            if(model_type==TestDoubleModel)
              {
               PrintFormat("\nTesting ONNX double: %s (%s)",model_name,ONNXFilenameDouble);
               model=OnnxCreateFromBuffer(ExtModelDouble,flags);
              }
            else
              {
               PrintFormat("Model type is not incorrect.");
               return(false);
              }
      //--- check
         if(model==INVALID_HANDLE)
           {
            PrintFormat("model_name=%s OnnxCreate error %d",model_name,GetLastError());
            return(false);
           }
      //---
         vector x_values= {};
         vector y_true= {};
         vector y_predicted= {};
      //---
         int n=100;
         GenerateData(n,x_values,y_true);
      //---
         bool run_result=false;
         if(model_type==TestFloatModel)
           {
            run_result=RunModelFloat(model,x_values,y_predicted);
           }
         else
            if(model_type==TestDoubleModel)
              {
               run_result=RunModelDouble(model,x_values,y_predicted);
              }
      //---
         if(run_result)
           {
            PrintFormat("MQL5:   R-Squared (Coefficient of determination): %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_R2));
            PrintFormat("MQL5:   Mean Absolute Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MAE));
            PrintFormat("MQL5:   Mean Squared Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MSE));
           }
         else
            PrintFormat("Error %d",GetLastError());
      //--- release model
         OnnxRelease(model);
      //---
         return(true);
        }
      //+------------------------------------------------------------------+
      //| Script program start function                                    |
      //+------------------------------------------------------------------+
      int OnStart(void)
        {
      //--- test ONNX regression model for float
         TestRegressionModel(ModelName,TestFloatModel);
      //--- test ONNX regression model for double
         TestRegressionModel(ModelName,TestDoubleModel);
      //---
         return(0);
        }
      //+------------------------------------------------------------------+

      출력:

      GaussianProcessRegressor (EURUSD,H1)    Testing ONNX float: GaussianProcessRegressor (gaussian_process_regressor_float.onnx)
      GaussianProcessRegressor (EURUSD,H1)    MQL5:   R-Squared (Coefficient of determination): 0.9999999999999936
      GaussianProcessRegressor (EURUSD,H1)    MQL5:   Mean Absolute Error: 0.0000064540769745
      GaussianProcessRegressor (EURUSD,H1)    MQL5:   Mean Squared Error: 0.0000000000849361
      GaussianProcessRegressor (EURUSD,H1)    
      GaussianProcessRegressor (EURUSD,H1)    Testing ONNX double: GaussianProcessRegressor (gaussian_process_regressor_double.onnx)
      GaussianProcessRegressor (EURUSD,H1)    MQL5:   R-Squared (Coefficient of determination): 1.0000000000000000
      GaussianProcessRegressor (EURUSD,H1)    MQL5:   Mean Absolute Error: 0.0000000000003504
      GaussianProcessRegressor (EURUSD,H1)    MQL5:   Mean Squared Error: 0.0000000000000000
      



      2.1.28.3. gaussian_process_regressor_float.onnx와 gaussian_process_regressor_double.onnx의 ONNX 표현


      그림.92. ONNX representation of the gaussian_process_regressor_float.onnx in Netron

      그림 92. 네트론에서 gaussian_process_regressor_float.onnx의 ONNX 표현


      그림.93. ONNX representation of the gaussian_process_regressor_double.onnx in Netron

      그림 93. gaussian_process_regressor_double.onnx의 ONNX 표현




      2.1.29. sklearn.linear_model.GammaRegressor

      GammaRegressor은 대상 변수가 감마 분포를 따르는 회귀 작업을 위해 설계된 머신러닝 메서드입니다.

      감마 분포는 양의 연속적인 확률 변수를 모델링하는 데 사용되는 확률 분포입니다. 이 메서드를 사용하면 비용, 시간 또는 비율과 같은 양수 값을 모델링하고 예측할 수 있습니다.

      GammaRegressor의 작동 방식:

      1. 데이터 입력: 감마 분포를 따르는 피처(독립 변수)와 목표 변수의 해당 값이 있는 초기 데이터 세트부터 시작합니다.
      2. 손실 함수 선택: GammaRegressor는 감마 분포에 해당하는 손실 함수를 사용하며 이 분포의 특성을 고려합니다. 이를 통해 감마 분포의 비음수성과 오른쪽 기울기를 고려하면서 데이터를 모델링할 수 있습니다.
      3. 모델 훈련: 모델은 선택한 손실 함수를 사용하여 데이터에 대해 학습됩니다. 학습 중에 손실 함수를 최소화하기 위해 모델의 매개변수를 조정합니다.
      4. 예측: 학습 후에는 모델을 사용하여 새로운 데이터에 대한 목표 변수의 값을 예측할 수 있습니다.

      GammaRegressor의 장점:

      • 양수 값 모델링: 이 메서드는 양수 값을 모델링하기 위해 특별히 고안된 것으로, 대상 변수가 하한값인 작업에 유용할 수 있습니다.
      • 감마 분포 형태 고려: GammaRegressor는 감마 분포의 특성을 고려하여 이 분포를 따르는 데이터를 보다 정확하게 모델링할 수 있습니다.
      • 계량경제학 및 의학 연구에서의 유용성: 감마 분포는 계량경제학 및 의학 연구에서 비용, 대기 시간 및 기타 양의 확률 변수를 모델링하는 데 자주 사용됩니다.

      GammaRegressor의 한계:

      • 데이터 유형 제한: 이 메서드는 대상 변수가 감마 분포 또는 유사한 분포를 따르는 회귀 작업에만 적합합니다. 이러한 분포를 따르지 않는 데이터의 경우 이 메서드는 효과적이지 않을 수 있습니다.
      • 손실 함수를 선택해야 합니다: 적절한 손실 함수를 선택하려면 대상 변수의 분포와 그 특성에 대한 지식이 필요할 수 있습니다.

      GammaRegressor는 감마 분포에 맞는 양수 값을 모델링하고 예측해야 하는 작업에 유용합니다.


      2.1.29.1. GammaRegressor 모델을 생성하고 float 및 double용 ONNX로 내보내기 위한 코드

      이 코드는 sklearn.linear_model.GammaRegressor 모델을 생성하고 합성 데이터로 학습시키고 모델을 ONNX 형식으로 저장하고 플로트 및 더블 입력 데이터를 모두 사용하여 예측을 수행합니다. 또한 원본 모델과 ONNX로 내보낸 모델 모두의 정확도를 평가합니다.

      # GammaRegressor.py
      # 이 코드는 GammaRegressor 모델을 학습하고 이를 ONNX 형식(float 및 double 모두)으로 내보내고 ONNX 모델을 사용하여 예측하는 과정을 보여줍니다.
      # Copyright 2023, MetaQuotes Ltd.
      # https://www.mql5.com


      # 소수점 이하 자릿수를 비교학 위한 함수
      def compare_decimal_places(value1, value2):
      # 두 값을 모두 문자열로 변환
          str_value1 = str(value1)
          str_value2 = str(value2)

      # 문자열에서 소수점 위치 찾기
          dot_position1 = str_value1.find(".")
          dot_position2 = str_value2.find(".")

      # 값 중 소수점이 없는 경우 0을 반환합니다.
          if dot_position1 == -1 or dot_position2 == -1:
              return 0

          # 소수점 이하 자릿수 계산
          decimal_places1 = len(str_value1) - dot_position1 - 1
          decimal_places2 = len(str_value2) - dot_position2 - 1

          # 소수점 이하 두 자리 수 중 최소값 찾기
          min_decimal_places = min(decimal_places1, decimal_places2)

          # 소수점 이하 자릿수가 일치하도록 카운트를 초기화
          matching_count = 0

          # 소수점 이하 문자 비교
          for i in range(1, min_decimal_places + 1):
              if str_value1[dot_position1 + i] == str_value2[dot_position2 + i]:
                  matching_count += 1
              else:
                  break

          return matching_count

      # 필요한 라이브러리 가져오기
      import numpy as np
      import matplotlib.pyplot as plt
      from sklearn.linear_model import GammaRegressor
      from sklearn.metrics import r2_score,mean_absolute_error,mean_squared_error
      import onnx
      import onnxruntime as ort
      from skl2onnx import convert_sklearn
      from skl2onnx.common.data_types import FloatTensorType
      from skl2onnx.common.data_types import DoubleTensorType
      from sys import argv

      # 모델 저장 경로를 정의합니다.
      data_path = argv[0]
      last_index = data_path.rfind("\\")+ 1
      data_path = data_path[0:last_index]

      # 회귀를 위한 합성 데이터 생성
      X = np.arange(0,100,1).reshape(-1,1)
      y = 10+4*X + 10*np.sin(X*0.5)

      model_name = "GammaRegressor"
      onnx_model_filename = data_path + "gamma_regressor"

      # Gamma Regressor 모델 만들기
      regression_model = GammaRegressor()

      # 데이터에 모델 맞추기
      regression_model.fit(X, y.ravel())

      # 전체 데이터 집합에 대한 값 예측
      y_pred = regression_model.predict(X)

      # 모델의 성능 평가
      r2 = r2_score(y, y_pred)
      MSE = MEAN_SQUARE_ERROR(Y, Y_PED)
      MAE = MEAN_ABSOLUTE_ERROR(Y, Y_PED)

      print("\n"+model_name+" Original model (double)")
      print("R-squared (Coefficient of determination):", r2)
      print("Mean Absolute Error:", mae)
      print("Mean Squared Error:", mse)

      # ONNX 모델로 변환 (float)
      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.

      initial_type_float = [('float_input', FloatTensorType([None, X.shape[1]]))]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_float = convert_sklearn(regression_model, initial_types=initial_type_float, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_float.onnx"
      onnx.save_model(onnx_model_float, onnx_filename)

      print("\n"+model_name+" ONNX model (float)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.
      initial_type_float = X.astype(np.float32)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_float = onnx_session.run([output_name], {input_name: initial_type_float})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_float = r2_score(y, y_pred_onnx_float)
      mse_onnx_float = mean_squared_error(y, y_pred_onnx_float)
      mae_onnx_float = mean_absolute_error(y, y_pred_onnx_float)
      print("R-squared (Coefficient of determination)", r2_onnx_float)
      print("Mean Absolute Error:", mae_onnx_float)
      print("Mean Squared Error:", mse_onnx_float)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_float))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_float))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_float))
      print("float ONNX model precision: ",compare_decimal_places(mae, mae_onnx_float))

      # 그림 크기 설정
      plt.figure(figsize=(8,5))
      # 원본 데이터와 회귀선을 그림으로 표시합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with float ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_float.png')

      # ONNX 모델로 변환 (double)
      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.

      initial_type_double = [('double_input', DoubleTensorType([None, X.shape[1]]))]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_double = convert_sklearn(regression_model, initial_types=initial_type_double, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_double.onnx"
      onnx.save_model(onnx_model_double, onnx_filename)

      print("\n"+model_name+" ONNX model (double)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.
      initial_type_double = X.astype(np.float64)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_double = onnx_session.run([output_name], {input_name: initial_type_double})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_double = r2_score(y, y_pred_onnx_double)
      mse_onnx_double = mean_squared_error(y, y_pred_onnx_double)
      mae_onnx_double = mean_absolute_error(y, y_pred_onnx_double)
      print("R-squared (Coefficient of determination)", r2_onnx_double)
      print("Mean Absolute Error:", mae_onnx_double)
      print("Mean Squared Error:", mse_onnx_double)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_double))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_double))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_double))
      print("double ONNX model precision: ",compare_decimal_places(mae, mae_onnx_double))

      # 그림 크기 설정
      plt.figure(figsize=(8,5))
      # 원본 데이터와 회귀선을 그림으로 표시합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with double ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_double.png')

      출력:

      Python  GammaRegressor Original model (double)
      Python  R-squared (Coefficient of determination): 0.7963797339354436
      Python  Mean Absolute Error: 37.266200319422815
      Python  Mean Squared Error: 2694.457784927322
      Python  
      Python  GammaRegressor ONNX model (float)
      Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\gamma_regressor_float.onnx
      Python  Information about input tensors in ONNX:
      Python  1. Name: float_input, Data Type: tensor(float), Shape: [None, 1]
      Python  Information about output tensors in ONNX:
      Python  1. Name: variable, Data Type: tensor(float), Shape: [None, 1]
      Python  R-squared (Coefficient of determination) 0.7963795030042045
      Python  Mean Absolute Error: 37.266211754095956
      Python  Mean Squared Error: 2694.4608407846144
      Python  R^2 matching decimal places:  6
      Python  MAE matching decimal places:  4
      Python  MSE matching decimal places:  1
      Python  float ONNX model precision:  4
      Python  
      Python  GammaRegressor ONNX model (double)
      Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\gamma_regressor_double.onnx
      Python  Information about input tensors in ONNX:
      Python  1. Name: double_input, Data Type: tensor(double), Shape: [None, 1]
      Python  Information about output tensors in ONNX:
      Python  1. Name: variable, Data Type: tensor(double), Shape: [None, 1]
      Python  R-squared (Coefficient of determination) 0.7963797339354436
      Python  Mean Absolute Error: 37.266200319422815
      Python  Mean Squared Error: 2694.457784927322
      Python  R^2 matching decimal places:  16
      Python  MAE matching decimal places:  15
      Python  MSE matching decimal places:  12
      Python  double ONNX model precision:  15
      

      Fig.94. GammaRegressor.py의 결과 (float ONNX)

      그림 94. GammaRegressor.py의 결과(float ONNX)


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

      이 코드는 저장된 gamma_regressor_float.onnxgamma_regressor_double.onnx 모델을 실행하고 MQL5에서 회귀 메트릭을 사용하는 것을 보여줍니다.

      //+------------------------------------------------------------------+
      //|                                               GammaRegressor.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"
      
      #define   ModelName          "GammaRegressor"
      #define   ONNXFilenameFloat  "gamma_regressor_float.onnx"
      #define   ONNXFilenameDouble "gamma_regressor_double.onnx"
      
      #resource ONNXFilenameFloat  as const uchar ExtModelFloat[];
      #resource ONNXFilenameDouble as const uchar ExtModelDouble[];
      
      #define   TestFloatModel  1
      #define   TestDoubleModel 2
      
      //+------------------------------------------------------------------+
      //| Calculate regression using float values                          |
      //+------------------------------------------------------------------+
      bool RunModelFloat(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         float input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=(float)input_vector[k];
      //--- prepare output tensor
         float output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      //+------------------------------------------------------------------+
      //| Calculate regression using double values                         |
      //+------------------------------------------------------------------+
      bool RunModelDouble(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         double input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=input_vector[k];
      //--- prepare output tensor
         double output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      
      //+------------------------------------------------------------------+
      //| Generate synthetic data                                          |
      //+------------------------------------------------------------------+
      bool GenerateData(const int n,vector &x,vector &y)
        {
         if(n<=0)
            return(false);
      //--- prepare arrays
         x.Resize(n);
         y.Resize(n);
      //---
         for(int i=0; i<n; i++)
           {
            x[i]=(double)1.0*i;
            y[i]=(double)(10+4*x[i] + 10*sin(x[i]*0.5));
           }
      //---
         return(true);
        }
      
      //+------------------------------------------------------------------+
      //| TestRegressionModel                                              |
      //+------------------------------------------------------------------+
      bool TestRegressionModel(const string model_name,const int model_type)
        {
      //---
         long  model=INVALID_HANDLE;
         ulong flags=ONNX_DEFAULT;
      
         if(model_type==TestFloatModel)
           {
            PrintFormat("\nTesting ONNX float: %s (%s)",model_name,ONNXFilenameFloat);
            model=OnnxCreateFromBuffer(ExtModelFloat,flags);
           }
         else
            if(model_type==TestDoubleModel)
              {
               PrintFormat("\nTesting ONNX double: %s (%s)",model_name,ONNXFilenameDouble);
               model=OnnxCreateFromBuffer(ExtModelDouble,flags);
              }
            else
              {
               PrintFormat("Model type is not incorrect.");
               return(false);
              }
      //--- check
         if(model==INVALID_HANDLE)
           {
            PrintFormat("model_name=%s OnnxCreate error %d",model_name,GetLastError());
            return(false);
           }
      //---
         vector x_values= {};
         vector y_true= {};
         vector y_predicted= {};
      //---
         int n=100;
         GenerateData(n,x_values,y_true);
      //---
         bool run_result=false;
         if(model_type==TestFloatModel)
           {
            run_result=RunModelFloat(model,x_values,y_predicted);
           }
         else
            if(model_type==TestDoubleModel)
              {
               run_result=RunModelDouble(model,x_values,y_predicted);
              }
      //---
         if(run_result)
           {
            PrintFormat("MQL5:   R-Squared (Coefficient of determination): %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_R2));
            PrintFormat("MQL5:   Mean Absolute Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MAE));
            PrintFormat("MQL5:   Mean Squared Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MSE));
           }
         else
            PrintFormat("Error %d",GetLastError());
      //--- release model
         OnnxRelease(model);
      //---
         return(true);
        }
      //+------------------------------------------------------------------+
      //| Script program start function                                    |
      //+------------------------------------------------------------------+
      int OnStart(void)
        {
      //--- test ONNX regression model for float
         TestRegressionModel(ModelName,TestFloatModel);
      //--- test ONNX regression model for double
         TestRegressionModel(ModelName,TestDoubleModel);
      //---
         return(0);
        }
      //+------------------------------------------------------------------+

      출력:

      GammaRegressor (EURUSD,H1)      Testing ONNX float: GammaRegressor (gamma_regressor_float.onnx)
      GammaRegressor (EURUSD,H1)      MQL5:   R-Squared (Coefficient of determination): 0.7963795030042045
      GammaRegressor (EURUSD,H1)      MQL5:   Mean Absolute Error: 37.2662117540959628
      GammaRegressor (EURUSD,H1)      MQL5:   Mean Squared Error: 2694.4608407846144473
      GammaRegressor (EURUSD,H1)      
      GammaRegressor (EURUSD,H1)      Testing ONNX double: GammaRegressor (gamma_regressor_double.onnx)
      GammaRegressor (EURUSD,H1)      MQL5:   R-Squared (Coefficient of determination): 0.7963797339354435
      GammaRegressor (EURUSD,H1)      MQL5:   Mean Absolute Error: 37.2662003194228220
      GammaRegressor (EURUSD,H1)      MQL5:   Mean Squared Error: 2694.4577849273218817
      

      파이썬의 원래 배정밀도 모델과 비교:

      Testing ONNX float: GammaRegressor (gamma_regressor_float.onnx)
      Python  Mean Absolute Error: 37.266200319422815
      MQL5:   Mean Absolute Error: 37.2662117540959628
            
      Testing ONNX double: GammaRegressor (gamma_regressor_double.onnx)
      Python  Mean Absolute Error: 37.266200319422815
      MQL5:   Mean Absolute Error: 37.2662003194228220

      ONNX float MAE의 정확도: 소수점 이하 4자리, ONNX double MAE의 정확도: 소수점 이하 13자리.


      2.1.29.3. gamma_regressor_float.onnx 및 gamma_regressor_double.onnx의 ONNX 표현


      Fig.95. Netron에서 gamma_regressor_float.onnx의 ONNX 표현

      그림 95. 네트론에서 gamma_regressor_float.onnx의 ONNX 표현


      Fig.96. 네트론에서 gamma_regressor_double.onnx의 ONNX 표현

      그림 96. 네트론에서 gamma_regressor_double.onnx의 ONNX 표현



      2.1.30. sklearn.linear_model.SGDRegressor

      SGDRegressor은 확률적 경사 하강(SGD)을 사용하여 회귀 모델을 훈련하는 회귀 메서드입니다. 선형 모델 제품군의 일부이며 회귀 작업에 사용될 수 있습니다. SGDRegressor의 핵심 속성은 효율성과 대량의 데이터를 처리할 수 있는 기능입니다.

      SGDRegressor의 작동 방식:

      1. 선형 회귀: Ridge 및 Lasso와 마찬가지로 SGDRegressor는 회귀 문제에서 독립 변수(피처)와 목표 변수 간의 선형 관계를 찾는 것을 목표로 합니다.
      2. Stochastic Gradient Descent: SGDRegressor의 기본은 확률적 경사 하강입니다. 전체 학습 데이터 세트에서 경사를 계산하는 대신 무작위로 선택된 미니 데이터 배치를 기반으로 모델을 업데이트합니다. 이를 통해 효율적인 모델 학습과 방대한 데이터 세트 작업이 가능합니다.
      3. 정규화: SGDRegressor는 L1 및 L2 정규화(Lasso and Ridge)를 지원합니다. 이렇게 하면 과적합을 제어하고 모델 안정성을 개선하는 데 도움이 됩니다.
      4. 하이퍼파라미터: Ridge 및 Lasso와 마찬가지로 SGDRegressor를 사용하면 정규화 매개변수(α, 알파) 및 정규화 유형과 같은 하이퍼파라미터를 조정할 수 있습니다.

      SGDRegressor의 장점:

      • 효율성: SGDRegressor는 대규모 데이터 세트에서 우수한 성능을 발휘하며 광범위한 데이터에 대해 효율적으로 모델을 학습시킵니다.
      • 정규화 기능: L1 및 L2 정규화를 적용하는 옵션은 과적합 문제를 관리하는 데 적합합니다.
      • 적응형 경사 하강: 확률적 경사 하강을 사용하면 변화하는 데이터에 적응하고 모델을 즉시 학습할 수 있습니다.

      SGDRegressor의 한계:

      • 하이퍼파라미터 선택에 대한 민감도: 학습 속도 및 정규화 계수와 같은 하이퍼파라미터를 조정하려면 실험이 필요할 수 있습니다.
      • 항상 글로벌 최소값으로 수렴하는 것은 아닙니다: 경사 하강의 확률적 특성으로 인해 SGDRegressor가 항상 손실 함수의 글로벌 최소값에 수렴하는 것은 아닙니다.

      SGDRegressor는 확률적 경사 하강을 사용하여 회귀 모델을 훈련하는 회귀 메서드로 효율적이고 대규모 데이터 세트를 처리할 수 있으며 과적합을 관리하기 위한 정규화를 지원합니다.


      2.1.30.1. SGDRegressor 모델을 생성하고 float 및 double용 ONNX로 내보내기 위한 코드

      이 코드는 sklearn.linear_model.SGDRegressor 모델을 생성하고 합성 데이터로 학습시키고 모델을 ONNX 형식으로 저장하고 플로트 및 더블 입력 데이터를 모두 사용하여 예측을 수행합니다. 또한 원본 모델과 ONNX로 내보낸 모델 모두의 정확도를 평가합니다.

      # SGDRegressor2.py
      # 이 코드는 SGDRegressor 모델을 학습하고 이를 ONNX 형식(float 및 double 모두)으로 내보내고 ONNX 모델을 사용하여 예측하는 과정을 보여 줍니다.
      # Copyright 2023, MetaQuotes Ltd.
      # https://www.mql5.com


      # 소수점 이하 자릿수를 비교학 위한 함수
      def compare_decimal_places(value1, value2):
      # 두 값을 모두 문자열로 변환
          str_value1 = str(value1)
          str_value2 = str(value2)

      # 문자열에서 소수점 위치 찾기
          dot_position1 = str_value1.find(".")
          dot_position2 = str_value2.find(".")

      # 값 중 소수점이 없는 경우 0을 반환합니다.
          if dot_position1 == -1 or dot_position2 == -1:
              return 0

          # 소수점 이하 자릿수 계산
          decimal_places1 = len(str_value1) - dot_position1 - 1
          decimal_places2 = len(str_value2) - dot_position2 - 1

          # 소수점 이하 두 자리 수 중 최소값 찾기
          min_decimal_places = min(decimal_places1, decimal_places2)

          # 소수점 이하 자릿수가 일치하도록 카운트를 초기화
          matching_count = 0

          # 소수점 이하 문자 비교
          for i in range(1, min_decimal_places + 1):
              if str_value1[dot_position1 + i] == str_value2[dot_position2 + i]:
                  matching_count += 1
              else:
                  break

          return matching_count

      # 필요한 라이브러리 가져오기
      import numpy as np
      import matplotlib.pyplot as plt
      에서 sklearn.linear_model import SGDRegressor
      from sklearn.metrics import r2_score,mean_absolute_error,mean_squared_error
      import onnx
      import onnxruntime as ort
      from skl2onnx import convert_sklearn
      from skl2onnx.common.data_types import FloatTensorType
      from skl2onnx.common.data_types import DoubleTensorType
      from sys import argv

      # 모델 저장 경로를 정의합니다.
      data_path = argv[0]
      last_index = data_path.rfind("\\")+ 1
      data_path = data_path[0:last_index]

      # 회귀를 위한 합성 데이터 생성
      X = np.arange(0,10,0.1).reshape(-1,1)
      y = 4*X + np.sin(X*10)

      model_name = "SGDRegressor"
      onnx_model_filename = data_path + "sgd_regressor"

      # SGDRegressor 모델 생성
      regression_model = SGDRegressor()

      # 데이터에 모델 맞추기
      regression_model.fit(X, y.ravel())

      # 전체 데이터 집합에 대한 값 예측
      y_pred = regression_model.predict(X)

      # 모델의 성능 평가
      r2 = r2_score(y, y_pred)
      MSE = MEAN_SQUARE_ERROR(Y, Y_PED)
      MAE = MEAN_ABSOLUTE_ERROR(Y, Y_PED)

      print("\n"+model_name+" Original model (double)")
      print("R-squared (Coefficient of determination):", r2)
      print("Mean Absolute Error:", mae)
      print("Mean Squared Error:", mse)

      # ONNX 모델로 변환 (float)
      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.

      initial_type_float = [('float_input', FloatTensorType([None, X.shape[1]]))]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_float = convert_sklearn(regression_model, initial_types=initial_type_float, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_float.onnx"
      onnx.save_model(onnx_model_float, onnx_filename)

      print("\n"+model_name+" ONNX model (float)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.
      initial_type_float = X.astype(np.float32)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_float = onnx_session.run([output_name], {input_name: initial_type_float})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_float = r2_score(y, y_pred_onnx_float)
      mse_onnx_float = mean_squared_error(y, y_pred_onnx_float)
      mae_onnx_float = mean_absolute_error(y, y_pred_onnx_float)
      print("R-squared (Coefficient of determination)", r2_onnx_float)
      print("Mean Absolute Error:", mae_onnx_float)
      print("Mean Squared Error:", mse_onnx_float)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_float))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_float))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_float))
      print("float ONNX model precision: ",compare_decimal_places(mae, mae_onnx_float))

      # 그림 크기 설정
      plt.figure(figsize=(8,5))
      # 원본 데이터와 회귀선을 그림으로 표시합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with float ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_float.png')

      # ONNX 모델로 변환 (double)
      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.

      initial_type_double = [('double_input', DoubleTensorType([None, X.shape[1]]))]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_double = convert_sklearn(regression_model, initial_types=initial_type_double, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_double.onnx"
      onnx.save_model(onnx_model_double, onnx_filename)

      print("\n"+model_name+" ONNX model (double)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.
      initial_type_double = X.astype(np.float64)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_double = onnx_session.run([output_name], {input_name: initial_type_double})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_double = r2_score(y, y_pred_onnx_double)
      mse_onnx_double = mean_squared_error(y, y_pred_onnx_double)
      mae_onnx_double = mean_absolute_error(y, y_pred_onnx_double)
      print("R-squared (Coefficient of determination)", r2_onnx_double)
      print("Mean Absolute Error:", mae_onnx_double)
      print("Mean Squared Error:", mse_onnx_double)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_double))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_double))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_double))
      print("double ONNX model precision: ",compare_decimal_places(mae, mae_onnx_double))

      # 그림 크기 설정
      plt.figure(figsize=(8,5))
      # 원본 데이터와 회귀선을 그림으로 표시합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with double ONNX)')
      plt.show()
      plt.savefig(data_path + model_name+'_plot_double.png')

      출력:

      Python  SGDRegressor Original model (double)
      Python  R-squared (Coefficient of determination): 0.9961197872743282
      Python  Mean Absolute Error: 0.6405924406136998
      Python  Mean Squared Error: 0.5169867345998348
      Python  
      Python  SGDRegressor ONNX model (float)
      Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\sgd_regressor_float.onnx
      Python  Information about input tensors in ONNX:
      Python  1. Name: float_input, Data Type: tensor(float), Shape: [None, 1]
      Python  Information about output tensors in ONNX:
      Python  1. Name: variable, Data Type: tensor(float), Shape: [None, 1]
      Python  R-squared (Coefficient of determination) 0.9961197876338647
      Python  Mean Absolute Error: 0.6405924014799271
      Python  Mean Squared Error: 0.5169866866963753
      Python  R^2 matching decimal places:  9
      Python  MAE matching decimal places:  7
      Python  MSE matching decimal places:  6
      Python  float ONNX model precision:  7
      Python  
      Python  SGDRegressor ONNX model (double)
      Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\sgd_regressor_double.onnx
      Python  Information about input tensors in ONNX:
      Python  1. Name: double_input, Data Type: tensor(double), Shape: [None, 1]
      Python  Information about output tensors in ONNX:
      Python  1. Name: variable, Data Type: tensor(double), Shape: [None, 1]
      Python  R-squared (Coefficient of determination) 0.9961197872743282
      Python  Mean Absolute Error: 0.6405924406136998
      Python  Mean Squared Error: 0.5169867345998348
      Python  R^2 matching decimal places:  16
      Python  MAE matching decimal places:  16
      Python  MSE matching decimal places:  16
      Python  double ONNX model precision:  16
      


      Fig.97. SGDRegressor.py의 결과(float ONNX)

      그림 97. SGDRegressor.py의 결과(float ONNX)


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

      이 코드는 저장된 sgd_regressor_float.onnxsgd_rgressor_double.onnx 모델을 실행하고 MQL5에서 회귀 메트릭을 사용하는 것을 보여 줍니다.

      //+------------------------------------------------------------------+
      //|                                                 SGDRegressor.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"
      
      #define   ModelName          "SGDRegressor"
      #define   ONNXFilenameFloat  "sgd_regressor_float.onnx"
      #define   ONNXFilenameDouble "sgd_regressor_double.onnx"
      
      #resource ONNXFilenameFloat  as const uchar ExtModelFloat[];
      #resource ONNXFilenameDouble as const uchar ExtModelDouble[];
      
      #define   TestFloatModel  1
      #define   TestDoubleModel 2
      
      //+------------------------------------------------------------------+
      //| Calculate regression using float values                          |
      //+------------------------------------------------------------------+
      bool RunModelFloat(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         float input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=(float)input_vector[k];
      //--- prepare output tensor
         float output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      //+------------------------------------------------------------------+
      //| Calculate regression using double values                         |
      //+------------------------------------------------------------------+
      bool RunModelDouble(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         double input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=input_vector[k];
      //--- prepare output tensor
         double output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      
      //+------------------------------------------------------------------+
      //| Generate synthetic data                                          |
      //+------------------------------------------------------------------+
      bool GenerateData(const int n,vector &x,vector &y)
        {
         if(n<=0)
            return(false);
      //--- prepare arrays
         x.Resize(n);
         y.Resize(n);
      //---
         for(int i=0; i<n; i++)
           {
            x[i]=(double)1.0*i*0.1;
            y[i]=(double)(4*x[i] + sin(x[i]*10));
           }
      //---
         return(true);
        }
      
      //+------------------------------------------------------------------+
      //| TestRegressionModel                                              |
      //+------------------------------------------------------------------+
      bool TestRegressionModel(const string model_name,const int model_type)
        {
      //---
         long  model=INVALID_HANDLE;
         ulong flags=ONNX_DEFAULT;
      
         if(model_type==TestFloatModel)
           {
            PrintFormat("\nTesting ONNX float: %s (%s)",model_name,ONNXFilenameFloat);
            model=OnnxCreateFromBuffer(ExtModelFloat,flags);
           }
         else
            if(model_type==TestDoubleModel)
              {
               PrintFormat("\nTesting ONNX double: %s (%s)",model_name,ONNXFilenameDouble);
               model=OnnxCreateFromBuffer(ExtModelDouble,flags);
              }
            else
              {
               PrintFormat("Model type is not incorrect.");
               return(false);
              }
      //--- check
         if(model==INVALID_HANDLE)
           {
            PrintFormat("model_name=%s OnnxCreate error %d",model_name,GetLastError());
            return(false);
           }
      //---
         vector x_values= {};
         vector y_true= {};
         vector y_predicted= {};
      //---
         int n=100;
         GenerateData(n,x_values,y_true);
      //---
         bool run_result=false;
         if(model_type==TestFloatModel)
           {
            run_result=RunModelFloat(model,x_values,y_predicted);
           }
         else
            if(model_type==TestDoubleModel)
              {
               run_result=RunModelDouble(model,x_values,y_predicted);
              }
      //---
         if(run_result)
           {
            PrintFormat("MQL5:   R-Squared (Coefficient of determination): %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_R2));
            PrintFormat("MQL5:   Mean Absolute Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MAE));
            PrintFormat("MQL5:   Mean Squared Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MSE));
           }
         else
            PrintFormat("Error %d",GetLastError());
      //--- release model
         OnnxRelease(model);
      //---
         return(true);
        }
      //+------------------------------------------------------------------+
      //| Script program start function                                    |
      //+------------------------------------------------------------------+
      int OnStart(void)
        {
      //--- test ONNX regression model for float
         TestRegressionModel(ModelName,TestFloatModel);
      //--- test ONNX regression model for double
         TestRegressionModel(ModelName,TestDoubleModel);
      //---
         return(0);
        }
      //+------------------------------------------------------------------+

      출력:

      SGDRegressor (EURUSD,H1)        Testing ONNX float: SGDRegressor (sgd_regressor_float.onnx)
      SGDRegressor (EURUSD,H1)        MQL5:   R-Squared (Coefficient of determination): 0.9961197876338647
      SGDRegressor (EURUSD,H1)        MQL5:   Mean Absolute Error: 0.6405924014799272
      SGDRegressor (EURUSD,H1)        MQL5:   Mean Squared Error: 0.5169866866963754
      SGDRegressor (EURUSD,H1)        
      SGDRegressor (EURUSD,H1)        Testing ONNX double: SGDRegressor (sgd_regressor_double.onnx)
      SGDRegressor (EURUSD,H1)        MQL5:   R-Squared (Coefficient of determination): 0.9961197872743282
      SGDRegressor (EURUSD,H1)        MQL5:   Mean Absolute Error: 0.6405924406136998
      SGDRegressor (EURUSD,H1)        MQL5:   Mean Squared Error: 0.5169867345998348
      

      파이썬의 원래 배정밀도 모델과 비교:

      Testing ONNX float: SGDRegressor (sgd_regressor_float.onnx)
      Python  Mean Absolute Error: 0.6405924406136998
      MQL5:   Mean Absolute Error: 0.6405924014799272
              
      Testing ONNX double: SGDRegressor (sgd_regressor_double.onnx)
      Python  Mean Absolute Error: 0.6405924406136998
      MQL5:   Mean Absolute Error: 0.6405924406136998

      ONNX float MAE의 정확도: 소수점 이하 7자리, ONNX 더블 MAE의 정확도: 소수점 이하 16자리.


      2.1.30.3. sgd_regressor_float.onnx 및 sgd_regressor_double.onnx의 ONNX 표현




      Fig.98. 네트론에서 sgd_regressor_float.onnx의 ONNX 표현

      그림 98. 네트론에서 sgd_regressor_float.onnx의 ONNX 표현


      Fig.99. 네트론에서 sgd_rgressor_double.onnx의 ONNX 표현

      그림.99. 네트론에서 sgd_rgressor_double.onnx의 ONNX 표현


      2.2. 부동 소수점 정밀도 ONNX 모델로만 변환되는 Scikit-learn 라이브러리의 회귀 모델

      이 섹션에서는 플로트 정밀도로만 작동할 수 있는 모델에 대해 설명합니다. 이를 배정밀도로 ONNX로 변환하면 ONNX 연산자의 ai.onnx.ml subset의 한계와 관련된 오류가 발생합니다.


      2.2.1. sklearn.linear_model.AdaBoostRegressor

      AdaBoostRegressor - 회귀에 사용되는 머신러닝 메서드으로, 수치 값(예: 부동산 가격, 판매량 등)을 예측하는 데 사용됩니다.

      이 메서드는 처음에는 분류 작업을 위해 개발된 AdaBoost(적응형 부스팅) 알고리즘의 변형입니다.

      AdaBoostRegressor의 작동 방식:

      1. 원본 데이터 세트: 이 작업은 피처(독립 변수)과 그에 해당하는 목표 변수(예측하고자 하는 종속 변수)가 포함된 원본 데이터 세트에서 시작됩니다.
      2. 가중치 초기화: 처음에는 각 데이터 포인트(관측값)의 가중치가 동일하며 이 가중치가 적용된 데이터 집합을 기반으로 모델이 구축됩니다.
      3. 취약한 학습자 교육: AdaBoostRegressor는 대상 변수를 예측하는 여러 약한 회귀 모델(예: 의사 결정 트리)을 구성합니다. 이러한 모델을 "약한 학습자"라고 합니다. 각각의 약한 학습자는 각 관찰의 가중치를 고려하면서 데이터에 대한 학습을 받게 됩니다.
      4. 약한 학습자 가중치 선택: AdaBoostRegressor는 학습자가 예측에서 얼마나 잘 수행했는지에 따라 각 약한 학습자에 대한 가중치를 계산합니다. 더 정확한 예측을 하는 학습자는 더 높은 가중치를 받으며 그 반대의 경우도 마찬가지입니다.
      5. 관찰 가중치 업데이트: 관측 가중치가 업데이트되어 이전에 잘못 예측된 관측이 더 큰 가중치를 받음으로써 다음 모델에서 그 중요도가 높아집니다.
      6. 최종 예측: AdaBoostRegressor는 모든 약한 학습자의 예측을 결합하여 학습자의 성과에 따라 가중치를 할당합니다. 이렇게 하면 모델의 최종 예측이 이루어집니다.

      AdaBoostRegressor의 장점:

      • 적응력: AdaBoostRegressor는 복잡한 함수에 적응하고 비선형 관계에 더 잘 대처합니다.
      • 과적합 감소: AdaBoostRegressor는 관측 가중치 업데이트를 통해 정규화를 사용하여 과적합을 방지합니다.
      • 강력한 앙상블: 여러 개의 약한 모델을 결합하여 AdaBoostRegressor는 목표 변수를 상당히 정확하게 예측할 수 있는 강력한 모델을 만들 수 있습니다.

      AdaBoostRegressor의 한계:

      • 이상값에 대한 민감도: AdaBoostRegressor는 데이터의 이상값에 민감하여 예측 품질에 영향을 미칩니다.
      • 높은 컴퓨팅 비용: 여러 명의 약한 학습자를 구성하려면 더 많은 계산 리소스와 시간이 필요할 수 있습니다.
      • 항상 최선의 선택은 아닙니다: AdaBoostRegressor가 항상 최적의 선택은 아니며 경우에 따라 다른 회귀 메서드가 더 나은 성능을 발휘할 수도 있습니다.

      AdaBoostRegressor는 다양한 회귀 작업에 적용할 수 있는 유용한 머신 러닝 메서드로 특히 데이터에 복잡한 종속성이 포함된 상황에서 유용합니다.


      2.2.1.1. AdaBoostRegressor 모델을 생성하고 float 및 double용 ONNX로 내보내기 위한 코드

      이 코드는 sklearn.linear_model.AdaBoostRegressor 모델을 생성하고 합성 데이터로 학습시키고 모델을 ONNX 형식으로 저장하고 플로트 및 더블 입력 데이터를 모두 사용하여 예측을 수행합니다. 또한 원본 모델과 ONNX로 내보낸 모델 모두의 정확도를 평가합니다.

      # AdaBoostRegressor.py
      # 이 코드는 AdaBoostRegressor 모델을 학습하고 이를 ONNX 형식(float 및 double 모두)으로 내보내고 ONNX 모델을 사용하여 예측하는 과정을 보여 줍니다.
      # Copyright 2023, MetaQuotes Ltd.
      # https://www.mql5.com


      # 소수점 이하 자릿수를 비교학 위한 함수
      def compare_decimal_places(value1, value2):
      # 두 값을 모두 문자열로 변환
          str_value1 = str(value1)
          str_value2 = str(value2)

      # 문자열에서 소수점 위치 찾기
          dot_position1 = str_value1.find(".")
          dot_position2 = str_value2.find(".")

      # 값 중 소수점이 없는 경우 0을 반환합니다.
          if dot_position1 == -1 or dot_position2 == -1:
              return 0

          # 소수점 이하 자릿수 계산
          decimal_places1 = len(str_value1) - dot_position1 - 1
          decimal_places2 = len(str_value2) - dot_position2 - 1

          # 소수점 이하 두 자리 수 중 최소값 찾기
          min_decimal_places = min(decimal_places1, decimal_places2)

          # 소수점 이하 자릿수가 일치하도록 카운트를 초기화
          matching_count = 0

          # 소수점 이하 문자 비교
          for i in range(1, min_decimal_places + 1):
              if str_value1[dot_position1 + i] == str_value2[dot_position2 + i]:
                  matching_count += 1
              else:
                  break

          return matching_count

      # 필요한 라이브러리 가져오기
      import numpy as np
      import matplotlib.pyplot as plt
      from sklearn.ensemble import AdaBoostRegressor
      from sklearn.metrics import r2_score,mean_absolute_error,mean_squared_error
      import onnx
      import onnxruntime as ort
      from skl2onnx import convert_sklearn
      from skl2onnx.common.data_types import FloatTensorType
      from skl2onnx.common.data_types import DoubleTensorType
      from sys import argv

      # 모델 저장 경로를 정의합니다.
      data_path = argv[0]
      last_index = data_path.rfind("\\")+ 1
      data_path = data_path[0:last_index]

      # 회귀를 위한 합성 데이터 생성
      X = np.arange(0,100,1).reshape(-1,1)
      y = 4*X + 10*np.sin(X*0.5)

      model_name = "AdaBoostRegressor"
      onnx_model_filename = data_path + "adaboost_regressor"

      # AdaBoostRegressor 모델 생성
      regression_model = AdaBoostRegressor()

      # 데이터에 모델 맞추기
      regression_model.fit(X, y.ravel())

      # 전체 데이터 집합에 대한 값 예측
      y_pred = regression_model.predict(X)

      # 모델의 성능 평가
      r2 = r2_score(y, y_pred)
      MSE = MEAN_SQUARE_ERROR(Y, Y_PED)
      MAE = MEAN_ABSOLUTE_ERROR(Y, Y_PED)

      print("\n"+model_name+" Original model (double)")
      print("R-squared (Coefficient of determination):", r2)
      print("Mean Absolute Error:", mae)
      print("Mean Squared Error:", mse)

      # ONNX 모델로 변환 (float)
      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.

      initial_type_float = [('float_input', FloatTensorType([None, X.shape[1]])]]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_float = convert_sklearn(regression_model, initial_types=initial_type_float, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_float.onnx"
      onnx.save_model(onnx_model_float, onnx_filename)

      print("\n"+model_name+" ONNX model (float)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.
      initial_type_float = X.astype(np.float32)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_float = onnx_session.run([output_name], {input_name: initial_type_float})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_float = r2_score(y, y_pred_onnx_float)
      mse_onnx_float = mean_squared_error(y, y_pred_onnx_float)
      mae_onnx_float = mean_absolute_error(y, y_pred_onnx_float)
      print("R-squared (Coefficient of determination)", r2_onnx_float)
      print("Mean Absolute Error:", mae_onnx_float)
      print("Mean Squared Error:", mse_onnx_float)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_float))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_float))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_float))
      print("float ONNX model precision: ",compare_decimal_places(mae, mae_onnx_float))

      # 그림 크기 설정
      plt.figure(figsize=(8,5))
      # 원본 데이터와 회귀선을 그림으로 표시합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with float ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_float.png')

      # ONNX 모델로 변환 (double)
      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.

      initial_type_double = [('double_input', DoubleTensorType([None, X.shape[1]]))]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_double = convert_sklearn(regression_model, initial_types=initial_type_double, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_double.onnx"
      onnx.save_model(onnx_model_double, onnx_filename)

      print("\n"+model_name+" ONNX model (double)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.
      initial_type_double = X.astype(np.float64)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_double = onnx_session.run([output_name], {input_name: initial_type_double})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_double = r2_score(y, y_pred_onnx_double)
      mse_onnx_double = mean_squared_error(y, y_pred_onnx_double)
      mae_onnx_double = mean_absolute_error(y, y_pred_onnx_double)
      print("R-squared (Coefficient of determination)", r2_onnx_double)
      print("Mean Absolute Error:", mae_onnx_double)
      print("Mean Squared Error:", mse_onnx_double)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_double))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_double))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_double))
      print("double ONNX model precision: ",compare_decimal_places(mae, mae_onnx_double))

      # 그림 크기 설정
      plt.figure(figsize=(8,5))
      # 원본 데이터와 회귀선을 그림으로 표시합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with double ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_double.png')

      출력:

      Python  AdaBoostRegressor Original model (double)
      Python  R-squared (Coefficient of determination): 0.9991257208809748
      Python  Mean Absolute Error: 2.3678022748065457
      Python  Mean Squared Error: 11.569124350863143
      Python  
      Python  AdaBoostRegressor ONNX model (float)
      Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\adaboost_regressor_float.onnx
      Python  Information about input tensors in ONNX:
      Python  1. Name: float_input, Data Type: tensor(float), Shape: [None, 1]
      Python  Information about output tensors in ONNX:
      Python  1. Name: variable, Data Type: tensor(float), Shape: [None, 1]
      Python  R-squared (Coefficient of determination) 0.9991257199849699
      Python  Mean Absolute Error: 2.36780399225718
      Python  Mean Squared Error: 11.569136207480646
      Python  R^2 matching decimal places:  7
      Python  MAE matching decimal places:  5
      Python  MSE matching decimal places:  4
      Python  float ONNX model precision:  5
      Python  
      Python  AdaBoostRegressor ONNX model (double)
      Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\adaboost_regressor_double.onnx
      

      여기서 모델은 float 및 double 용 ONNX 모델로 내보냈습니다. ONNX 플로트 모델은 성공적으로 실행되었지만 더블 모델에서는 실행 오류가 발생했습니다(오류 탭의 오류):

      AdaBoostRegressor.py started    AdaBoostRegressor.py    1       1
      Traceback (most recent call last):      AdaBoostRegressor.py    1       1
          onnx_session = ort.InferenceSession(onnx_filename)  AdaBoostRegressor.py    159     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.Fail: [ONNXRuntimeError] : 1 : FAIL : Load model from C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\adaboost_regressor_double.onnx failed:Type Error:       onnxruntime_inference_collection.py     424     1
      AdaBoostRegressor.py finished in 3207 ms                5       1
      

      Fig.100. AdaBoostRegressor.py의 결과(float ONNX)

      그림.100. AdaBoostRegressor.py의 결과(float ONNX)


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

      이 코드는 저장된 adaboost_regressor_float.onnxadaboost_regressor_double.onnx 모델을 실행하고 MQL5에서 회귀 메트릭을 사용하는 것을 보여줍니다.

      //+------------------------------------------------------------------+
      //|                                            AdaBoostRegressor.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"
      
      #define   ModelName          "AdaBoostRegressor"
      #define   ONNXFilenameFloat  "adaboost_regressor_float.onnx"
      #define   ONNXFilenameDouble "adaboost_regressor_double.onnx"
      
      #resource ONNXFilenameFloat  as const uchar ExtModelFloat[];
      #resource ONNXFilenameDouble as const uchar ExtModelDouble[];
      
      #define   TestFloatModel  1
      #define   TestDoubleModel 2
      
      //+------------------------------------------------------------------+
      //| Calculate regression using float values                          |
      //+------------------------------------------------------------------+
      bool RunModelFloat(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         float input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=(float)input_vector[k];
      //--- prepare output tensor
         float output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      //+------------------------------------------------------------------+
      //| Calculate regression using double values                         |
      //+------------------------------------------------------------------+
      bool RunModelDouble(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         double input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=input_vector[k];
      //--- prepare output tensor
         double output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      
      //+------------------------------------------------------------------+
      //| Generate synthetic data                                          |
      //+------------------------------------------------------------------+
      bool GenerateData(const int n,vector &x,vector &y)
        {
         if(n<=0)
            return(false);
      //--- prepare arrays
         x.Resize(n);
         y.Resize(n);
      //---
         for(int i=0; i<n; i++)
           {
            x[i]=(double)1.0*i;
            y[i]=(double)(4*x[i] + 10*sin(x[i]*0.5));
           }
      //---
         return(true);
        }
      
      //+------------------------------------------------------------------+
      //| TestRegressionModel                                              |
      //+------------------------------------------------------------------+
      bool TestRegressionModel(const string model_name,const int model_type)
        {
      //---
         long  model=INVALID_HANDLE;
         ulong flags=ONNX_DEFAULT;
      
         if(model_type==TestFloatModel)
           {
            PrintFormat("\nTesting ONNX float: %s (%s)",model_name,ONNXFilenameFloat);
            model=OnnxCreateFromBuffer(ExtModelFloat,flags);
           }
         else
            if(model_type==TestDoubleModel)
              {
               PrintFormat("\nTesting ONNX double: %s (%s)",model_name,ONNXFilenameDouble);
               model=OnnxCreateFromBuffer(ExtModelDouble,flags);
              }
            else
              {
               PrintFormat("Model type is not incorrect.");
               return(false);
              }
      //--- check
         if(model==INVALID_HANDLE)
           {
            PrintFormat("model_name=%s OnnxCreate error %d",model_name,GetLastError());
            return(false);
           }
      //---
         vector x_values= {};
         vector y_true= {};
         vector y_predicted= {};
      //---
         int n=100;
         GenerateData(n,x_values,y_true);
      //---
         bool run_result=false;
         if(model_type==TestFloatModel)
           {
            run_result=RunModelFloat(model,x_values,y_predicted);
           }
         else
            if(model_type==TestDoubleModel)
              {
               run_result=RunModelDouble(model,x_values,y_predicted);
              }
      //---
         if(run_result)
           {
            PrintFormat("MQL5:   R-Squared (Coefficient of determination): %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_R2));
            PrintFormat("MQL5:   Mean Absolute Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MAE));
            PrintFormat("MQL5:   Mean Squared Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MSE));
           }
         else
            PrintFormat("Error %d",GetLastError());
      //--- release model
         OnnxRelease(model);
      //---
         return(true);
        }
      //+------------------------------------------------------------------+
      //| Script program start function                                    |
      //+------------------------------------------------------------------+
      int OnStart(void)
        {
      //--- test ONNX regression model for float
         TestRegressionModel(ModelName,TestFloatModel);
      //--- test ONNX regression model for double
         TestRegressionModel(ModelName,TestDoubleModel);
      //---
         return(0);
        }
      //+------------------------------------------------------------------+

      출력:

      AdaBoostRegressor (EURUSD,H1)   
      AdaBoostRegressor (EURUSD,H1)   Testing ONNX float: AdaBoostRegressor (adaboost_regressor_float.onnx)
      AdaBoostRegressor (EURUSD,H1)   MQL5:   R-Squared (Coefficient of determination): 0.9991257199849699
      AdaBoostRegressor (EURUSD,H1)   MQL5:   Mean Absolute Error: 2.3678039922571803
      AdaBoostRegressor (EURUSD,H1)   MQL5:   Mean Squared Error: 11.5691362074806463
      AdaBoostRegressor (EURUSD,H1)   
      AdaBoostRegressor (EURUSD,H1)   Testing ONNX double: AdaBoostRegressor (adaboost_regressor_double.onnx)
      AdaBoostRegressor (EURUSD,H1)   ONNX: cannot create session (OrtStatus: 1 'Type Error: Type parameter (T) of Optype (Mul) bound to different types (tensor(float) and tensor(double) in node (Mul).'), inspect code 'Scripts\Regression\AdaBoostRegressor.mq5' (133:16)
      AdaBoostRegressor (EURUSD,H1)   model_name=AdaBoostRegressor OnnxCreate error 5800
      
      ONNX 플로트 모델은 성공적으로 실행되었지만 더블 모델에서는 실행 오류가 발생했습니다.


      2.2.1.3. adaboost_regressor_float.onnx 및 adaboost_regressor_double.onnx의 ONNX 표현

      Fig.101. 네트론에서 adaboost_regressor_float.onnx의 ONNX 표현

      그림 101. 네트론에서 adaboost_regressor_float.onnx의 ONNX 표현


      Fig.102. 네트론에서 adaboost_regressor_double.onnx의 ONNX 표현

      그림 102. 네트론에서 adaboost_regressor_double.onnx의 ONNX 표현



      2.2.2. sklearn.linear_model.BaggingRegressor

      BaggingRegressor는 회귀 작업에 사용되는 머신 러닝 메서드입니다.

      여러 개의 기본 회귀 모델을 구성하고 예측을 결합하여 보다 안정적이고 정확한 결과를 얻는 '배깅'(부트스트랩 애그리게이팅) 개념을 기반으로 하는 앙상블 메서드입니다.

      BaggingRegressor의 작동 방식:

      1. 원본 데이터 세트: 피처(독립 변수)와 그에 해당하는 목표 변수(예측하고자 하는 종속 변수)가 포함된 원본 데이터 세트에서 시작합니다.
      2. 하위 집합 생성: BaggingRegressor는 원본 데이터에서 여러 개의 하위 집합(교체가 있는 샘플)을 무작위로 생성합니다. 각 하위 집합에는 원본 데이터의 무작위 관찰 집합이 포함되어 있습니다.
      3. 기본 회귀 모델 학습: 각 하위 집합에 대해 BaggingRegressor는 별도의 기본 회귀 모델(예: 의사 결정 트리, 랜덤 포레스트, 선형 회귀 모델 등)을 구축합니다.
      4. 기본 모델의 예측: 각 기본 모델은 해당 하위 집합을 기반으로 대상 변수를 예측하는 데 사용됩니다.
      5. 평균 또는 조합: BaggingRegressor는 최종 회귀 예측을 구하기 위해 모든 기본 모델의 예측을 평균하거나 결합합니다.

      BaggingRegressor의 장점:

      • 분산 감소: BaggingRegressor는 모델의 분산을 줄여 데이터의 변동에 더욱 견고하도록 합니다.
      • 과적합 감소: 모델이 다양한 데이터 하위 집합에 대해 학습되기 때문에 BaggingRegressor는 일반적으로 과적합의 위험을 줄여줍니다.
      • 일반화 기능의 개선: 여러 모델의 예측을 결합함으로써 BaggingRegressor는 일반적으로 더 정확하고 안정적인 예측을 제공합니다.
      • 다양한 기본 모델: BaggingRegressor는 다양한 유형의 기본 회귀 모델을 사용할 수 있으므로 유연한 메서드입니다.

      BaggingRegressor의 한계:

      • 기본 모델이 이미 데이터에서 우수한 성능을 발휘하는 경우 항상 성능을 향상시킬 수 있는 것은 아닙니다.
      • BaggingRegressor는 단일 모델 훈련에 비해 더 많은 계산 리소스와 시간이 필요할 수 있습니다.

      BaggingRegressor는 회귀 작업 특히 잡음이 많은 데이터와 예측 안정성 개선이 필요한 경우에 유용하게 사용할 수 있는 강력한 머신 러닝 메서드입니다.


      2.2.2.1. BaggingRegressor 모델을 생성하고 float 및 double용 ONNX로 내보내기 위한 코드

      이 코드는 sklearn.linear_model.BaggingRegressor 모델을 생성하고 합성 데이터로 학습시키고 모델을 ONNX 형식으로 저장하고 플로트 및 더블 입력 데이터를 모두 사용하여 예측을 수행합니다. 또한 원본 모델과 ONNX로 내보낸 모델 모두의 정확도를 평가합니다.

      # BaggingRegressor.py
      # 이 코드는 BaggingRegressor 모델을 학습하고 이를 ONNX 형식(float 및 double 모두)으로 내보내고 ONNX 모델을 사용하여 예측하는 과정을 보여줍니다.
      # Copyright 2023, MetaQuotes Ltd.
      # https://www.mql5.com


      # 소수점 이하 자릿수를 비교학 위한 함수
      def compare_decimal_places(value1, value2):
      # 두 값을 모두 문자열로 변환
          str_value1 = str(value1)
          str_value2 = str(value2)

      # 문자열에서 소수점 위치 찾기
          dot_position1 = str_value1.find(".")
          dot_position2 = str_value2.find(".")

      # 값 중 소수점이 없는 경우 0을 반환합니다.
          if dot_position1 == -1 or dot_position2 == -1:
              return 0

          # 소수점 이하 자릿수 계산
          decimal_places1 = len(str_value1) - dot_position1 - 1
          decimal_places2 = len(str_value2) - dot_position2 - 1

          # 소수점 이하 두 자리 수 중 최소값 찾기
          min_decimal_places = min(decimal_places1, decimal_places2)

          # 소수점 이하 자릿수가 일치하도록 카운트를 초기화
          matching_count = 0

          # 소수점 이하 문자 비교
          for i in range(1, min_decimal_places + 1):
              if str_value1[dot_position1 + i] == str_value2[dot_position2 + i]:
                  matching_count += 1
              else:
                  break

          return matching_count

      # 필요한 라이브러리 가져오기
      import numpy as np
      import matplotlib.pyplot as plt
      from sklearn.ensemble import BaggingRegressor
      from sklearn.metrics import r2_score,mean_absolute_error,mean_squared_error
      import onnx
      import onnxruntime as ort
      from skl2onnx import convert_sklearn
      from
      skl2onnx.common.data_types import FloatTensorType
      from skl2onnx.common.data_types import DoubleTensorType
      from sys import argv

      # 모델 저장 경로를 정의합니다.
      data_path = argv[0]
      last_index = data_path.rfind("\\")+ 1
      data_path = data_path[0:last_index]

      # 회귀를 위한 합성 데이터 생성
      X = np.arange(0,100,1).reshape(-1,1)
      y = 4*X + 10*np.sin(X*0.5)

      model_name = "BaggingRegressor"
      onnx_model_filename = data_path + "bagging_regressor"

      # Bagging Regressor 모델 만들기
      regression_model = BaggingRegressor()

      # 데이터에 모델 맞추기
      regression_model.fit(X, y.ravel())

      # 전체 데이터 집합에 대한 값 예측
      y_pred = regression_model.predict(X)

      # 모델의 성능 평가
      r2 = r2_score(y, y_pred)
      MSE = MEAN_SQUARE_ERROR(Y, Y_PED)
      MAE = MEAN_ABSOLUTE_ERROR(Y, Y_PED)

      print("\n"+model_name+" Original model (double)")
      print("R-squared (Coefficient of determination):", r2)
      print("Mean Absolute Error:", mae)
      print("Mean Squared Error:", mse)

      # ONNX 모델로 변환 (float)
      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.
      initial_type_float = [('float_input', FloatTensorType([None, X.shape[1]])]]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_float = convert_sklearn(regression_model, initial_types=initial_type_float, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_float.onnx"
      onnx.save_model(onnx_model_float, onnx_filename)

      print("\n"+model_name+" ONNX model (float)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.

      print("Information 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}")

      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.
      initial_type_float = X.astype(np.float32)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_float = onnx_session.run([output_name], {input_name: initial_type_loat})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_float = r2_score(y, y_pred_onnx_float)
      mse_onnx_float = mean_squared_error(y, y_pred_onnx_float)
      mae_onnx_float = mean_absolute_error(y, y_pred_onnx_float)
      print("R-squared (Coefficient of determination)", r2_onnx_float)
      print("Mean Absolute Error:", mae_onnx_float)
      print("Mean Squared Error:", mse_onnx_float)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_float))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_float))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_float))
      print("float ONNX model precision: ",compare_decimal_places(mae, mae_onnx_float))

      # 그림 크기 설정
      plt.figure(figsize=(8, 5))
      # 원본 데이터와 회귀선을 그림으로 표시합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with float ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_float.png')

      # ONNX 모델로 변환 (double)
      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.

      initial_type_double = [('double_input', DoubleTensorType([None, X.shape[1]]))]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_double = convert_sklearn(regression_model, initial_types=initial_type_double, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_double.onnx"
      onnx.save_model(onnx_model_double, onnx_filename)

      print("\n"+model_name+" ONNX model (double)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.
      initial_type_double = X.astype(np.float64)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_double = onnx_session.run([output_name], {input_name: initial_type_double})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_double = r2_score(y, y_pred_onnx_double)
      mse_onnx_double = mean_squared_error(y, y_pred_onnx_double)
      mae_onnx_double = mean_absolute_error(y, y_pred_onnx_double)
      print("R-squared (Coefficient of determination)", r2_onnx_double)
      print("Mean Absolute Error:", mae_onnx_double)
      print("Mean Squared Error:", mse_onnx_double)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_double))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_double))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_double))
      print("double ONNX model precision: ",compare_decimal_places(mae, mae_onnx_double))

      # 그림 크기 설정
      plt.figure(figsize=(8,5))
      # 원본 데이터와 회귀선을 그림으로 표시합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with double ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_double.png')

      출력:

      Python  
      Python  BaggingRegressor Original model (double)
      Python  R-squared (Coefficient of determination): 0.9998128324923137
      Python  Mean Absolute Error: 1.0257279210387649
      Python  Mean Squared Error: 2.4767424083953005
      Python  
      Python  BaggingRegressor ONNX model (float)
      Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\bagging_regressor_float.onnx
      Python  Information about input tensors in ONNX:
      Python  1. Name: float_input, Data Type: tensor(float), Shape: [None, 1]
      Python  Information about output tensors in ONNX:
      Python  1. Name: variable, Data Type: tensor(float), Shape: [None, 1]
      Python  R-squared (Coefficient of determination) 0.9998128317934672
      Python  Mean Absolute Error: 1.0257282792130034
      Python  Mean Squared Error: 2.4767516560614187
      Python  R^2 matching decimal laces:  8
      Python  MAE matching decimal places:  5
      Python  MSE matching decimal places:  4
      Python  float ONNX model precision:  5
      Python  
      Python  BaggingRegressor ONNX model (double)
      Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\bagging_regressor_double.onnx
      

      오류 탭:

      BaggingRegressor.py started     BaggingRegressor.py     1       1
      Traceback (most recent call last):      BaggingRegressor.py     1       1
          onnx_session = ort.InferenceSession(onnx_filename)  BaggingRegressor.py     161     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.Fail: [ONNXRuntimeError] : 1 : FAIL : Load model from C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\bagging_regressor_double.onnx failed:Type Error: T      onnxruntime_inference_collection.py     424     1
      BaggingRegressor.py finished in 3173 ms         5       1
      

      Fig.103. BaggingRegressor.py의 결과 (float ONNX)

      그림 103. BaggingRegressor.py의 결과(float ONNX)


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

      이 코드는 저장된 bagging_regressor_float.onnxbagging_regressor_double.onnx를 실행하고 MQL5에서 회귀 메트릭을 사용하는 것을 보여줍니다.

      //+------------------------------------------------------------------+
      //|                                             BaggingRegressor.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"
      
      #define   ModelName          "BaggingRegressor"
      #define   ONNXFilenameFloat  "bagging_regressor_float.onnx"
      #define   ONNXFilenameDouble "bagging_regressor_double.onnx"
      
      #resource ONNXFilenameFloat  as const uchar ExtModelFloat[];
      #resource ONNXFilenameDouble as const uchar ExtModelDouble[];
      
      #define   TestFloatModel  1
      #define   TestDoubleModel 2
      
      //+------------------------------------------------------------------+
      //| Calculate regression using float values                          |
      //+------------------------------------------------------------------+
      bool RunModelFloat(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         float input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=(float)input_vector[k];
      //--- prepare output tensor
         float output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      //+------------------------------------------------------------------+
      //| Calculate regression using double values                         |
      //+------------------------------------------------------------------+
      bool RunModelDouble(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         double input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=input_vector[k];
      //--- prepare output tensor
         double output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      
      //+------------------------------------------------------------------+
      //| Generate synthetic data                                          |
      //+------------------------------------------------------------------+
      bool GenerateData(const int n,vector &x,vector &y)
        {
         if(n<=0)
            return(false);
      //--- prepare arrays
         x.Resize(n);
         y.Resize(n);
      //---
         for(int i=0; i<n; i++)
           {
            x[i]=(double)1.0*i;
            y[i]=(double)(4*x[i] + 10*sin(x[i]*0.5));
           }
      //---
         return(true);
        }
      
      //+------------------------------------------------------------------+
      //| TestRegressionModel                                              |
      //+------------------------------------------------------------------+
      bool TestRegressionModel(const string model_name,const int model_type)
        {
      //---
         long  model=INVALID_HANDLE;
         ulong flags=ONNX_DEFAULT;
      
         if(model_type==TestFloatModel)
           {
            PrintFormat("\nTesting ONNX float: %s (%s)",model_name,ONNXFilenameFloat);
            model=OnnxCreateFromBuffer(ExtModelFloat,flags);
           }
         else
            if(model_type==TestDoubleModel)
              {
               PrintFormat("\nTesting ONNX double: %s (%s)",model_name,ONNXFilenameDouble);
               model=OnnxCreateFromBuffer(ExtModelDouble,flags);
              }
            else
              {
               PrintFormat("Model type is not incorrect.");
               return(false);
              }
      //--- check
         if(model==INVALID_HANDLE)
           {
            PrintFormat("model_name=%s OnnxCreate error %d",model_name,GetLastError());
            return(false);
           }
      //---
         vector x_values= {};
         vector y_true= {};
         vector y_predicted= {};
      //---
         int n=100;
         GenerateData(n,x_values,y_true);
      //---
         bool run_result=false;
         if(model_type==TestFloatModel)
           {
            run_result=RunModelFloat(model,x_values,y_predicted);
           }
         else
            if(model_type==TestDoubleModel)
              {
               run_result=RunModelDouble(model,x_values,y_predicted);
              }
      //---
         if(run_result)
           {
            PrintFormat("MQL5:   R-Squared (Coefficient of determination): %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_R2));
            PrintFormat("MQL5:   Mean Absolute Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MAE));
            PrintFormat("MQL5:   Mean Squared Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MSE));
           }
         else
            PrintFormat("Error %d",GetLastError());
      //--- release model
         OnnxRelease(model);
      //---
         return(true);
        }
      //+------------------------------------------------------------------+
      //| Script program start function                                    |
      //+------------------------------------------------------------------+
      int OnStart(void)
        {
      //--- test ONNX regression model for float
         TestRegressionModel(ModelName,TestFloatModel);
      //--- test ONNX regression model for double
         TestRegressionModel(ModelName,TestDoubleModel);
      //---
         return(0);
        }
      //+------------------------------------------------------------------+

      출력:

      BaggingRegressor (EURUSD,H1)    Testing ONNX float: BaggingRegressor (bagging_regressor_float.onnx)
      BaggingRegressor (EURUSD,H1)    MQL5:   R-Squared (Coefficient of determination): 0.9998128317934672
      BaggingRegressor (EURUSD,H1)    MQL5:   Mean Absolute Error: 1.0257282792130034
      BaggingRegressor (EURUSD,H1)    MQL5:   Mean Squared Error: 2.4767516560614196
      BaggingRegressor (EURUSD,H1)    
      BaggingRegressor (EURUSD,H1)    Testing ONNX double: BaggingRegressor (bagging_regressor_double.onnx)
      BaggingRegressor (EURUSD,H1)    ONNX: cannot create session (OrtStatus: 1 'Type Error: Type (tensor(double)) of output arg (variable) of node (ReduceMean) does not match expected type (tensor(float)).'), inspect code 'Scripts\Regression\BaggingRegressor.mq5' (133:16)
      BaggingRegressor (EURUSD,H1)    model_name=BaggingRegressor OnnxCreate error 5800
      

      플로트로 계산된 ONNX 모델은 정상적으로 실행되었지만 더블로 모델을 실행할 때 오류가 발생했습니다.


      2.2.2.3. ONNX representation of the bagging_regressor_float.onnx and bagging_regressor_double.onnx



      Fig.104. Netron에서 bagging_regressor_float.onnx의 ONNX 표현

      그림 104. 네트론에서 bagging_regressor_float.onnx의 ONNX 표현


      Fig.105. Netron에서 bagging_regressor_double.onnx의 ONNX 표현

      그림 105. 네트론에서 bagging_regressor_double.onnx의 ONNX 표현




      2.2.3. sklearn.linear_model.DecisionTreeRegressor

      DecisionTreeRegressor은 회귀 작업에 사용되는 머신러닝 메서드으로 일련의 피처(독립 변수)를 기반으로 대상 변수의 수치 값을 예측합니다.

      이 메서드는 피처 공간을 구간으로 분할하고 각 구간에 대한 목표 변수의 값을 예측하는 의사 결정 트리를 구축하는 것을 기반으로 합니다.

      DecisionTreeRegressor의 작동 원리:

      1. 건설 시작: 피처(독립 변수)와 대상 변수의 해당 값이 포함된 초기 데이터 세트부터 시작합니다.
      2. 피처 선택 및 분할: 의사 결정 트리는 데이터를 두 개 이상의 하위 그룹으로 나누는 피처와 임계값을 선택합니다. 이 분할은 각 하위 그룹 내에서 평균 제곱 오차(대상 변수의 예측값과 실제값 간의 평균 제곱 편차)를 최소화하기 위해 수행됩니다.
      3. 재귀적 구축: 각 하위 그룹에 대해 피처 선택 및 분할 프로세스가 반복되어 하위 트리가 생성됩니다. 이 프로세스는 최대 트리 깊이 또는 노드의 최소 샘플과 같은 특정 중지 기준이 충족될 때까지 재귀적으로 수행됩니다.
      4. 리프 노드: 중지 기준이 충족되면 리프 노드가 생성되어 지정된 리프 노드에 해당하는 샘플에 대한 목표 변수의 수치 값을 예측합니다.
      5. 예측: 새 데이터의 경우 의사 결정 트리가 적용되고 새 관측값이 목표 변수의 수치 값을 예측하는 리프 노드에 도달할 때까지 트리를 통과합니다.

      DecisionTreeRegressor의 장점:

      • 해석 가능성: 의사 결정 트리는 이해하기 쉽고 시각화하기 쉬우므로 모델 의사 결정을 설명하는 데 유용합니다.
      • 이상값 견고성: 의사 결정 트리는 데이터 이상값에 대해 강력할 수 있습니다.
      • 숫자 데이터와 범주형 데이터를 모두 처리합니다: 의사 결정 트리는 추가 전처리 없이 숫자 및 범주형 피처를 모두 처리할 수 있습니다.
      • 자동화된 피처 선택: 트리는 자동으로 중요한 피처를 선택하고 관련성이 낮은 피처는 무시할 수 있습니다.

      DecisionTreeRegressor의 한계:

      • 과도한 취약성: 의사 결정 트리는 특히 너무 깊을 경우 과적합이 발생하기 쉽습니다.
      • 일반화 문제: 의사 결정 트리는 학습 집합에 포함되지 않은 데이터에는 잘 일반화되지 않을 수 있습니다.
      • 항상 최적의 선택은 아닙니다: 경우에 따라 선형 회귀나 K-최근접 이웃과 같은 다른 회귀 메서드들이 더 나은 성능을 보일 수도 있습니다.

      특히 모델의 의사 결정 로직을 이해하고 프로세스를 시각화하는 것이 중요한 회귀 작업에 유용한 메서드 중 하나가 바로 DecisionTreeRegressor입니다.


      2.2.3.1. DecisionTreeRegressor 모델을 생성하고 float 및 double용 ONNX로 내보내기 위한 코드

      이 코드는 sklearn.linear_model.DecisionTreeRegressor 모델을 생성하고 합성 데이터로 학습시키고 모델을 ONNX 형식으로 저장하고 플로트 및 더블 입력 데이터를 모두 사용하여 예측을 수행합니다. 또한 원본 모델과 ONNX로 내보낸 모델 모두의 정확도를 평가합니다.

      # DecisionTreeRegressor.py
      # 이 코드는 DecisionTreeRegressor 모델을 학습하고 이를 ONNX 형식(float 및 double 모두)으로 내보내고 ONNX 모델을 사용하여 예측하는 과정을 보여 줍니다.
      # Copyright 2023, MetaQuotes Ltd.
      # https://www.mql5.com


      # 소수점 이하 자릿수를 비교학 위한 함수
      def compare_decimal_places(value1, value2):
      # 두 값을 모두 문자열로 변환
          str_value1 = str(value1)
          str_value2 = str(value2)

      # 문자열에서 소수점 위치 찾기
          dot_position1 = str_value1.find(".")
          dot_position2 = str_value2.find(".")

      # 값 중 소수점이 없는 경우 0을 반환합니다.
          if dot_position1 == -1 or dot_position2 == -1:
              return 0

          # 소수점 이하 자릿수 계산
          decimal_places1 = len(str_value1) - dot_position1 - 1
          decimal_places2 = len(str_value2) - dot_position2 - 1

          # 소수점 이하 두 자리 수 중 최소값 찾기
          min_decimal_places = min(decimal_places1, decimal_places2)

          # 소수점 이하 자릿수가 일치하도록 카운트를 초기화
          matching_count = 0

          # 소수점 이하 문자 비교
          for i in range(1, min_decimal_places + 1):
              if str_value1[dot_position1 + i] == str_value2[dot_position2 + i]:
                  matching_count += 1
              else:
                  break

          return matching_count

      # 필요한 라이브러리 가져오기
      import numpy as np
      import matplotlib.pyplot as plt
      from sklearn.tree import DecisionTreeRegressor
      from sklearn.metrics import r2_score,mean_absolute_error,mean_squared_error
      import onnx
      import onnxruntime as ort
      from skl2onnx import convert_sklearn
      from skl2onnx.common.data_types import FloatTensorType
      from skl2onnx.common.data_types import DoubleTensorType
      from sys import argv

      # 모델 저장 경로를 정의합니다.
      data_path = argv[0]
      last_index = data_path.rfind("\\")+ 1
      data_path = data_path[0:last_index]

      # 회귀를 위한 합성 데이터 생성
      X = np.arange(0,100,1).reshape(-1,1)
      y = 4*X + 10*np.sin(X*0.5)

      model_name = "DecisionTreeRegressor"
      onnx_model_filename = data_path + "decision_tree_regressor"

      # 의사 결정 트리 회귀 모델 만들기
      regression_model = DecisionTreeRegressor()

      # 데이터에 모델 맞추기
      regression_model.fit(X, y)

      # 전체 데이터 집합에 대한 값 예측
      y_pred = regression_model.predict(X)

      # 모델의 성능 평가
      r2 = r2_score(y, y_pred)
      MSE = MEAN_SQUARE_ERROR(Y, Y_PED)
      MAE = MEAN_ABSOLUTE_ERROR(Y, Y_PED)

      print("\n"+model_name+" Original model (double)")
      print("R-squared (Coefficient of determination):", r2)
      print("Mean Absolute Error:", mae)
      print("Mean Squared Error:", mse)

      # ONNX 모델로 변환 (float)
      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.
      initial_type_float = [('float_input', FloatTensorType([None, X.shape[1]])]]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_float = convert_sklearn(regression_model, initial_types=initial_type_float, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_float.onnx"
      onnx.save_model(onnx_model_float, onnx_filename)

      print("\n"+model_name+" ONNX model (float)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.
      initial_type_float = X.astype(np.float32)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_float = onnx_session.run([output_name], {input_name: initial_type_float})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_float = r2_score(y, y_pred_onnx_float)
      mse_onnx_float = mean_squared_error(y, y_pred_onnx_float)
      mae_onnx_float = mean_absolute_error(y, y_pred_onnx_float)
      print("R-squared (Coefficient of determination)", r2_onnx_float)
      print("Mean Absolute Error:", mae_onnx_float)
      print("Mean Squared Error:", mse_onnx_float)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_float))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_float))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_float))
      print("float ONNX model precision: ",compare_decimal_places(mae, mae_onnx_float))

      # 그림 크기 설정
      plt.figure(figsize=(8,5))
      # 원본 데이터와 회귀선을 그림으로 표시합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with float ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_float.png')

      # ONNX 모델로 변환 (double)
      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.

      initial_type_double = [('double_input', DoubleTensorType([None, X.shape[1]]))]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_double = convert_sklearn(regression_model, initial_types=initial_type_double, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_double.onnx"
      onnx.save_model(onnx_model_double, onnx_filename)

      print("\n"+model_name+" ONNX model (double)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.

      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.
      initial_type_double = X.astype(np.float64)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_double = onnx_session.run([output_name], {input_name: initial_type_double})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_double = r2_score(y, y_pred_onnx_double)
      mse_onnx_double = mean_squared_error(y, y_pred_onnx_double)
      mae_onnx_double = mean_absolute_error(y, y_pred_onnx_double)
      print("R-squared (Coefficient of determination)", r2_onnx_double)
      print("Mean Absolute Error:", mae_onnx_double)
      print("Mean Squared Error:", mse_onnx_double)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_double))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_double))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_double))
      print("double ONNX model precision: ",compare_decimal_places(mae, mae_onnx_double))

      # 그림 크기 설정
      plt.figure(figsize=(8,5))
      # 원본 데이터와 회귀선을 그림으로 표시합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with double ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_double.png')

      출력:

      Python  DecisionTreeRegressor Original model (double)
      Python  R-squared (Coefficient of determination): 1.0
      Python  Mean Absolute Error: 0.0
      Python  Mean Squared Error: 0.0
      Python  
      Python  DecisionTreeRegressor ONNX model (float)
      Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\decision_tree_regressor_float.onnx
      Python  Information about input tensors in ONNX:
      Python  1. Name: float_input, Data Type: tensor(float), Shape: [None, 1]
      Python  Information about output tensors in ONNX:
      Python  1. Name: variable, Data Type: tensor(float), Shape: [None, 1]
      Python  R-squared (Coefficient of determination) 0.9999999999999971
      Python  Mean Absolute Error: 4.393654615473253e-06
      Python  Mean Squared Error: 3.829042036424747e-11
      Python  R^2 matching decimal places:  0
      Python  MAE matching decimal places:  0
      Python  MSE matching decimal places:  0
      Python  float ONNX model precision:  0
      Python  
      Python  DecisionTreeRegressor ONNX model (double)
      Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\decision_tree_regressor_double.onnx
      

      오류 탭:

      DecisionTreeRegressor.py started        DecisionTreeRegressor.py        1       1
      Traceback (most recent call last):      DecisionTreeRegressor.py        1       1
          onnx_session = ort.InferenceSession(onnx_filename)  DecisionTreeRegressor.py        160     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.Fail: [ONNXRuntimeError] : 1 : FAIL : Load model from C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\decision_tree_regressor_double.onnx failed:Type Er      onnxruntime_inference_collection.py     424     1
      DecisionTreeRegressor.py finished in 2957 ms            5       1
      


      Fig.106. DecisionTreeRegressor.py의 결과 (float ONNX)

      그림 106. DecisionTreeRegressor.py (float ONNX)의 결과


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

      이 코드는 저장된 decision_tree_regressor_float.onnx decision_tree_regressor_double.onnx 모델을 실행하고 MQL5에서 회귀 메트릭을 사용하는 것을 보여줍니다.

      //+------------------------------------------------------------------+
      //|                                        DecisionTreeRegressor.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"
      
      #define   ModelName          "DecisionTreeRegressor"
      #define   ONNXFilenameFloat  "decision_tree_regressor_float.onnx"
      #define   ONNXFilenameDouble "decision_tree_regressor_double.onnx"
      
      #resource ONNXFilenameFloat  as const uchar ExtModelFloat[];
      #resource ONNXFilenameDouble as const uchar ExtModelDouble[];
      
      #define   TestFloatModel  1
      #define   TestDoubleModel 2
      
      //+------------------------------------------------------------------+
      //| Calculate regression using float values                          |
      //+------------------------------------------------------------------+
      bool RunModelFloat(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         float input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=(float)input_vector[k];
      //--- prepare output tensor
         float output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      //+------------------------------------------------------------------+
      //| Calculate regression using double values                         |
      //+------------------------------------------------------------------+
      bool RunModelDouble(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         double input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=input_vector[k];
      //--- prepare output tensor
         double output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      
      //+------------------------------------------------------------------+
      //| Generate synthetic data                                          |
      //+------------------------------------------------------------------+
      bool GenerateData(const int n,vector &x,vector &y)
        {
         if(n<=0)
            return(false);
      //--- prepare arrays
         x.Resize(n);
         y.Resize(n);
      //---
         for(int i=0; i<n; i++)
           {
            x[i]=(double)1.0*i;
            y[i]=(double)(4*x[i] + 10*sin(x[i]*0.5));
           }
      //---
         return(true);
        }
      
      //+------------------------------------------------------------------+
      //| TestRegressionModel                                              |
      //+------------------------------------------------------------------+
      bool TestRegressionModel(const string model_name,const int model_type)
        {
      //---
         long  model=INVALID_HANDLE;
         ulong flags=ONNX_DEFAULT;
      
         if(model_type==TestFloatModel)
           {
            PrintFormat("\nTesting ONNX float: %s (%s)",model_name,ONNXFilenameFloat);
            model=OnnxCreateFromBuffer(ExtModelFloat,flags);
           }
         else
            if(model_type==TestDoubleModel)
              {
               PrintFormat("\nTesting ONNX double: %s (%s)",model_name,ONNXFilenameDouble);
               model=OnnxCreateFromBuffer(ExtModelDouble,flags);
              }
            else
              {
               PrintFormat("Model type is not incorrect.");
               return(false);
              }
      //--- check
         if(model==INVALID_HANDLE)
           {
            PrintFormat("model_name=%s OnnxCreate error %d",model_name,GetLastError());
            return(false);
           }
      //---
         vector x_values= {};
         vector y_true= {};
         vector y_predicted= {};
      //---
         int n=100;
         GenerateData(n,x_values,y_true);
      //---
         bool run_result=false;
         if(model_type==TestFloatModel)
           {
            run_result=RunModelFloat(model,x_values,y_predicted);
           }
         else
            if(model_type==TestDoubleModel)
              {
               run_result=RunModelDouble(model,x_values,y_predicted);
              }
      //---
         if(run_result)
           {
            PrintFormat("MQL5:   R-Squared (Coefficient of determination): %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_R2));
            PrintFormat("MQL5:   Mean Absolute Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MAE));
            PrintFormat("MQL5:   Mean Squared Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MSE));
           }
         else
            PrintFormat("Error %d",GetLastError());
      //--- release model
         OnnxRelease(model);
      //---
         return(true);
        }
      //+------------------------------------------------------------------+
      //| Script program start function                                    |
      //+------------------------------------------------------------------+
      int OnStart(void)
        {
      //--- test ONNX regression model for float
         TestRegressionModel(ModelName,TestFloatModel);
      //--- test ONNX regression model for double
         TestRegressionModel(ModelName,TestDoubleModel);
      //---
         return(0);
        }
      //+------------------------------------------------------------------+

      출력:

      DecisionTreeRegressor (EURUSD,H1)       Testing ONNX float: DecisionTreeRegressor (decision_tree_regressor_float.onnx)
      DecisionTreeRegressor (EURUSD,H1)       MQL5:   R-Squared (Coefficient of determination): 0.9999999999999971
      DecisionTreeRegressor (EURUSD,H1)       MQL5:   Mean Absolute Error: 0.0000043936546155
      DecisionTreeRegressor (EURUSD,H1)       MQL5:   Mean Squared Error: 0.0000000000382904
      DecisionTreeRegressor (EURUSD,H1)       
      DecisionTreeRegressor (EURUSD,H1)       Testing ONNX double: DecisionTreeRegressor (decision_tree_regressor_double.onnx)
      DecisionTreeRegressor (EURUSD,H1)       ONNX: cannot create session (OrtStatus: 1 'Type Error: Type (tensor(double)) of output arg (variable) of node (TreeEnsembleRegressor) does not match expected type (tensor(float)).'), inspect code 'Scripts\Regression\DecisionTreeRegressor.mq5' (133:16)
      DecisionTreeRegressor (EURUSD,H1)       model_name=DecisionTreeRegressor OnnxCreate error 5800
      

      플로트로 계산된 ONNX 모델은 정상적으로 실행되었지만 더블로 모델을 실행할 때 오류가 발생했습니다.


      2.2.3.3. decision_tree_regressor_float.onnx 및 decision_tree_regressor_double.onnx의 ONNX 표현


      그림 107. Netron에서 decision_tree_regressor_float.onnx의 ONNX 표현

      그림 107. 네트론에서 decision_tree_regressor_float.onnx의 ONNX 표현


      Fig.108. Netron에서 decision_tree_regressor_double.onnx의 ONNX 표현

      그림 108. 네트론에서 decision_tree_regressor_double.onnx의 ONNX 표현



      2.2.4. sklearn.tree.ExtraTreeRegressor

      ExtraTreeRegressor, 즉 극히 무작위화된 트리 회귀분석기가 의사 결정 트리를 기반으로 하는 회귀 앙상블 방식입니다.

      이 메서드는 랜덤 포리스트의 변형으로 각 트리 노드에 대해 최적의 분할을 선택하는 대신 각 노드에 대해 무작위 분할을 사용한다는 점에서 다릅니다. 이렇게 하면 더 무작위적이고 빠르기 때문에 특정 상황에서 유리할 수 있습니다.

      ExtraTreeRegressor의 작동 원리:

      1. 건설 시작: 피처(독립 변수)와 대상 변수의 해당 값이 포함된 초기 데이터 세트부터 시작합니다.
      2. 분할의 무작위성: 최적의 분할이 선택되는 일반 의사 결정 트리와 달리 ExtraTreeRegressor는 임의의 임계값을 사용하여 트리 노드를 분할합니다. 이렇게 하면 분할 프로세스가 더 무작위로 이루어지고 과적합이 덜 발생하게 됩니다.
      3. 트리 구성: 트리는 무작위 피처와 임계값을 기반으로 노드를 분할하여 구축됩니다. 이 프로세스는 최대 트리 깊이 또는 노드의 최소 샘플 수와 같은 특정 중지 기준이 충족될 때까지 계속됩니다.
      4. 나무의 앙상블: ExtraTreeRegressor는 이러한 무작위 트리를 여러 개 구성하며 그 수는 "n_estimators" 하이퍼파라미터로 제어됩니다.
      5. 예측: 새 데이터의 대상 변수를 예측하기 위해 ExtraTreeRegressor는 앙상블에 있는 모든 트리의 예측 평균을 구하기만 하면 됩니다.

      ExtraTreeRegressor의 장점:

      • 과적합 감소: 무작위 노드 분할을 사용하면 일반 의사 결정 트리에 비해 과적합이 덜 발생합니다.
      • 높은 병렬화: 트리는 독립적으로 구축되기 때문에 여러 프로세서에서 훈련할 수 있도록 ExtraTreeRegressor를 쉽게 병렬화할 수 있습니다.
      • 빠른 교육: 경사 부스팅과 같은 다른 메서드에 비해 ExtraTreeRegressor는 더 빠르게 학습할 수 있습니다.

      ExtraTreeRegressor의 한계:

      • 정확도가 떨어질 수 있습니다: 경우에 따라 특히 작은 데이터 집합의 경우에는 더 복잡한 메서드에 비해 ExtraTreeRegressor의 정확도가 떨어질 수 있습니다.
      • 해석하기 어렵습니다: 선형 모델, 의사 결정 트리 및 기타 간단한 메서드과 비교할 때 ExtraTreeRegressor는 일반적으로 해석하기가 어렵습니다.

      ExtraTreeRegressor는 과적합을 줄이고 빠른 훈련이 필요한 상황에서 회귀를 위한 유용한 메서드가 될 수 있습니다.


      2.2.4.1. ExtraTreeRegressor 모델을 생성하고 float 및 double용 ONNX로 내보내기 위한 코드

      이 코드는 sklearn.tree.ExtraTreeRegressor 모델을 생성하고 합성 데이터로 학습시키고 모델을 ONNX 형식으로 저장하고 플로트 및 더블 입력 데이터를 모두 사용하여 예측을 수행합니다. 또한 원본 모델과 ONNX로 내보낸 모델 모두의 정확도를 평가합니다.

      # ExtraTreeRegressor.py
      # 이 코드는 ExtraTreeRegressor 모델을 학습하고 이를 ONNX 형식(float 및 double 모두)으로 내보내고 ONNX 모델을 사용하여 예측하는 과정을 보여 줍니다.
      # Copyright 2023, MetaQuotes Ltd.
      # https://www.mql5.com


      # 소수점 이하 자릿수를 비교학 위한 함수
      def compare_decimal_places(value1, value2):
      # 두 값을 모두 문자열로 변환
          str_value1 = str(value1)
          str_value2 = str(value2)

      # 문자열에서 소수점 위치 찾기
          dot_position1 = str_value1.find(".")
          dot_position2 = str_value2.find(".")

      # 값 중 소수점이 없는 경우 0을 반환합니다.
          if dot_position1 == -1 or dot_position2 == -1:
              return 0

          # 소수점 이하 자릿수 계산
          decimal_places1 = len(str_value1) - dot_position1 - 1
          decimal_places2 = len(str_value2) - dot_position2 - 1

          # 소수점 이하 두 자리 수 중 최소값 찾기
          min_decimal_places = min(decimal_places1, decimal_places2)

          # 소수점 이하 자릿수가 일치하도록 카운트를 초기화
          matching_count = 0

          # 소수점 이하 문자 비교
          for i in range(1, min_decimal_places + 1):
              if str_value1[dot_position1 + i] == str_value2[dot_position2 + i]:
                  matching_count += 1
              else:
                  break

          return matching_count

      # 필요한 라이브러리 가져오기
      import numpy as np
      import matplotlib.pyplot as plt
      from sklearn.tree import ExtraTreeRegressor
      from sklearn.metrics import r2_score,mean_absolute_error,mean_squared_error
      import onnx
      import onnxruntime as ort
      from skl2onnx import convert_sklearn
      from skl2onnx.common.data_types import FloatTensorType
      from skl2onnx.common.data_types import DoubleTensorType
      from sys import argv

      # 모델 저장 경로를 정의합니다.
      data_path = argv[0]
      last_index = data_path.rfind("\\")+ 1
      data_path = data_path[0:last_index]

      # 회귀를 위한 합성 데이터 생성
      X = np.arange(0,100,1).reshape(-1,1)
      y = 4*X + 10*np.sin(X*0.5)

      model_name = "ExtraTreeRegressor"
      onnx_model_filename = data_path + "extra_tree_regressor"

      # ExtraTreeRegressor 모델 만들기
      regression_model = ExtraTreeRegressor()

      # 데이터에 모델 맞추기
      regression_model.fit(X, y.ravel())

      # 전체 데이터 집합에 대한 값 예측
      y_pred = regression_model.predict(X)

      # 모델의 성능 평가
      r2 = r2_score(y, y_pred)
      MSE = MEAN_SQUARE_ERROR(Y, Y_PED)
      MAE = MEAN_ABSOLUTE_ERROR(Y, Y_PED)

      print("\n"+model_name+" Original model (double)")
      print("R-squared (Coefficient of determination):", r2)
      print("Mean Absolute Error:", mae)
      print("Mean Squared Error:", mse)

      # ONNX 모델로 변환 (float)
      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.

      initial_type_float = [('float_input', FloatTensorType([None, X.shape[1]])]]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_float = convert_sklearn(regression_model, initial_types=initial_type_float, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_float.onnx"
      onnx.save_model(onnx_model_float, onnx_filename)

      print("\n"+model_name+" ONNX model (float)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.
      initial_type_float = X.astype(np.float32)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_float = onnx_session.run([output_name], {input_name: initial_type_float})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_float = r2_score(y, y_pred_onnx_float)
      mse_onnx_float = mean_squared_error(y, y_pred_onnx_float)
      mae_onnx_float = mean_absolute_error(y, y_pred_onnx_float)
      print("R-squared (Coefficient of determination)", r2_onnx_float)
      print("Mean Absolute Error:", mae_onnx_float)
      print("Mean Squared Error:", mse_onnx_float)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_float))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_float))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_float))
      print("float ONNX model precision: ",compare_decimal_places(mae, mae_onnx_float))

      # 그림 크기 설정
      plt.figure(figsize=(8,5))
      # 원본 데이터와 회귀 데이터를 플로팅합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with float ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_float.png')

      # ONNX 모델로 변환 (double)
      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.

      initial_type_double = [('double_input', DoubleTensorType([None, X.shape[1]]))]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_double = convert_sklearn(regression_model, initial_types=initial_type_double, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_double.onnx"
      onnx.save_model(onnx_model_double, onnx_filename)

      print("\n"+model_name+" ONNX model (double)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.
      initial_type_double = X.astype(np.float64)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_double = onnx_session.run([output_name], {input_name: initial_type_double})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_double = r2_score(y, y_pred_onnx_double)
      mse_onnx_double = mean_squared_error(y, y_pred_onnx_double)
      mae_onnx_double = mean_absolute_error(y, y_pred_onnx_double)
      print("R-squared (Coefficient of determination)", r2_onnx_double)
      print("Mean Absolute Error:", mae_onnx_double)
      print("Mean Squared Error:", mse_onnx_double)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_double))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_double))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_double))
      print("double ONNX model precision: ",compare_decimal_places(mae, mae_onnx_double))

      # 그림 크기 설정
      plt.figure(figsize=(8, 5))
      # 원본 데이터와 회귀선을 그림으로 표시합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with double ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_double.png')

      출력:

      2023.10.30 14:40:57.665 Python  ExtraTreeRegressor Original model (double)
      2023.10.30 14:40:57.665 Python  R-squared (Coefficient of determination): 1.0
      2023.10.30 14:40:57.665 Python  Mean Absolute Error: 0.0
      2023.10.30 14:40:57.665 Python  Mean Squared Error: 0.0
      2023.10.30 14:40:57.681 Python  
      2023.10.30 14:40:57.681 Python  ExtraTreeRegressor ONNX model (float)
      2023.10.30 14:40:57.681 Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\extra_tree_regressor_float.onnx
      2023.10.30 14:40:57.681 Python  Information about input tensors in ONNX:
      2023.10.30 14:40:57.681 Python  1. Name: float_input, Data Type: tensor(float), Shape: [None, 1]
      2023.10.30 14:40:57.681 Python  Information about output tensors in ONNX:
      2023.10.30 14:40:57.681 Python  1. Name: variable, Data Type: tensor(float), Shape: [None, 1]
      2023.10.30 14:40:57.681 Python  R-squared (Coefficient of determination) 0.9999999999999971
      2023.10.30 14:40:57.681 Python  Mean Absolute Error: 4.393654615473253e-06
      2023.10.30 14:40:57.681 Python  Mean Squared Error: 3.829042036424747e-11
      2023.10.30 14:40:57.681 Python  R^2 matching decimal places:  0
      2023.10.30 14:40:57.681 Python  MAE matching decimal places:  0
      2023.10.30 14:40:57.681 Python  MSE matching decimal places:  0
      2023.10.30 14:40:57.681 Python  float ONNX model precision:  0
      2023.10.30 14:40:58.011 Python  
      2023.10.30 14:40:58.011 Python  ExtraTreeRegressor ONNX model (double)
      2023.10.30 14:40:58.011 Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\extra_tree_regressor_double.onnx
      

      오류 탭:

      ExtraTreeRegressor.py started   ExtraTreeRegressor.py   1       1
      Traceback (most recent call last):      ExtraTreeRegressor.py   1       1
          onnx_session = ort.InferenceSession(onnx_filename)  ExtraTreeRegressor.py   159     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.Fail: [ONNXRuntimeError] : 1 : FAIL : Load model from C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\extra_tree_regressor_double.onnx failed:Type Error      onnxruntime_inference_collection.py     424     1
      ExtraTreeRegressor.py finished in 2980 ms               5       1
      

      Fig.109. ExtraTreeRegressor.py의 결과 (float ONNX)

      그림 109. ExtraTreeRegressor.py의 결과(float ONNX)


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

      이 코드는 저장된 extra_tree_regressor_float.onnxextra_tree_regressor_double.onnx 모델을 실행하고 MQL5에서 회귀 메트릭을 사용하는 것을 보여 줍니다.

      //+------------------------------------------------------------------+
      //|                                           ExtraTreeRegressor.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"
      
      #define   ModelName          "ExtraTreeRegressor"
      #define   ONNXFilenameFloat  "extra_tree_regressor_float.onnx"
      #define   ONNXFilenameDouble "extra_tree_regressor_double.onnx"
      
      #resource ONNXFilenameFloat  as const uchar ExtModelFloat[];
      #resource ONNXFilenameDouble as const uchar ExtModelDouble[];
      
      #define   TestFloatModel  1
      #define   TestDoubleModel 2
      
      //+------------------------------------------------------------------+
      //| Calculate regression using float values                          |
      //+------------------------------------------------------------------+
      bool RunModelFloat(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         float input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=(float)input_vector[k];
      //--- prepare output tensor
         float output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      //+------------------------------------------------------------------+
      //| Calculate regression using double values                         |
      //+------------------------------------------------------------------+
      bool RunModelDouble(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         double input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=input_vector[k];
      //--- prepare output tensor
         double output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      
      //+------------------------------------------------------------------+
      //| Generate synthetic data                                          |
      //+------------------------------------------------------------------+
      bool GenerateData(const int n,vector &x,vector &y)
        {
         if(n<=0)
            return(false);
      //--- prepare arrays
         x.Resize(n);
         y.Resize(n);
      //---
         for(int i=0; i<n; i++)
           {
            x[i]=(double)1.0*i;
            y[i]=(double)(4*x[i] + 10*sin(x[i]*0.5));
           }
      //---
         return(true);
        }
      
      //+------------------------------------------------------------------+
      //| TestRegressionModel                                              |
      //+------------------------------------------------------------------+
      bool TestRegressionModel(const string model_name,const int model_type)
        {
      //---
         long  model=INVALID_HANDLE;
         ulong flags=ONNX_DEFAULT;
      
         if(model_type==TestFloatModel)
           {
            PrintFormat("\nTesting ONNX float: %s (%s)",model_name,ONNXFilenameFloat);
            model=OnnxCreateFromBuffer(ExtModelFloat,flags);
           }
         else
            if(model_type==TestDoubleModel)
              {
               PrintFormat("\nTesting ONNX double: %s (%s)",model_name,ONNXFilenameDouble);
               model=OnnxCreateFromBuffer(ExtModelDouble,flags);
              }
            else
              {
               PrintFormat("Model type is not incorrect.");
               return(false);
              }
      //--- check
         if(model==INVALID_HANDLE)
           {
            PrintFormat("model_name=%s OnnxCreate error %d",model_name,GetLastError());
            return(false);
           }
      //---
         vector x_values= {};
         vector y_true= {};
         vector y_predicted= {};
      //---
         int n=100;
         GenerateData(n,x_values,y_true);
      //---
         bool run_result=false;
         if(model_type==TestFloatModel)
           {
            run_result=RunModelFloat(model,x_values,y_predicted);
           }
         else
            if(model_type==TestDoubleModel)
              {
               run_result=RunModelDouble(model,x_values,y_predicted);
              }
      //---
         if(run_result)
           {
            PrintFormat("MQL5:   R-Squared (Coefficient of determination): %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_R2));
            PrintFormat("MQL5:   Mean Absolute Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MAE));
            PrintFormat("MQL5:   Mean Squared Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MSE));
           }
         else
            PrintFormat("Error %d",GetLastError());
      //--- release model
         OnnxRelease(model);
      //---
         return(true);
        }
      //+------------------------------------------------------------------+
      //| Script program start function                                    |
      //+------------------------------------------------------------------+
      int OnStart(void)
        {
      //--- test ONNX regression model for float
         TestRegressionModel(ModelName,TestFloatModel);
      //--- test ONNX regression model for double
         TestRegressionModel(ModelName,TestDoubleModel);
      //---
         return(0);
        }
      //+------------------------------------------------------------------+

      출력:

      ExtraTreeRegressor (EURUSD,H1)  Testing ONNX float: ExtraTreeRegressor (extra_tree_regressor_float.onnx)
      ExtraTreeRegressor (EURUSD,H1)  MQL5:   R-Squared (Coefficient of determination): 0.9999999999999971
      ExtraTreeRegressor (EURUSD,H1)  MQL5:   Mean Absolute Error: 0.0000043936546155
      ExtraTreeRegressor (EURUSD,H1)  MQL5:   Mean Squared Error: 0.0000000000382904
      ExtraTreeRegressor (EURUSD,H1)  
      ExtraTreeRegressor (EURUSD,H1)  Testing ONNX double: ExtraTreeRegressor (extra_tree_regressor_double.onnx)
      ExtraTreeRegressor (EURUSD,H1)  ONNX: cannot create session (OrtStatus: 1 'Type Error: Type (tensor(double)) of output arg (variable) of node (TreeEnsembleRegressor) does not match expected type (tensor(float)).'), inspect code 'Scripts\Regression\ExtraTreeRegressor.mq5' (133:16)
      ExtraTreeRegressor (EURUSD,H1)  model_name=ExtraTreeRegressor OnnxCreate error 5800
      

      플로트 ONNX 모델은 정상적으로 실행되었지만 ONNX 모델을 더블로 실행할 때는 오류가 발생했습니다.


      2.2.4.3. ONNX 표현 extra_tree_regressor_float.onnx 및 extra_tree_regressor_double.onnx


      Fig.110. Netron에서 extra_tree_regressor_float.onnx의 ONNX 표현

      그림 110. 네트론에서 extra_tree_regressor_float.onnx의 ONNX 표현


      Fig.111. Netron에서 extra_tree_regressor_double.onnx의 ONNX 표현

      그림 111. 네트론에서 extra_tree_regressor_double.onnx의 ONNX 표현


      2.2.5. sklearn.ensemble.ExtraTreesRegressor

      (Extremely Randomized Trees Regressor)는 회귀 작업을 위한 랜덤 포레스트의 변형을 나타내는 머신 러닝 메서드입니다.

      이 메서드는 의사 결정 트리의 앙상블을 사용하여 피처 집합을 기반으로 대상 변수의 수치 값을 예측합니다.

      ExtraTreesRegressor의 작동 방식:

      1. 구성 시작: 시작은 피처(독립 변수) 및 대상 변수의 해당 값을 포함한 원본 데이터 집합으로 시작합니다.
      2. 스플릿에서의 무작위성: 노드를 분할하기 위해 최적의 분할을 선택하는 일반 의사 결정 트리와 달리 ExtraTreesRegressor는 임의의 임계값을 사용하여 트리 노드를 분할합니다. 이러한 무작위성은 분할 프로세스를 보다 임의적으로 만들고 과적합의 가능성을 낮춥니다.
      3. 트리 빌딩: ExtraTreesRegressor는 앙상블에 여러 개의 의사 결정 트리를 구성합니다. 트리의 개수는 "n_estimators" 하이퍼파라미터로 제어됩니다. 각 트리는 데이터의 무작위 하위 샘플(교체 포함)과 피처의 무작위 하위 집합에 대해 학습됩니다.
      4. 예측: 새 데이터의 대상 변수를 예측하기 위해 ExtraTreesRegressor는 앙상블에 있는 모든 트리의 예측을 집계합니다(일반적으로 평균을 내서).

      ExtraTreesRegressor의 장점:

      • 과적합 감소: 무작위 노드 분할과 데이터 서브샘플링을 사용하면 기존 의사 결정 트리에 비해 과적합이 덜 발생합니다.
      • 높은 병렬화: 트리가 독립적으로 구축되므로 여러 프로세서에서 훈련할 수 있도록 ExtraTreesRegressor를 쉽게 병렬화할 수 있습니다.
      • 이상값에 대한 견고성: 이 메서드은 일반적으로 데이터의 이상값에 대한 복원력을 보여줍니다.
      • 숫자 및 범주형 데이터 처리하기: ExtraTreesRegressor는 추가 전처리 없이 숫자 및 범주형 피처를 모두 처리할 수 있습니다.

      ExtraTreesRegressor의 한계:

      • 하이퍼파라미터의 미세 조정이 필요할 수 있습니다: ExtraTreesRegressor는 일반적으로 기본 매개변수로 잘 작동하지, 최대 성능을 달성하려면 하이퍼파라미터를 미세 조정해야 할 수도 있습니다.
      • 해석 가능성 감소: 다른 앙상블 메서드와 마찬가지로 ExtraTreesRegressor는 선형 회귀와 같은 단순한 모델에 비해 해석이 어렵습니다.

      특히 과적합을 줄이고 모델의 일반화를 개선해야 할 때 다양한 작업에서 회귀를 위한 유용한 메서드가 될 수 있습니다.


      2.2.5.1. ExtraTreesRegressor 모델을 생성하고 float 및 double용 ONNX로 내보내는 코드

      이 코드는 sklearn.ensemble.ExtraTreesRegressor 모델을 생성하고 합성 데이터로 학습시키고 모델을 ONNX 형식으로 저장하고 플로트 및 더블 입력 데이터를 모두 사용하여 예측을 수행합니다. 또한 원본 모델과 ONNX로 내보낸 모델 모두의 정확도를 평가합니다.

      # ExtraTreesRegressor.py
      # 이 코드는 ExtraTreesRegressor 모델을 학습하고 이를 ONNX 형식(float 및 double 모두)으로 내보내고 ONNX 모델을 사용하여 예측하는 과정을 보여 줍니다.
      # Copyright 2023, MetaQuotes Ltd.
      # https://www.mql5.com


      # 소수점 이하 자릿수를 비교학 위한 함수
      def compare_decimal_places(value1, value2):
      # 두 값을 모두 문자열로 변환
          str_value1 = str(value1)
          str_value2 = str(value2)

      # 문자열에서 소수점 위치 찾기
          dot_position1 = str_value1.find(".")
          dot_position2 = str_value2.find(".")

      # 값 중 소수점이 없는 경우 0을 반환합니다.
          if dot_position1 == -1 or dot_position2 == -1:
              return 0

          # 소수점 이하 자릿수 계산
          decimal_places1 = len(str_value1) - dot_position1 - 1
          decimal_places2 = len(str_value2) - dot_position2 - 1

          # 소수점 이하 두 자리 수 중 최소값 찾기
          min_decimal_places = min(decimal_places1, decimal_places2)

          # 소수점 이하 자릿수가 일치하도록 카운트를 초기화
          matching_count = 0

          # 소수점 이하 문자 비교
          for i in range(1, min_decimal_places + 1):
              if str_value1[dot_position1 + i] == str_value2[dot_position2 + i]:
                  matching_count += 1
              else:
                  break

          return matching_count

      # 필요한 라이브러리 가져오기
      import numpy as np
      import matplotlib.pyplot as plt
      에서 sklearn.ensemble import ExtraTreesRegressor
      from sklearn.metrics import r2_score,mean_absolute_error,mean_squared_error
      import onnx
      import onnxruntime as ort
      from skl2onnx import convert_sklearn
      from skl2onnx.common.data_types import FloatTensorType
      from skl2onnx.common.data_types import DoubleTensorType
      from sys import argv

      # 모델 저장 경로를 정의합니다.
      data_path = argv[0]
      last_index = data_path.rfind("\\")+ 1
      data_path = data_path[0:last_index]

      # 회귀를 위한 합성 데이터 생성
      X = np.arange(0,100,1).reshape(-1,1)
      y = 4*X + 10*np.sin(X*0.5)

      model_name = "ExtraTreesRegressor"
      onnx_model_filename = data_path + "extra_trees_regressor"

      # 추가 트리 회귀 모델 만들기
      regression_model = ExtraTreesRegressor()

      # 데이터에 모델 맞추기
      regression_model.fit(X, y.ravel())

      # 전체 데이터 집합에 대한 값 예측
      y_pred = regression_model.predict(X)

      # 모델의 성능 평가
      r2 = r2_score(y, y_pred)
      MSE = MEAN_SQUARE_ERROR(Y, Y_PED)
      MAE = MEAN_ABSOLUTE_ERROR(Y, Y_PED)

      print("\n"+model_name+" Original model (double)")
      print("R-squared (Coefficient of determination):", r2)
      print("Mean Absolute Error:", mae)
      print("Mean Squared Error:", mse)

      # ONNX 모델로 변환 (float)
      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.

      initial_type_float = [('float_input', FloatTensorType([None, X.shape[1]])]]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_float = convert_sklearn(regression_model, initial_types=initial_type_float, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_float.onnx"
      onnx.save_model(onnx_model_float, onnx_filename)

      print("\n"+model_name+" ONNX model (float)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.
      initial_type_float = X.astype(np.float32)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_float = onnx_session.run([output_name], {input_name: initial_type_float})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_float = r2_score(y, y_pred_onnx_float)
      mse_onnx_float = mean_squared_error(y, y_pred_onnx_float)
      mae_onnx_float = mean_absolute_error(y, y_pred_onnx_float)
      print("R-squared (Coefficient of determination)", r2_onnx_float)
      print("Mean Absolute Error:", mae_onnx_float)
      print("Mean Squared Error:", mse_onnx_float)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_float))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_float))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_float))
      print("float ONNX model precision: ",compare_decimal_places(mae, mae_onnx_float))

      # 그림 크기 설정
      plt.figure(figsize=(8,5))
      # 원본 데이터와 회귀선을 그림으로 표시합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with float ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_float.png')

      # ONNX 모델로 변환 (double)
      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.

      initial_type_double = [('double_input', DoubleTensorType([None, X.shape[1]]))]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_double = convert_sklearn(regression_model, initial_types=initial_type_double, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_double.onnx"
      onnx.save_model(onnx_model_double, onnx_filename)

      print("\n"+model_name+" ONNX model (double)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.
      initial_type_double = X.astype(np.float64)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_double = onnx_session.run([output_name], {input_name: initial_type_double})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_double = r2_score(y, y_pred_onnx_double)
      mse_onnx_double = mean_squared_error(y, y_pred_onnx_double)
      mae_onnx_double = mean_absolute_error(y, y_pred_onnx_double)
      print("R-squared (Coefficient of determination)", r2_onnx_double)
      print("Mean Absolute Error:", mae_onnx_double)
      print("Mean Squared Error:", mse_onnx_double)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_double))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_double))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_double))
      print("double ONNX model precision: ",compare_decimal_places(mae, mae_onnx_double))

      # 그림 크기 설정
      plt.figure(figsize=(8,5))
      # 원본 데이터와 회귀선을 그림으로 표시합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with double ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_double.png')

      출력:

      Python  ExtraTreesRegressor Original model (double)
      Python  R-squared (Coefficient of determination): 1.0
      Python  Mean Absolute Error: 2.2302160118670144e-13
      Python  Mean Squared Error: 8.41048471722451e-26
      Python  
      Python  ExtraTreesRegressor ONNX model (float)
      Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\extra_trees_regressor_float.onnx
      Python  Information about input tensors in ONNX:
      Python  1. Name: float_input, Data Type: tensor(float), Shape: [None, 1]
      Python  Information about output tensors in ONNX:
      Python  1. Name: variable, Data Type: tensor(float), Shape: [None, 1]
      Python  R-squared (Coefficient of determination) 0.9999999999998015
      Python  Mean Absolute Error: 3.795239380975701e-05
      Python  Mean Squared Error: 2.627067474763585e-09
      Python  R^2 matching decimal places:  0
      Python  MAE matching decimal places:  0
      Python  MSE matching decimal places:  0
      Python  float ONNX model precision:  0
      Python  
      Python  ExtraTreesRegressor ONNX model (double)
      Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\extra_trees_regressor_double.onnx
      

      오류 탭:

      ExtraTreesRegressor.py started  ExtraTreesRegressor.py  1       1
      Traceback (most recent call last):      ExtraTreesRegressor.py  1       1
          onnx_session = ort.InferenceSession(onnx_filename)  ExtraTreesRegressor.py  160     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.Fail: [ONNXRuntimeError] : 1 : FAIL : Load model from C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\extra_trees_regressor_double.onnx failed:Type Erro      onnxruntime_inference_collection.py     424     1
      ExtraTreesRegressor.py finished in 4654 ms              5       1
      

      Fig.112. ExtraTreesRegressor.py의 결과(float ONNX)

      그림 112. ExtraTreesRegressor.py의 결과(float ONNX)


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

      이 코드는 extra_trees_regressor_float.onnxextra_trees_regressor_double.onnx 모델을 생성하고 MQL5에서 회귀 메트릭을 사용하는 방법을 보여 줍니다.

      //+------------------------------------------------------------------+
      //|                                          ExtraTreesRegressor.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"
      
      #define   ModelName          "ExtraTreesRegressor"
      #define   ONNXFilenameFloat  "extra_trees_regressor_float.onnx"
      #define   ONNXFilenameDouble "extra_trees_regressor_double.onnx"
      
      #resource ONNXFilenameFloat  as const uchar ExtModelFloat[];
      #resource ONNXFilenameDouble as const uchar ExtModelDouble[];
      
      #define   TestFloatModel  1
      #define   TestDoubleModel 2
      
      //+------------------------------------------------------------------+
      //| Calculate regression using float values                          |
      //+------------------------------------------------------------------+
      bool RunModelFloat(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         float input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=(float)input_vector[k];
      //--- prepare output tensor
         float output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      //+------------------------------------------------------------------+
      //| Calculate regression using double values                         |
      //+------------------------------------------------------------------+
      bool RunModelDouble(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         double input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=input_vector[k];
      //--- prepare output tensor
         double output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      
      //+------------------------------------------------------------------+
      //| Generate synthetic data                                          |
      //+------------------------------------------------------------------+
      bool GenerateData(const int n,vector &x,vector &y)
        {
         if(n<=0)
            return(false);
      //--- prepare arrays
         x.Resize(n);
         y.Resize(n);
      //---
         for(int i=0; i<n; i++)
           {
            x[i]=(double)1.0*i;
            y[i]=(double)(4*x[i] + 10*sin(x[i]*0.5));
           }
      //---
         return(true);
        }
      
      //+------------------------------------------------------------------+
      //| TestRegressionModel                                              |
      //+------------------------------------------------------------------+
      bool TestRegressionModel(const string model_name,const int model_type)
        {
      //---
         long  model=INVALID_HANDLE;
         ulong flags=ONNX_DEFAULT;
      
         if(model_type==TestFloatModel)
           {
            PrintFormat("\nTesting ONNX float: %s (%s)",model_name,ONNXFilenameFloat);
            model=OnnxCreateFromBuffer(ExtModelFloat,flags);
           }
         else
            if(model_type==TestDoubleModel)
              {
               PrintFormat("\nTesting ONNX double: %s (%s)",model_name,ONNXFilenameDouble);
               model=OnnxCreateFromBuffer(ExtModelDouble,flags);
              }
            else
              {
               PrintFormat("Model type is not incorrect.");
               return(false);
              }
      //--- check
         if(model==INVALID_HANDLE)
           {
            PrintFormat("model_name=%s OnnxCreate error %d",model_name,GetLastError());
            return(false);
           }
      //---
         vector x_values= {};
         vector y_true= {};
         vector y_predicted= {};
      //---
         int n=100;
         GenerateData(n,x_values,y_true);
      //---
         bool run_result=false;
         if(model_type==TestFloatModel)
           {
            run_result=RunModelFloat(model,x_values,y_predicted);
           }
         else
            if(model_type==TestDoubleModel)
              {
               run_result=RunModelDouble(model,x_values,y_predicted);
              }
      //---
         if(run_result)
           {
            PrintFormat("MQL5:   R-Squared (Coefficient of determination): %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_R2));
            PrintFormat("MQL5:   Mean Absolute Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MAE));
            PrintFormat("MQL5:   Mean Squared Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MSE));
           }
         else
            PrintFormat("Error %d",GetLastError());
      //--- release model
         OnnxRelease(model);
      //---
         return(true);
        }
      //+------------------------------------------------------------------+
      //| Script program start function                                    |
      //+------------------------------------------------------------------+
      int OnStart(void)
        {
      //--- test ONNX regression model for float
         TestRegressionModel(ModelName,TestFloatModel);
      //--- test ONNX regression model for double
         TestRegressionModel(ModelName,TestDoubleModel);
      //---
         return(0);
        }
      //+------------------------------------------------------------------+

      출력:

      ExtraTreesRegressor (EURUSD,H1) Testing ONNX float: ExtraTreesRegressor (extra_trees_regressor_float.onnx)
      ExtraTreesRegressor (EURUSD,H1) MQL5:   R-Squared (Coefficient of determination): 0.9999999999998015
      ExtraTreesRegressor (EURUSD,H1) MQL5:   Mean Absolute Error: 0.0000379523938098
      ExtraTreesRegressor (EURUSD,H1) MQL5:   Mean Squared Error: 0.0000000026270675
      ExtraTreesRegressor (EURUSD,H1) 
      ExtraTreesRegressor (EURUSD,H1) Testing ONNX double: ExtraTreesRegressor (extra_trees_regressor_double.onnx)
      ExtraTreesRegressor (EURUSD,H1) ONNX: cannot create session (OrtStatus: 1 'Type Error: Type (tensor(double)) of output arg (variable) of node (TreeEnsembleRegressor) does not match expected type (tensor(float)).'), inspect code 'Scripts\Regression\ExtraTreesRegressor.mq5' (133:16)
      ExtraTreesRegressor (EURUSD,H1) model_name=ExtraTreesRegressor OnnxCreate error 5800
      

      플로트 ONNX 모델은 정상적으로 실행되었지만 ONNX 모델을 더블로 실행할 때는 오류가 발생했습니다.


      2.2.5.3. extra_trees_regressor_float.onnx 및 extra_trees_regressor_double.onnx의 ONNX 표현


      그림 113. Netron에서 extra_trees_regressor_float.onnx의 ONNX 표현

      그림 113. 넷트론에서 extra_trees_regressor_float.onnx의 ONNX 표현


      그림 114. Netron에서 extra_trees_regressor_double.onnx의 ONNX 표현

      그림 114. 네트론에서 extra_trees_regressor_double.onnx의 ONNX 표현


      2.2.6. sklearn.svm.NuSVR

      NuSVR는 회귀 작업에 사용되는 머신 러닝 메서드입니다. 이 메서드는 SVM(서포트 벡터 머신)을 기반으로 하지만 분류 작업 대신 회귀 작업에 적용됩니다.

      NuSVR는 대상 변수의 연속 값을 예측하여 회귀 작업을 해결하도록 설계된 SVM의 변형입니다.

      NuSVR 작동 방식:

      1. 입력 데이터: 피처(독립 변수)와 대상 변수(연속형)의 값을 포함하는 데이터 집합으로 시작합니다.
      2. 커널 선택: NuSVR은 선형, 다항식 또는 방사형 기저 함수(RBF)와 같은 커널을 사용하여 데이터를 선형 분리 쌍곡면을 찾을 수 있는 고차원 공간으로 변환합니다.
      3. Nu 매개변수 정의하기: Nu 매개변수는 모델 복잡도를 제어하고 이상값으로 간주할 훈련 예제의 수를 정의합니다. Nu 값은 0에서 1 사이여야 하며 서포트 벡터의 수에 영향을 줍니다.
      4. 지원 벡터 구성: NuSVR은 이 하이퍼플레인과 가장 가까운 샘플 포인트 사이의 간격을 최대화하는 최적의 분리 하이퍼플레인을 찾는 것을 목표로 합니다.
      5. 모델 교육: 모델은 회귀 오류를 최소화하고 Nu 매개변수와 관련된 제약 조건을 충족하도록 학습됩니다.
      6. 예측하기: 학습 후에는 모델을 사용하여 새로운 데이터에 대한 목표 변수의 값을 예측할 수 있습니다.

      NuSVR의 장점:

      • 이상값 처리: NuSVR을 사용하면 Nu 매개 변수를 사용하여 이상값을 제어하여 이상값으로 간주되는 학습 예제의 수를 조절할 수 있습니다.
      • 다중 커널: 이 메서드는 다양한 유형의 커널을 지원하므로 복잡한 비선형 관계를 모델링할 수 있습니다.

      NuSVR의 한계:

      • Nu 매개변수 선택: Nu 매개변수의 올바른 값을 선택하려면 약간의 도전이 필요할 수 있습니다.
      • 데이터 규모 감도: NuSVR을 포함한 SVM은 데이터 규모에 민감할 수 있으므로 피처 표준화 또는 정규화가 필요할 수 있습니다.
      • 계산의 복잡성: 대규모 데이터 세트와 복잡한 커널의 경우 NuSVR은 계산 비용이 많이 들 수 있습니다.

      NuSVR은 서포트 벡터 머신(SVM) 방식을 기반으로 하는 회귀 작업을 위한 머신 러닝 메서드로 대상 변수의 연속 값을 예측할 수 있으며 Nu 매개변수를 사용하여 이상값을 관리할 수 있는 기능을 제공합니다.


      2.2.6.1. NuSVR 모델을 생성하고 float 및 double용 ONNX로 내보내기 위한 코드

      이 코드는 sklearn.svm.NuSVR 모델을 생성하고 합성 데이터로 학습시키고 모델을 ONNX 형식으로 저장하고 플로트 및 더블 입력 데이터를 모두 사용하여 예측을 수행합니다. 또한 원본 모델과 ONNX로 내보낸 모델 모두의 정확도를 평가합니다.

      # NuSVR.py
      # 이 코드는 NuSVR 모델을 학습하고 이를 ONNX 형식(float 및 double)으로 내보내고 ONNX 모델을 사용하여 예측하는 과정을 보여 줍니다.
      # Copyright 2023, MetaQuotes Ltd.
      # https://www.mql5.com


      # 소수점 이하 자릿수를 비교학 위한 함수
      def compare_decimal_places(value1, value2):
      # 두 값을 모두 문자열로 변환
          str_value1 = str(value1)
          str_value2 = str(value2)

      # 문자열에서 소수점 위치 찾기
          dot_position1 = str_value1.find(".")
          dot_position2 = str_value2.find(".")

      # 값 중 소수점이 없는 경우 0을 반환합니다.
          if dot_position1 == -1 or dot_position2 == -1:
              return 0

          # 소수점 이하 자릿수 계산
          decimal_places1 = len(str_value1) - dot_position1 - 1
          decimal_places2 = len(str_value2) - dot_position2 - 1

          # 소수점 이하 두 자리 수 중 최소값 찾기
          min_decimal_places = min(decimal_places1, decimal_places2)

          # 소수점 이하 자릿수가 일치하도록 카운트를 초기화
          matching_count = 0

          # 소수점 이하 문자 비교
          for i in range(1, min_decimal_places + 1):
              if str_value1[dot_position1 + i] == str_value2[dot_position2 + i]:
                  matching_count += 1
              else:
                  break

          return matching_count

      # 필요한 라이브러리 가져오기
      import numpy as np
      import matplotlib.pyplot as plt
      에서 sklearn.svm import NuSVR
      from sklearn.metrics import r2_score,mean_absolute_error,mean_squared_error
      import onnx
      import onnxruntime as ort
      from skl2onnx import convert_sklearn
      from skl2onnx.common.data_types import FloatTensorType
      from skl2onnx.common.data_types import DoubleTensorType
      from sys import argv

      # 모델 저장 경로를 정의합니다.
      data_path = argv[0]
      last_index = data_path.rfind("\\")+ 1
      data_path = data_path[0:last_index]

      # 회귀를 위한 합성 데이터 생성
      X = np.arange(0,100,1).reshape(-1,1)
      y = 4*X + 10*np.sin(X*0.5)

      model_name = "NuSVR"
      onnx_model_filename = data_path + "nu_svr"

      # NuSVR 모델 만들기
      nusvr_model = NuSVR()

      # 데이터에 모델 맞추기
      nusvr_model.fit(X, y.ravel())

      # 전체 데이터 집합에 대한 값 예측
      y_pred = nusvr_model.predict(X)

      # 모델의 성능 평가
      r2 = r2_score(y, y_pred)
      MSE = MEAN_SQUARE_ERROR(Y, Y_PED)
      MAE = MEAN_ABSOLUTE_ERROR(Y, Y_PED)

      print("\n"+model_name+" Original model (double)")
      print("R-squared (Coefficient of determination):", r2)
      print("Mean Absolute Error:", mae)
      print("Mean Squared Error:", mse)

      # ONNX 모델로 변환 (float)
      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.

      initial_type_float = [('float_input', FloatTensorType([None, X.shape[1]])]]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_float = convert_sklearn(nusvr_model, initial_types=initial_type_float, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_float.onnx"
      onnx.save_model(onnx_model_float, onnx_filename)

      print("\n"+model_name+" ONNX model (float)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.
      initial_type_float = X.astype(np.float32)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_float = onnx_session.run([output_name], {input_name: initial_type_float})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_float = r2_score(y, y_pred_onnx_float)
      mse_onnx_float = mean_squared_error(y, y_pred_onnx_float)
      mae_onnx_float = mean_absolute_error(y, y_pred_onnx_float)
      print("R-squared (Coefficient of determination)", r2_onnx_float)
      print("Mean Absolute Error:", mae_onnx_float)
      print("Mean Squared Error:", mse_onnx_float)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_float))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_float))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_float))
      print("float ONNX model precision: ",compare_decimal_places(mae, mae_onnx_float))

      # 그림 크기 설정
      plt.figure(figsize=(8,5))
      # 원본 데이터와 회귀선을 그림으로 표시합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with float ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_float.png')

      # ONNX 모델로 변환 (double)
      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.

      initial_type_double = [('double_input', DoubleTensorType([None, X.shape[1]]))]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_double = convert_sklearn(nusvr_model, initial_types=initial_type_double, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_double.onnx"
      onnx.save_model(onnx_model_double, onnx_filename)

      print("\n"+model_name+" ONNX model (double)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.
      initial_type_double = X.astype(np.float64)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_double = onnx_session.run([output_name], {input_name: initial_type_double})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_double = r2_score(y, y_pred_onnx_double)
      mse_onnx_double = mean_squared_error(y, y_pred_onnx_double)
      mae_onnx_double = mean_absolute_error(y, y_pred_onnx_double)
      print("R-squared (Coefficient of determination)", r2_onnx_double)
      print("Mean Absolute Error:", mae_onnx_double)
      print("Mean Squared Error:", mse_onnx_double)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_double))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_double))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_double))
      print("double ONNX model precision: ",compare_decimal_places(mae, mae_onnx_double))

      # 그림 크기 설정
      plt.figure(figsize=(8,5))
      # 원본 데이터와 회귀선을 그림으로 표시합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with double ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_double.png')

      출력:

      Python  NuSVR Original model (double)
      Python  R-squared (Coefficient of determination): 0.2771437770527445
      Python  Mean Absolute Error: 83.76666411704255
      Python  Mean Squared Error: 9565.381751764757
      Python  
      Python  NuSVR ONNX model (float)
      Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\nu_svr_float.onnx
      Python  Information about input tensors in ONNX:
      1. Name: float_input, Data Type: tensor(float), Shape: [None, 1]
      Python  Information about output tensors in ONNX:
      Python  1. Name: variable, Data Type: tensor(float), Shape: [None, 1]
      Python  R-squared (Coefficient of determination) 0.27714379657935495
      Python  Mean Absolute Error: 83.766663385322
      Python  Mean Squared Error: 9565.381493373838
      Python  R^2 matching decimal places:  7
      Python  MAE matching decimal places:  5
      Python  MSE matching decimal places:  3
      Python  float ONNX model precision:  5
      Python  
      Python  NuSVR ONNX model (double)
      Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\nu_svr_double.onnx
      

      오류 탭:

      NuSVR.py started        NuSVR.py        1       1
      Traceback (most recent call last):      NuSVR.py        1       1
          onnx_session = ort.InferenceSession(onnx_filename)  NuSVR.py        159     1
          self._create_inference_session(providers, provider_options, disabled_optimizers)    onnxruntime_inference_collection.py     383     1
          sess.initialize_session(providers, provider_options, disabled_optimizers)   onnxruntime_inference_collection.py     435     1
      onnxruntime.capi.onnxruntime_pybind11_state.NotImplemented: [ONNXRuntimeError] : 9 : NOT_IMPLEMENTED : Could not find an implementation for SVMRegressor(1) node with name 'SVM'        onnxruntime_inference_collection.py     435     1
      NuSVR.py finished in 2925 ms            5       1
      

      Fig.115. NuSVR.py의 결과(float ONNX)

      그림 115. NuSVR.py의 결과(float ONNX)


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

      이 코드는 저장된 nu_svr_float.onnxnu_svr_double.onnx 모델을 실행하고 MQL5에서 회귀 메트릭을 사용하는 것을 보여줍니다.

      //+------------------------------------------------------------------+
      //|                                                        NuSVR.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"
      
      #define   ModelName          "NuSVR"
      #define   ONNXFilenameFloat  "nu_svr_float.onnx"
      #define   ONNXFilenameDouble "nu_svr_double.onnx"
      
      #resource ONNXFilenameFloat  as const uchar ExtModelFloat[];
      #resource ONNXFilenameDouble as const uchar ExtModelDouble[];
      
      #define   TestFloatModel  1
      #define   TestDoubleModel 2
      
      //+------------------------------------------------------------------+
      //| Calculate regression using float values                          |
      //+------------------------------------------------------------------+
      bool RunModelFloat(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         float input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=(float)input_vector[k];
      //--- prepare output tensor
         float output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      //+------------------------------------------------------------------+
      //| Calculate regression using double values                         |
      //+------------------------------------------------------------------+
      bool RunModelDouble(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         double input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=input_vector[k];
      //--- prepare output tensor
         double output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      
      //+------------------------------------------------------------------+
      //| Generate synthetic data                                          |
      //+------------------------------------------------------------------+
      bool GenerateData(const int n,vector &x,vector &y)
        {
         if(n<=0)
            return(false);
      //--- prepare arrays
         x.Resize(n);
         y.Resize(n);
      //---
         for(int i=0; i<n; i++)
           {
            x[i]=(double)1.0*i;
            y[i]=(double)(4*x[i] + 10*sin(x[i]*0.5));
           }
      //---
         return(true);
        }
      
      //+------------------------------------------------------------------+
      //| TestRegressionModel                                              |
      //+------------------------------------------------------------------+
      bool TestRegressionModel(const string model_name,const int model_type)
        {
      //---
         long  model=INVALID_HANDLE;
         ulong flags=ONNX_DEFAULT;
      
         if(model_type==TestFloatModel)
           {
            PrintFormat("\nTesting ONNX float: %s (%s)",model_name,ONNXFilenameFloat);
            model=OnnxCreateFromBuffer(ExtModelFloat,flags);
           }
         else
            if(model_type==TestDoubleModel)
              {
               PrintFormat("\nTesting ONNX double: %s (%s)",model_name,ONNXFilenameDouble);
               model=OnnxCreateFromBuffer(ExtModelDouble,flags);
              }
            else
              {
               PrintFormat("Model type is not incorrect.");
               return(false);
              }
      //--- check
         if(model==INVALID_HANDLE)
           {
            PrintFormat("model_name=%s OnnxCreate error %d",model_name,GetLastError());
            return(false);
           }
      //---
         vector x_values= {};
         vector y_true= {};
         vector y_predicted= {};
      //---
         int n=100;
         GenerateData(n,x_values,y_true);
      //---
         bool run_result=false;
         if(model_type==TestFloatModel)
           {
            run_result=RunModelFloat(model,x_values,y_predicted);
           }
         else
            if(model_type==TestDoubleModel)
              {
               run_result=RunModelDouble(model,x_values,y_predicted);
              }
      //---
         if(run_result)
           {
            PrintFormat("MQL5:   R-Squared (Coefficient of determination): %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_R2));
            PrintFormat("MQL5:   Mean Absolute Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MAE));
            PrintFormat("MQL5:   Mean Squared Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MSE));
           }
         else
            PrintFormat("Error %d",GetLastError());
      //--- release model
         OnnxRelease(model);
      //---
         return(true);
        }
      //+------------------------------------------------------------------+
      //| Script program start function                                    |
      //+------------------------------------------------------------------+
      int OnStart(void)
        {
      //--- test ONNX regression model for float
         TestRegressionModel(ModelName,TestFloatModel);
      //--- test ONNX regression model for double
         TestRegressionModel(ModelName,TestDoubleModel);
      //---
         return(0);
        }
      //+------------------------------------------------------------------+

      출력:

      NuSVR (EURUSD,H1)       Testing ONNX float: NuSVR (nu_svr_float.onnx)
      NuSVR (EURUSD,H1)       MQL5:   R-Squared (Coefficient of determination): 0.2771437965793548
      NuSVR (EURUSD,H1)       MQL5:   Mean Absolute Error: 83.7666633853219906
      NuSVR (EURUSD,H1)       MQL5:   Mean Squared Error: 9565.3814933738358377
      NuSVR (EURUSD,H1)       
      NuSVR (EURUSD,H1)       Testing ONNX double: NuSVR (nu_svr_double.onnx)
      NuSVR (EURUSD,H1)       ONNX: cannot create session (OrtStatus: 9 'Could not find an implementation for SVMRegressor(1) node with name 'SVM''), inspect code 'Scripts\Regression\NuSVR.mq5' (133:16)
      NuSVR (EURUSD,H1)       model_name=NuSVR OnnxCreate error 5800
      

      플로트 ONNX 모델은 정상적으로 실행되었지만 ONNX 모델을 더블로 실행할 때는 오류가 발생했습니다.

      파이썬의 원래 배정밀도 모델과 비교:

      Testing ONNX float: NuSVR (nu_svr_float.onnx)
      Python  Mean Absolute Error: 83.76666411704255
      MQL5:   Mean Absolute Error: 83.7666633853219906


      2.2.6.3. nu_svr_float.onnx 및 nu_svr_double.onnx의 ONNX 표현


      Fig.116. 네트론에서 nu_svr_float.onnx의 ONNX 표현

      그림 116. 네트론에서 nu_svr_float.onnx의 ONNX 표현


      Fig.117. 네트론에서 nu_svr_double.onnx의 ONNX 표현

      그림 117. 네트론에서 nu_svr_double.onnx의 ONNX 표현



      2.2.7. sklearn.ensemble.RandomForestRegressor

      RandomForestRegressor은 회귀 작업을 해결하는 데 사용되는 머신 러닝 메서드로

      앙상블 학습을 기반으로 하는 가장 인기 있는 메서드 중 하나이며 랜덤 포레스트 알고리즘을 사용하여 강력하고 강력한 회귀 모델을 생성합니다.

      RandomForestRegressor의 작동 방식은 다음과 같습니다:

      1. 입력 데이터: 피처(독립 변수)와 대상 변수(연속형)를 포함하는 데이터 집합으로 시작합니다.
      2. Random Forest: RandomForestRegressor는 의사 결정 트리의 앙상블을 사용하여 회귀 작업을 해결합니다. 포리스트의 각 트리는 목표 변수 값을 예측하는 작업을 합니다.
      3. 부트스트랩 샘플링: 각 트리는 부트스트랩 샘플을 사용하여 학습되는데 이는 학습 데이터 세트에서 무작위로 샘플링하여 교체하는 것을 의미합니다. 이를 통해 각 트리가 학습하는 데이터의 다양성을 확보할 수 있습니다.
      4. 무작위 피처 선택: 각 트리를 구축할 때 피처의 무작위 하위 집합도 선택되므로 모델이 더욱 견고해지고 각각의 트리 간의 상관관계가 줄어듭니다.
      5. 평균 예측: 모든 트리가 구성되면 RandomForestRegressor는 예측을 평균하거나 결합하여 최종 회귀 예측을 얻습니다.

      RandomForestRegressor의 장점:

      • 강력한 성능과 견고함: RandomForestRegressor는 종종 좋은 성능을 제공하는 강력한 회귀 메서드입니다.
      • 대용량 데이터 처리: 대규모 데이터 세트를 잘 처리하고 다양한 피처를 처리할 수 있습니다.
      • 과적합에 대한 복원력: 부트스트랩 샘플링과 무작위 피처 선택으로 인해 랜덤 포레스트는 일반적으로 과적합에 대해 강력합니다.
      • 피처 중요도 추정: 랜덤 포레스트는 회귀 작업에서 각 피처의 중요도에 대한 정보를 제공할 수 있습니다.

      RandomForestRegressor의 한계:

      • 해석 가능성 부족: 이 모델은 선형 모델에 비해 해석이 어려울 수 있습니다.
      • 항상 가장 정확한 모델은 아닙니다: 일부 작업에서는 더 복잡한 앙상블이 불필요할 수 있으며 이 경우 선형 모델이 더 적합할 수 있습니다.

      RandomForestRegressor는 회귀 작업을 위한 강력한 머신 러닝 메서드로 무작위 의사 결정 트리의 앙상블을 사용하여 안정적이고 성능이 뛰어난 회귀 모델을 생성합니다. 이 메서드는 대규모 데이터 세트가 있는 작업이나 피처 중요도를 평가할 때 특히 유용합니다.


      2.2.7.1. RandomForestRegressor 모델을 생성하고 float 및 double용 ONNX로 내보내기 위한 코드

      이 코드는 sklearn.ensemble.RandomForestRegressor 모델을 생성하고 합성 데이터로 학습시키고 모델을 ONNX 형식으로 저장하고 플로트 및 더블 입력 데이터를 모두 사용하여 예측을 수행합니다. 또한 원본 모델과 ONNX로 내보낸 모델 모두의 정확도를 평가합니다.

      # RandomForestRegressor.py
      # 이 코드는 RandomForestRegressor 모델을 학습하고 이를 ONNX 형식(float 및 double 모두)으로 내보내고 ONNX 모델을 사용하여 예측하는 과정을 보여 줍니다.
      # Copyright 2023, MetaQuotes Ltd.
      # https://www.mql5.com


      # 소수점 이하 자릿수를 비교학 위한 함수
      def compare_decimal_places(value1, value2):
      # 두 값을 모두 문자열로 변환
          str_value1 = str(value1)
          str_value2 = str(value2)

      # 문자열에서 소수점 위치 찾기
          dot_position1 = str_value1.find(".")
          dot_position2 = str_value2.find(".")

      # 값 중 소수점이 없는 경우 0을 반환합니다.
          if dot_position1 == -1 or dot_position2 == -1:
              return 0

          # 소수점 이하 자릿수 계산
          decimal_places1 = len(str_value1) - dot_position1 - 1
          decimal_places2 = len(str_value2) - dot_position2 - 1

          # 소수점 이하 두 자리 수 중 최소값 찾기
          min_decimal_places = min(decimal_places1, decimal_places2)

          # 소수점 이하 자릿수가 일치하도록 카운트를 초기화
          matching_count = 0

          # 소수점 이하 문자 비교
          for i in range(1, min_decimal_places + 1):
              if str_value1[dot_position1 + i] == str_value2[dot_position2 + i]:
                  matching_count += 1
              else:
                  break

          return matching_count

      # 필요한 라이브러리 가져오기

      import numpy as np
      import matplotlib.pyplot as plt
      에서 sklearn.ensemble import RandomForestRegressor
      from sklearn.metrics import r2_score,mean_absolute_error,mean_squared_error
      import onnx
      import onnxruntime as ort
      from skl2onnx import convert_sklearn
      from skl2onnx.common.data_types import FloatTensorType
      from skl2onnx.common.data_types import DoubleTensorType
      from sys import argv

      # 모델 저장 경로를 정의합니다.
      data_path = argv[0]
      last_index = data_path.rfind("\\")+ 1
      data_path = data_path[0:last_index]

      # 회귀를 위한 합성 데이터 생성
      X = np.arange(0,100,1).reshape(-1,1)
      y = 4*X + 10*np.sin(X*0.5)

      model_name = "RandomForestRegressor"
      onnx_model_filename = data_path + "random_forest_regressor"

      # 랜덤 포레스트 레귤레이터 모델을 생성합니다.
      regression_model = RandomForestRegressor()

      # 데이터에 모델 맞추기
      regression_model.fit(X, y.ravel())

      # 전체 데이터 집합에 대한 값 예측
      y_pred = regression_model.predict(X)

      # 모델의 성능 평가
      r2 = r2_score(y, y_pred)
      MSE = MEAN_SQUARE_ERROR(Y, Y_PED)
      MAE = MEAN_ABSOLUTE_ERROR(Y, Y_PED)

      print("\n"+model_name+" Original model (double)")
      print("R-squared (Coefficient of determination):", r2)
      print("Mean Absolute Error:", mae)
      print("Mean Squared Error:", mse)

      # ONNX 모델로 변환 (float)
      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.

      initial_type_float = [('float_input', FloatTensorType([None, X.shape[1]])]]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_float = convert_sklearn(regression_model, initial_types=initial_type_float, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_float.onnx"
      onnx.save_model(onnx_model_float, onnx_filename)

      print("\n"+model_name+" ONNX model (float)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.
      initial_type_float = X.astype(np.float32)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_float = onnx_session.run([output_name], {input_name: initial_type_float})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_float = r2_score(y, y_pred_onnx_float)
      mse_onnx_float = mean_squared_error(y, y_pred_onnx_float)
      mae_onnx_float = mean_absolute_error(y, y_pred_onnx_float)
      print("R-squared (Coefficient of determination)", r2_onnx_float)
      print("Mean Absolute Error:", mae_onnx_float)
      print("Mean Squared Error:", mse_onnx_float)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_float))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_float))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_float))
      print("float ONNX model precision: ",compare_decimal_places(mae, mae_onnx_float))

      # 그림 크기 설정
      plt.figure(figsize=(8,5))
      # 원본 데이터와 회귀선을 그림으로 표시합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with float ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_float.png')

      # ONNX 모델로 변환 (double)
      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.

      initial_type_double = [('double_input', DoubleTensorType([None, X.shape[1]]))]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_double = convert_sklearn(regression_model, initial_types=initial_type_double, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_double.onnx"
      onnx.save_model(onnx_model_double, onnx_filename)

      print("\n"+model_name+" ONNX model (double)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.
      initial_type_double = X.astype(np.float64)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_double = onnx_session.run([output_name], {input_name: initial_type_double})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_double = r2_score(y, y_pred_onnx_double)
      mse_onnx_double = mean_squared_error(y, y_pred_onnx_double)
      mae_onnx_double = mean_absolute_error(y, y_pred_onnx_double)
      print("R-squared (Coefficient of determination)", r2_onnx_double)
      print("Mean Absolute Error:", mae_onnx_double)
      print("Mean Squared Error:", mse_onnx_double)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_double))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_double))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_double))
      print("double ONNX model precision: ",compare_decimal_places(mae, mae_onnx_double))

      # 그림 크기 설정
      plt.figure(figsize=(8,5))
      # 원본 데이터와 회귀선을 그림으로 표시합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with double ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_double.png')

      출력:

      Python  RandomForestRegressor Original model (double)
      Python  R-squared (Coefficient of determination): 0.9998854509605539
      Python  Mean Absolute Error: 0.9186485980852603
      Python  Mean Squared Error: 1.5157997632401086
      Python  
      Python  RandomForestRegressor ONNX model (float)
      Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\random_forest_regressor_float.onnx
      Python  Information about input tensors in ONNX:
      Python  1. Name: float_input, Data Type: tensor(float), Shape: [None, 1]
      Python  Information about output tensors in ONNX:
      Python  1. Name: variable, Data Type: tensor(float), Shape: [None, 1]
      Python  R-squared (Coefficient of determination) 0.9998854516013125
      Python  Mean Absolute Error: 0.9186420704511761
      Python  Mean Squared Error: 1.515791284236419
      Python  R^2 matching decimal places:  8
      Python  MAE matching decimal places:  5
      Python  MSE matching decimal places:  5
      Python  float ONNX model precision:  5
      Python  
      Python  RandomForestRegressor ONNX model (double)
      Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\random_forest_regressor_double.onnx
      

      오류 탭:

      RandomForestRegressor.py started        RandomForestRegressor.py        1       1
      Traceback (most recent call last):      RandomForestRegressor.py        1       1
          onnx_session = ort.InferenceSession(onnx_filename)  RandomForestRegressor.py        159     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.Fail: [ONNXRuntimeError] : 1 : FAIL : Load model from C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\random_forest_regressor_double.onnx failed:Type Er      onnxruntime_inference_collection.py     424     1
      RandomForestRegressor.py finished in 4392 ms            5       1
      

      Fig.118. RandomForestRegressor.py의 결과 (float ONNX)

      그림 118. RandomForestRegressor.py의 결과(float ONNX)


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

      이 코드는 저장된 random_forest_regressor_float.onnxrandom_forest_regressor_double.onnx 모델을 실행하고 MQL5에서 회귀 메트릭을 사용하는 것을 보여 줍니다.

      //+------------------------------------------------------------------+
      //|                                        RandomForestRegressor.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"
      
      #define   ModelName          "RandomForestRegressor"
      #define   ONNXFilenameFloat  "random_forest_regressor_float.onnx"
      #define   ONNXFilenameDouble "random_forest_regressor_double.onnx"
      
      #resource ONNXFilenameFloat  as const uchar ExtModelFloat[];
      #resource ONNXFilenameDouble as const uchar ExtModelDouble[];
      
      #define   TestFloatModel  1
      #define   TestDoubleModel 2
      
      //+------------------------------------------------------------------+
      //| Calculate regression using float values                          |
      //+------------------------------------------------------------------+
      bool RunModelFloat(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         float input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=(float)input_vector[k];
      //--- prepare output tensor
         float output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      //+------------------------------------------------------------------+
      //| Calculate regression using double values                         |
      //+------------------------------------------------------------------+
      bool RunModelDouble(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         double input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=input_vector[k];
      //--- prepare output tensor
         double output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      
      //+------------------------------------------------------------------+
      //| Generate synthetic data                                          |
      //+------------------------------------------------------------------+
      bool GenerateData(const int n,vector &x,vector &y)
        {
         if(n<=0)
            return(false);
      //--- prepare arrays
         x.Resize(n);
         y.Resize(n);
      //---
         for(int i=0; i<n; i++)
           {
            x[i]=(double)1.0*i;
            y[i]=(double)(4*x[i] + 10*sin(x[i]*0.5));
           }
      //---
         return(true);
        }
      
      //+------------------------------------------------------------------+
      //| TestRegressionModel                                              |
      //+------------------------------------------------------------------+
      bool TestRegressionModel(const string model_name,const int model_type)
        {
      //---
         long  model=INVALID_HANDLE;
         ulong flags=ONNX_DEFAULT;
      
         if(model_type==TestFloatModel)
           {
            PrintFormat("\nTesting ONNX float: %s (%s)",model_name,ONNXFilenameFloat);
            model=OnnxCreateFromBuffer(ExtModelFloat,flags);
           }
         else
            if(model_type==TestDoubleModel)
              {
               PrintFormat("\nTesting ONNX double: %s (%s)",model_name,ONNXFilenameDouble);
               model=OnnxCreateFromBuffer(ExtModelDouble,flags);
              }
            else
              {
               PrintFormat("Model type is not incorrect.");
               return(false);
              }
      //--- check
         if(model==INVALID_HANDLE)
           {
            PrintFormat("model_name=%s OnnxCreate error %d",model_name,GetLastError());
            return(false);
           }
      //---
         vector x_values= {};
         vector y_true= {};
         vector y_predicted= {};
      //---
         int n=100;
         GenerateData(n,x_values,y_true);
      //---
         bool run_result=false;
         if(model_type==TestFloatModel)
           {
            run_result=RunModelFloat(model,x_values,y_predicted);
           }
         else
            if(model_type==TestDoubleModel)
              {
               run_result=RunModelDouble(model,x_values,y_predicted);
              }
      //---
         if(run_result)
           {
            PrintFormat("MQL5:   R-Squared (Coefficient of determination): %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_R2));
            PrintFormat("MQL5:   Mean Absolute Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MAE));
            PrintFormat("MQL5:   Mean Squared Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MSE));
           }
         else
            PrintFormat("Error %d",GetLastError());
      //--- release model
         OnnxRelease(model);
      //---
         return(true);
        }
      //+------------------------------------------------------------------+
      //| Script program start function                                    |
      //+------------------------------------------------------------------+
      int OnStart(void)
        {
      //--- test ONNX regression model for float
         TestRegressionModel(ModelName,TestFloatModel);
      //--- test ONNX regression model for double
         TestRegressionModel(ModelName,TestDoubleModel);
      //---
         return(0);
        }
      //+------------------------------------------------------------------+

      출력:

      RandomForestRegressor (EURUSD,H1)       
      RandomForestRegressor (EURUSD,H1)       Testing ONNX float: RandomForestRegressor (random_forest_regressor_float.onnx)
      RandomForestRegressor (EURUSD,H1)       MQL5:   R-Squared (Coefficient of determination): 0.9998854516013125
      RandomForestRegressor (EURUSD,H1)       MQL5:   Mean Absolute Error: 0.9186420704511761
      RandomForestRegressor (EURUSD,H1)       MQL5:   Mean Squared Error: 1.5157912842364190
      RandomForestRegressor (EURUSD,H1)       
      RandomForestRegressor (EURUSD,H1)       Testing ONNX double: RandomForestRegressor (random_forest_regressor_double.onnx)
      RandomForestRegressor (EURUSD,H1)       ONNX: cannot create session (OrtStatus: 1 'Type Error: Type (tensor(double)) of output arg (variable) of node (TreeEnsembleRegressor) does not match expected type (tensor(float)).'), inspect code 'Scripts\Regression\RandomForestRegressor.mq5' (133:16)
      RandomForestRegressor (EURUSD,H1)       model_name=RandomForestRegressor OnnxCreate error 5800
      

      플로트 ONNX 모델은 정상적으로 실행되었지만 ONNX 모델을 더블로 실행할 때는 오류가 발생했습니다.


      2.2.7.3. ONNX representation of the random_forest_regressor_float.onnx and random_forest_regressor_double.onnx


      Fig.119. Netron에서 random_forest_regressor_float.onnx의 ONNX 표현

      그림 119. 네트론에서 random_forest_regressor_float.onnx의 ONNX 표현


      Fig.120. Netron에서 random_forest_regressor_double.onnx의 ONNX 표현

      그림 120. 네트론에서 random_forest_regressor_double.onnx의 ONNX 표현



      2.2.8. sklearn.ensemble.GradientBoostingRegressor

      GradientBoostingRegressor 회귀 작업에 사용되는 머신 러닝 메서드로 앙상블 메서드 제품군의 일부이며 약한 모델을 만들고 경사 부스팅을 사용하여 강한 모델로 결합하는 아이디어를 기반으로 합니다.

      경사 부스팅은 취약한 모델을 반복적으로 추가하고 이전 모델의 오류를 수정하여 모델을 향상시키는 기법입니다.

      GradientBoostingRegressor의 작동 방식은 다음과 같습니다:

      1. 초기화: 피처(독립 변수)와 그에 해당하는 목표 값이 포함된 원본 데이터 집합으로 시작합니다.
      2. 첫 번째 모델: 원본 데이터에 대해 단순 회귀 모델(예: 의사 결정 트리)로 선택한 첫 번째 모델을 훈련하는 것으로 시작됩니다.
      3. 잔차 및 경사 방지: 첫 번째 모델의 예측 값과 실제 목표 변수 값의 차이인 잔차가 계산됩니다. 그런 다음 이 손실 함수의 반구배를 계산하고 이는 모델을 개선할 방향을 나타냅니다.
      4. 다음 모델 구축하기: 다음 모델은 경사 방지(첫 번째 모델의 오류)를 예측하는 데 중점을 두고 구성됩니다. 이 모델은 잔차에 대해 학습되어 첫 번째 모델에 추가됩니다.
      5. 반복: 새 모델을 구성하고 잔여물을 수정하는 과정은 여러 번 반복됩니다. 각각의 새 모델은 이전 모델의 잔차를 고려하여 예측을 개선하는 것을 목표로 합니다.
      6. 모델 조합: 모든 모델의 예측은 중요도에 따라 평균화하거나 가중치를 부여하여 최종 예측으로 결합됩니다.

      GradientBoostingRegressor의 장점:

      • 고성능: 경사 부스팅은 회귀 작업에서 고성능을 달성할 수 있는 강력한 메서드입니다.
      • 이상값에 대한 견고성: 데이터의 이상값을 처리하고 이러한 불확실성을 고려하여 모델을 구축합니다.
      • 자동 피처 선택: 대상 변수를 예측하는 데 가장 중요한 피처를 자동으로 선택합니다.
      • 다양한 손실 함수 처리: 이 메서드를 사용하면 작업에 따라 다양한 손실 함수를 사용할 수 있습니다.

      GradientBoostingRegressor의 한계:

      • 하이퍼파라미터 조정이 필요합니다: 최대 성능을 달성하려면 학습 속도, 트리 깊이, 모델 수와 같은 하이퍼파라미터를 조정해야 합니다.
      • 계산 비용이 많이 듭니다: 경사 부스팅은 특히 데이터 양이 많고 트리 수가 많은 경우 계산 비용이 많이 들 수 있습니다.

      GradientBoostingRegressor는 올바른 하이퍼파라미터 튜닝으로 고성능을 달성하기 위해 실제 작업에서 자주 사용되는 강력한 회귀 메서드입니다.


      2.2.8.1. GradientBoostingRegressor 모델을 생성하고 float 및 double용 ONNX로 내보내기 위한 코드입니다.

      이 코드는 sklearn.ensemble.GradientBoostingRegressor 모델을 생성하고 합성 데이터로 학습시키고 모델을 ONNX 형식으로 저장하고 플로트 및 더블 입력 데이터를 모두 사용하여 예측을 수행합니다. 또한 원본 모델과 ONNX로 내보낸 모델 모두의 정확도를 평가합니다.

      # GradientBoostingRegressor.py
      # 이 코드는 GradientBoostingRegressor 모델을 학습하고, 이를 ONNX 형식(float 및 double 모두)으로 내보내고, ONNX 모델을 사용하여 예측하는 과정을 보여 줍니다.
      # Copyright 2023, MetaQuotes Ltd.
      # https://www.mql5.com


      # 소수점 이하 자릿수를 비교학 위한 함수
      def compare_decimal_places(value1, value2):
      # 두 값을 모두 문자열로 변환
          str_value1 = str(value1)
          str_value2 = str(value2)

      # 문자열에서 소수점 위치 찾기
          dot_position1 = str_value1.find(".")
          dot_position2 = str_value2.find(".")

      # 값 중 소수점이 없는 경우 0을 반환합니다.
          if dot_position1 == -1 or dot_position2 == -1:
              return 0

          # 소수점 이하 자릿수 계산
          decimal_places1 = len(str_value1) - dot_position1 - 1
          decimal_places2 = len(str_value2) - dot_position2 - 1

          # 소수점 이하 두 자리 수 중 최소값 찾기
          min_decimal_places = min(decimal_places1, decimal_places2)

          # 소수점 이하 자릿수가 일치하도록 카운트를 초기화
          matching_count = 0

          # 소수점 이하 문자 비교
          for i in range(1, min_decimal_places + 1):
              if str_value1[dot_position1 + i] == str_value2[dot_position2 + i]:
                  matching_count += 1
              else:
                  break

          return matching_count

      # 필요한 라이브러리 가져오기
      import numpy as np
      import matplotlib.pyplot as plt
      에서 sklearn.ensemble import 그래디언트부스팅레귤레이터
      from sklearn.metrics import r2_score,mean_absolute_error,mean_squared_error
      import onnx
      import onnxruntime as ort
      from skl2onnx import convert_sklearn
      from skl2onnx.common.data_types import FloatTensorType
      from skl2onnx.common.data_types import DoubleTensorType
      from sys import argv

      # 모델 저장 경로를 정의합니다.
      data_path = argv[0]
      last_index = data_path.rfind("\\")+ 1
      data_path = data_path[0:last_index]

      # 회귀를 위한 합성 데이터 생성
      X = np.arange(0,100,1).reshape(-1,1)
      y = 4*X + 10*np.sin(X*0.5)

      model_name = "GradientBoostingRegressor"
      onnx_model_filename = data_path + "gradient_boosting_regressor"

      #경사 부스팅 회귀 모델 만들기
      regression_model = GradientBoostingRegressor()

      # 데이터에 모델 맞추기
      regression_model.fit(X, y.ravel())

      # 전체 데이터 집합에 대한 값 예측
      y_pred = regression_model.predict(X)

      # 모델의 성능 평가
      r2 = r2_score(y, y_pred)
      MSE = MEAN_SQUARE_ERROR(Y, Y_PED)
      MAE = MEAN_ABSOLUTE_ERROR(Y, Y_PED)

      print("\n"+model_name+" Original model (double)")
      print("R-squared (Coefficient of determination):", r2)
      print("Mean Absolute Error:", mae)
      print("Mean Squared Error:", mse)

      # ONNX 모델로 변환 (float)
      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.

      initial_type_float = [('float_input', FloatTensorType([None, X.shape[1]]))]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_float = convert_sklearn(regression_model, initial_types=initial_type_float, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_float.onnx"
      onnx.save_model(onnx_model_float, onnx_filename)

      print("\n"+model_name+" ONNX model (float)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.
      initial_type_float = X.astype(np.float32)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_float = onnx_session.run([output_name], {input_name: initial_type_float})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_float = r2_score(y, y_pred_onnx_float)
      mse_onnx_float = mean_squared_error(y, y_pred_onnx_float)
      mae_onnx_float = mean_absolute_error(y, y_pred_onnx_float)
      print("R-squared (Coefficient of determination)", r2_onnx_float)
      print("Mean Absolute Error:", mae_onnx_float)
      print("Mean Squared Error:", mse_onnx_float)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_float))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_float))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_float))
      print("float ONNX model precision: ",compare_decimal_places(mae, mae_onnx_float))

      # 그림 크기 설정
      plt.figure(figsize=(8,5))
      # 원본 데이터와 회귀선을 그림으로 표시합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with float ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_float.png')

      # ONNX 모델로 변환 (double)
      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.

      initial_type_double = [('double_input', DoubleTensorType([None, X.shape[1]]))]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_double = convert_sklearn(regression_model, initial_types=initial_type_double, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_double.onnx"
      onnx.save_model(onnx_model_double, onnx_filename)

      print("\n"+model_name+" ONNX model (double)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.
      initial_type_double = X.astype(np.float64)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_double = onnx_session.run([output_name], {input_name: initial_type_double})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_double = r2_score(y, y_pred_onnx_double)
      mse_onnx_double = mean_squared_error(y, y_pred_onnx_double)
      mae_onnx_double = mean_absolute_error(y, y_pred_onnx_double)
      print("R-squared (Coefficient of determination)", r2_onnx_double)
      print("Mean Absolute Error:", mae_onnx_double)
      print("Mean Squared Error:", mse_onnx_double)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_double))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_double))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_double))
      print("double ONNX model precision: ",compare_decimal_places(mae, mae_onnx_double))

      # 그림 크기 설정
      plt.figure(figsize=(8,5))
      # 원본 데이터와 회귀선을 그림으로 표시합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with double ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_double.png')

      출력:

      Python  GradientBoostingRegressor Original model (double)
      Python  R-squared (Coefficient of determination): 0.9999959514652565
      Python  Mean Absolute Error: 0.15069342754017417
      Python  Mean Squared Error: 0.053573282108575676
      Python  
      Python  GradientBoostingRegressor ONNX model (float)
      Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\gradient_boosting_regressor_float.onnx
      Python  Information about input tensors in ONNX:
      Python  1. Name: float_input, Data Type: tensor(float), Shape: [None, 1]
      Python  Information about output tensors in ONNX:
      Python  1. Name: variable, Data Type: tensor(float), Shape: [None, 1]
      Python  R-squared (Coefficient of determination) 0.9999959514739537
      Python  Mean Absolute Error: 0.15069457426101718
      Python  Mean Squared Error: 0.05357316702127665
      Python  R^2 matching decimal places:  10
      Python  MAE matching decimal places:  5
      Python  MSE matching decimal places:  6
      Python  float ONNX model precision:  5
      Python  
      Python  GradientBoostingRegressor ONNX model (double)
      Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\gradient_boosting_regressor_double.onnx
      

      오류 탭:

      GradientBoostingRegressor.py started    GradientBoostingRegressor.py    1       1
      Traceback (most recent call last):      GradientBoostingRegressor.py    1       1
          onnx_session = ort.InferenceSession(onnx_filename)  GradientBoostingRegressor.py    161     1
          self._create_inference_session(providers, provider_options, disabled_optimizers)    onnxruntime_inference_collection.py     419     1
          sess = C.InferenceSession(session_options, self._model_path, True, self._read_config_from_model)    onnxruntime_inference_collection.py     452     1
      onnxruntime.capi.onnxruntime_pybind11_state.Fail: [ONNXRuntimeError] : 1 : FAIL : Load model from C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\gradient_boosting_regressor_double.onnx failed:Typ      onnxruntime_inference_collection.py     452     1
      GradientBoostingRegressor.py finished in 3073 ms                5       1
      

      Fig.121. GradientBoostingRegressor.py의 결과 (float ONNX)

      그림 121. GradientBoostingRegressor.py의 결과 (float ONNX)


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

      이 코드는 gradient_boosting_regressor_float.onnxgradient_boosting_regressor_double.onnx 모델을 실행하고 MQL5에서 회귀 메트릭을 사용하는 것을 보여줍니다.

      //+------------------------------------------------------------------+
      //|                                    GradientBoostingRegressor.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"
      
      #define   ModelName          "GradientBoostingRegressor"
      #define   ONNXFilenameFloat  "gradient_boosting_regressor_float.onnx"
      #define   ONNXFilenameDouble "gradient_boosting_regressor_double.onnx"
      
      #resource ONNXFilenameFloat  as const uchar ExtModelFloat[];
      #resource ONNXFilenameDouble as const uchar ExtModelDouble[];
      
      #define   TestFloatModel  1
      #define   TestDoubleModel 2
      
      //+------------------------------------------------------------------+
      //| Calculate regression using float values                          |
      //+------------------------------------------------------------------+
      bool RunModelFloat(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         float input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=(float)input_vector[k];
      //--- prepare output tensor
         float output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      //+------------------------------------------------------------------+
      //| Calculate regression using double values                         |
      //+------------------------------------------------------------------+
      bool RunModelDouble(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         double input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=input_vector[k];
      //--- prepare output tensor
         double output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      
      //+------------------------------------------------------------------+
      //| Generate synthetic data                                          |
      //+------------------------------------------------------------------+
      bool GenerateData(const int n,vector &x,vector &y)
        {
         if(n<=0)
            return(false);
      //--- prepare arrays
         x.Resize(n);
         y.Resize(n);
      //---
         for(int i=0; i<n; i++)
           {
            x[i]=(double)1.0*i;
            y[i]=(double)(4*x[i] + 10*sin(x[i]*0.5));
           }
      //---
         return(true);
        }
      
      //+------------------------------------------------------------------+
      //| TestRegressionModel                                              |
      //+------------------------------------------------------------------+
      bool TestRegressionModel(const string model_name,const int model_type)
        {
      //---
         long  model=INVALID_HANDLE;
         ulong flags=ONNX_DEFAULT;
      
         if(model_type==TestFloatModel)
           {
            PrintFormat("\nTesting ONNX float: %s (%s)",model_name,ONNXFilenameFloat);
            model=OnnxCreateFromBuffer(ExtModelFloat,flags);
           }
         else
            if(model_type==TestDoubleModel)
              {
               PrintFormat("\nTesting ONNX double: %s (%s)",model_name,ONNXFilenameDouble);
               model=OnnxCreateFromBuffer(ExtModelDouble,flags);
              }
            else
              {
               PrintFormat("Model type is not incorrect.");
               return(false);
              }
      //--- check
         if(model==INVALID_HANDLE)
           {
            PrintFormat("model_name=%s OnnxCreate error %d",model_name,GetLastError());
            return(false);
           }
      //---
         vector x_values= {};
         vector y_true= {};
         vector y_predicted= {};
      //---
         int n=100;
         GenerateData(n,x_values,y_true);
      //---
         bool run_result=false;
         if(model_type==TestFloatModel)
           {
            run_result=RunModelFloat(model,x_values,y_predicted);
           }
         else
            if(model_type==TestDoubleModel)
              {
               run_result=RunModelDouble(model,x_values,y_predicted);
              }
      //---
         if(run_result)
           {
            PrintFormat("MQL5:   R-Squared (Coefficient of determination): %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_R2));
            PrintFormat("MQL5:   Mean Absolute Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MAE));
            PrintFormat("MQL5:   Mean Squared Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MSE));
           }
         else
            PrintFormat("Error %d",GetLastError());
      //--- release model
         OnnxRelease(model);
      //---
         return(true);
        }
      //+------------------------------------------------------------------+
      //| Script program start function                                    |
      //+------------------------------------------------------------------+
      int OnStart(void)
        {
      //--- test ONNX regression model for float
         TestRegressionModel(ModelName,TestFloatModel);
      //--- test ONNX regression model for double
         TestRegressionModel(ModelName,TestDoubleModel);
      //---
         return(0);
        }
      //+------------------------------------------------------------------+

      출력:

      GradientBoostingRegressor (EURUSD,H1)   Testing ONNX float: GradientBoostingRegressor (gradient_boosting_regressor_float.onnx)
      GradientBoostingRegressor (EURUSD,H1)   MQL5:   R-Squared (Coefficient of determination): 0.9999959514739537
      GradientBoostingRegressor (EURUSD,H1)   MQL5:   Mean Absolute Error: 0.1506945742610172
      GradientBoostingRegressor (EURUSD,H1)   MQL5:   Mean Squared Error: 0.0535731670212767
      GradientBoostingRegressor (EURUSD,H1)   
      GradientBoostingRegressor (EURUSD,H1)   Testing ONNX double: GradientBoostingRegressor (gradient_boosting_regressor_double.onnx)
      GradientBoostingRegressor (EURUSD,H1)   ONNX: cannot create session (OrtStatus: 1 'Type Error: Type (tensor(double)) of output arg (variable) of node (TreeEnsembleRegressor) does not match expected type (tensor(float)).'), inspect code 'Scripts\Regression\GradientBoostingRegressor.mq5' (133:16)
      GradientBoostingRegressor (EURUSD,H1)   model_name=GradientBoostingRegressor OnnxCreate error 5800
      

      플로트 ONNX 모델은 정상적으로 실행되었지만 ONNX 모델을 더블로 실행할 때는 오류가 발생했습니다.

      파이썬의 원래 배정밀도 모델과 비교:

      Testing ONNX float: GradientBoostingRegressor (gradient_boosting_regressor_float.onnx)
      Python  Mean Absolute Error: 0.15069342754017417
      MQL5:   Mean Absolute Error: 0.1506945742610172
      

      ONNX float MAE의 정확도: 소수점 이하 5자리.


      2.2.8.3. gradient_boosting_regressor_float.onnx 및 gradient_boosting_regressor_double.onnx의 ONNX 표현


      Fig.122. 네트론에서 그라데이션_부스팅_리그레서_플로트.onnx의 ONNX 표현

      그림 122. 네트론에서 gradient_boosting_regressor_float.onnx의 ONNX 표현


      Fig.123. Netron에서 gradient_boosting_regressor_double.onnx의 ONNX 표현

      그림 123. 네트론에서 gradient_boosting_regressor_double.onnx의 ONNX 표현



      2.2.9. sklearn.ensemble.HistGradientBoostingRegressor

      HistGradientBoostingRegressor는 대규모 데이터 세트 작업에 최적화된 그라데이션 부스팅의 변형을 나타내는 머신 러닝 메서드입니다.

      이 메서드은 회귀 작업에 사용되며 히스토그램 기반의 메서드를 사용하여 학습 프로세스를 신속하게 처리한다는 의미에서 'Hist'라는 이름이 붙었습니다.

      HistGradientBoostingRegressor 작동 방식:

      1. 초기화: 피처(독립 변수)와 그에 해당하는 목표 값이 포함된 원본 데이터 집합으로 시작합니다.
      2. 히스토그램 기반 메서드: 트리 노드에서 데이터를 정확하게 분할하는 대신 HistGradientBoostingRegressor는 히스토그램 기반 메서드를 사용하여 히스토그램 형태로 데이터를 효율적으로 표현합니다. 이는 특히 대규모 데이터 세트에서 학습 프로세스의 속도를 크게 높여줍니다.
      3. 기본 트리를 구축: 이 메서드는 데이터의 히스토그램적인 표현을 사용하여 '히스토그램 의사 결정 트리'라고 하는 기본 의사 결정 트리 집합을 구성합니다. 이 트리는 경사 부스팅을 기반으로 제작되며 이전 모델의 잔차에 맞게 조정됩니다.
      4. 점진적 학습: HistGradientBoostingRegressor는 앙상블에 새 트리를 점진적으로 추가하며 각 트리는 이전 트리의 잔차를 보정합니다.
      5. 모델 조합: 기본 트리를 구축한 후 모든 트리의 예측을 결합하여 최종 예측을 얻습니다.

      HistGradientBoostingRegressor의 장점:

      • 고성능: 이 메서드는 대량의 데이터를 처리하는 데 최적화되어 있으며 고성능을 달성할 수 있습니다.
      • 노이즈 견고성: HistGradientBoostingRegressor는 일반적으로 데이터에 노이즈가 있는 경우에도 잘 작동합니다.
      • 고차원적 효율성: 이 메서드는 많은 수의 피처(고차원 데이터)가 있는 작업을 처리할 수 있습니다.
      • 뛰어난 병렬화: 여러 프로세서에서 교육을 효율적으로 병렬화할 수 있습니다.

      HistGradientBoostingRegressor의 한계:

      • 하이퍼파라미터 튜닝이 필요합니다: 최대 성능을 달성하려면 트리 깊이 및 모델 수와 같은 하이퍼파라미터를 조정해야 합니다.
      • 선형 모델보다 해석 가능성이 낮습니다: 다른 앙상블 메서드와 마찬가지로 HistGradientBoostingRegressor는 선형 회귀와 같은 단순한 모델보다 해석이 어렵습니다.

      고성능과 고차원 데이터 효율성이 필수인 대규모 데이터 집합을 포함하는 작업에 유용한 회귀 메서드가 될 수 있습니다.


      2.2.9.1. HistGradientBoostingRegressor 모델을 생성하고 float 및 double용 ONNX로 내보내기 위한 코드

      이 코드는 sklearn.ensemble.HistGradientBoostingRegressor 모델을 생성하고 합성 데이터로 학습시키고 모델을 ONNX 형식으로 저장하고 플로트 및 더블 입력 데이터를 모두 사용하여 예측을 수행합니다. 또한 원본 모델과 ONNX로 내보낸 모델 모두의 정확도를 평가합니다.

      # HistGradientBoostingRegressor.py
      # 이 코드는 HistGradientBoostingRegressor 모델을 훈련하고 이를 ONNX 형식(float 및 double 모두)으로 내보내고 ONNX 모델을 사용하여 예측하는 과정을 보여 줍니다.
      # Copyright 2023, MetaQuotes Ltd.
      # https://www.mql5.com


      # 소수점 이하 자릿수를 비교학 위한 함수
      def compare_decimal_places(value1, value2):
      # 두 값을 모두 문자열로 변환
          str_value1 = str(value1)
          str_value2 = str(value2)

      # 문자열에서 소수점 위치 찾기
          dot_position1 = str_value1.find(".")
          dot_position2 = str_value2.find(".")

      # 값 중 소수점이 없는 경우 0을 반환합니다.
          if dot_position1 == -1 or dot_position2 == -1:
              return 0

          # 소수점 이하 자릿수 계산
          decimal_places1 = len(str_value1) - dot_position1 - 1
          decimal_places2 = len(str_value2) - dot_position2 - 1

          # 소수점 이하 두 자리 수 중 최소값 찾기
          min_decimal_places = min(decimal_places1, decimal_places2)

          # 소수점 이하 자릿수가 일치하도록 카운트를 초기화
          matching_count = 0

          # 소수점 이하 문자 비교
          for i in range(1, min_decimal_places + 1):
              if str_value1[dot_position1 + i] == str_value2[dot_position2 + i]:
                  matching_count += 1
              else:
                  break

          return matching_count

      # 필요한 라이브러리 가져오기
      import numpy as np
      import matplotlib.pyplot as plt
      from sklearn.ensemble import HistGradientBoostingRegressor
      from sklearn.metrics import r2_score,mean_absolute_error,mean_squared_error
      import onnx
      import onnxruntime as ort
      from skl2onnx import convert_sklearn
      from skl2onnx.common.data_types import FloatTensorType
      from skl2onnx.common.data_types import DoubleTensorType
      from sys import argv

      # 모델 저장 경로를 정의합니다.
      data_path = argv[0]
      last_index = data_path.rfind("\\")+ 1
      data_path = data_path[0:last_index]

      # 회귀를 위한 합성 데이터 생성
      X = np.arange(0,100,1).reshape(-1,1)
      y = 4*X + 10*np.sin(X*0.5)

      model_name = "HistGradientBoostingRegressor"
      onnx_model_filename = data_path + "hist_gradient_boosting_regressor"

      # 히스토그램 기반 Gradient Boosting Regressor 모델 만들기
      hist_gradient_boosting_model = HistGradientBoostingRegressor()

      # 데이터에 모델 맞추기
      hist_gradient_boosting_model.fit(X, y.ravel())

      # 전체 데이터 집합에 대한 값 예측
      y_pred = hist_gradient_boosting_model.predict(X)

      # 모델의 성능 평가
      r2 = r2_score(y, y_pred)
      MSE = MEAN_SQUARE_ERROR(Y, Y_PED)
      MAE = MEAN_ABSOLUTE_ERROR(Y, Y_PED)

      print("\n"+model_name+" Original model (double)")
      print("R-squared (Coefficient of determination):", r2)
      print("Mean Absolute Error:", mae)
      print("Mean Squared Error:", mse)

      # ONNX 모델로 변환 (float)
      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.

      initial_type_float = [('float_input', FloatTensorType([None, X.shape[1]])]]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_float = convert_sklearn(hist_gradient_boosting_model, initial_types=initial_type_float, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_float.onnx"
      onnx.save_model(onnx_model_float, onnx_filename)

      print("\n"+model_name+" ONNX model (float)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.
      initial_type_float = X.astype(np.float32)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_float = onnx_session.run([output_name], {input_name: initial_type_float})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_float = r2_score(y, y_pred_onnx_float)
      mse_onnx_float = mean_squared_error(y, y_pred_onnx_float)
      mae_onnx_float = mean_absolute_error(y, y_pred_onnx_float)
      print("R-squared (Coefficient of determination)", r2_onnx_float)
      print("Mean Absolute Error:", mae_onnx_float)
      print("Mean Squared Error:", mse_onnx_float)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_float))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_float))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_float))
      print("float ONNX model precision: ",compare_decimal_places(mae, mae_onnx_float))

      # 그림 크기 설정
      plt.figure(figsize=(8,5))
      # 원본 데이터와 회귀선을 그림으로 표시합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with float ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_float.png')

      # ONNX 모델로 변환 (double)
      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.
      initial_type_double = [('double_input', DoubleTensorType([None, X.shape[1]]))]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_double = convert_sklearn(hist_gradient_boosting_model, initial_types=initial_type_double, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_double.onnx"
      onnx.save_model(onnx_model_double, onnx_filename)

      print("\n"+model_name+" ONNX model (double)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.
      initial_type_double = X.astype(np.float64)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_double = onnx_session.run([output_name], {input_name: initial_type_double})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_double = r2_score(y, y_pred_onnx_double)
      mse_onnx_double = mean_squared_error(y, y_pred_onnx_double)
      mae_onnx_double = mean_absolute_error(y, y_pred_onnx_double)
      print("R-squared (Coefficient of determination)", r2_onnx_double)
      print("Mean Absolute Error:", mae_onnx_double)
      print("Mean Squared Error:", mse_onnx_double)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_double))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_double))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_double))
      print("double ONNX model precision: ",compare_decimal_places(mae, mae_onnx_double))

      # 그림 크기 설정
      plt.figure(figsize=(8,5))
      # 원본 데이터와 회귀선을 그림으로 표시합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with double ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_double.png')

      출력:

      Python  HistGradientBoostingRegressor Original model (double)
      Python  R-squared (Coefficient of determination): 0.9833421349506157
      Python  Mean Absolute Error: 9.070567104488434
      Python  Mean Squared Error: 220.4295035561544
      Python  
      Python  HistGradientBoostingRegressor ONNX model (float)
      Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\hist_gradient_boosting_regressor_float.onnx
      Python  Information about input tensors in ONNX:
      Python  1. Name: float_input, Data Type: tensor(float), Shape: [None, 1]
      Python  Information about output tensors in ONNX:
      Python  1. Name: variable, Data Type: tensor(float), Shape: [None, 1]
      Python  R-squared (Coefficient of determination) 0.9833421351962779
      Python  Mean Absolute Error: 9.07056497799043
      Python  Mean Squared Error: 220.42950030536645
      Python  R^2 matching decimal places:  8
      Python  MAE matching decimal places:  5
      Python  MSE matching decimal places:  5
      Python  float ONNX model precision:  5
      Python  
      Python  HistGradientBoostingRegressor ONNX model (double)
      Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\hist_gradient_boosting_regressor_double.onnx
      

      오류 탭:

      HistGradientBoostingRegressor.py started        HistGradientBoostingRegressor.py        1       1
      Traceback (most recent call last):      HistGradientBoostingRegressor.py        1       1
          onnx_session = ort.InferenceSession(onnx_filename)  HistGradientBoostingRegressor.py        161     1
          self._create_inference_session(providers, provider_options, disabled_optimizers)    onnxruntime_inference_collection.py     419     1
          sess = C.InferenceSession(session_options, self._model_path, True, self._read_config_from_model)    onnxruntime_inference_collection.py     452     1
      onnxruntime.capi.onnxruntime_pybind11_state.Fail: [ONNXRuntimeError] : 1 : FAIL : Load model from C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\hist_gradient_boosting_regressor_double.onnx faile      onnxruntime_inference_collection.py     452     1
      HistGradientBoostingRegressor.py finished in 3100 ms            5       1
      

      Fig.124. 히스트 그래디언트 부스팅 레귤레이터.py 결과 (float ONNX)


      그림 124. HistGradientBoostingRegressor.py의 결과 (float ONNX)


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

      이 코드는 저장된 hist_gradient_boosting_regressor_float.onnxhist_gradient_boosting_regressor_double.onnx 모델을 실행하고 MQL5에서 회귀 메트릭을 사용하는 것을 보여 줍니다.

      //+------------------------------------------------------------------+
      //|                                HistGradientBoostingRegressor.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"
      
      #define   ModelName          "HistGradientBoostingRegressor"
      #define   ONNXFilenameFloat  "hist_gradient_boosting_regressor_float.onnx"
      #define   ONNXFilenameDouble "hist_gradient_boosting_regressor_double.onnx"
      
      #resource ONNXFilenameFloat  as const uchar ExtModelFloat[];
      #resource ONNXFilenameDouble as const uchar ExtModelDouble[];
      
      #define   TestFloatModel  1
      #define   TestDoubleModel 2
      
      //+------------------------------------------------------------------+
      //| Calculate regression using float values                          |
      //+------------------------------------------------------------------+
      bool RunModelFloat(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         float input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=(float)input_vector[k];
      //--- prepare output tensor
         float output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      //+------------------------------------------------------------------+
      //| Calculate regression using double values                         |
      //+------------------------------------------------------------------+
      bool RunModelDouble(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         double input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=input_vector[k];
      //--- prepare output tensor
         double output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      
      //+------------------------------------------------------------------+
      //| Generate synthetic data                                          |
      //+------------------------------------------------------------------+
      bool GenerateData(const int n,vector &x,vector &y)
        {
         if(n<=0)
            return(false);
      //--- prepare arrays
         x.Resize(n);
         y.Resize(n);
      //---
         for(int i=0; i<n; i++)
           {
            x[i]=(double)1.0*i;
            y[i]=(double)(4*x[i] + 10*sin(x[i]*0.5));
           }
      //---
         return(true);
        }
      
      //+------------------------------------------------------------------+
      //| TestRegressionModel                                              |
      //+------------------------------------------------------------------+
      bool TestRegressionModel(const string model_name,const int model_type)
        {
      //---
         long  model=INVALID_HANDLE;
         ulong flags=ONNX_DEFAULT;
      
         if(model_type==TestFloatModel)
           {
            PrintFormat("\nTesting ONNX float: %s (%s)",model_name,ONNXFilenameFloat);
            model=OnnxCreateFromBuffer(ExtModelFloat,flags);
           }
         else
            if(model_type==TestDoubleModel)
              {
               PrintFormat("\nTesting ONNX double: %s (%s)",model_name,ONNXFilenameDouble);
               model=OnnxCreateFromBuffer(ExtModelDouble,flags);
              }
            else
              {
               PrintFormat("Model type is not incorrect.");
               return(false);
              }
      //--- check
         if(model==INVALID_HANDLE)
           {
            PrintFormat("model_name=%s OnnxCreate error %d",model_name,GetLastError());
            return(false);
           }
      //---
         vector x_values= {};
         vector y_true= {};
         vector y_predicted= {};
      //---
         int n=100;
         GenerateData(n,x_values,y_true);
      //---
         bool run_result=false;
         if(model_type==TestFloatModel)
           {
            run_result=RunModelFloat(model,x_values,y_predicted);
           }
         else
            if(model_type==TestDoubleModel)
              {
               run_result=RunModelDouble(model,x_values,y_predicted);
              }
      //---
         if(run_result)
           {
            PrintFormat("MQL5:   R-Squared (Coefficient of determination): %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_R2));
            PrintFormat("MQL5:   Mean Absolute Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MAE));
            PrintFormat("MQL5:   Mean Squared Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MSE));
           }
         else
            PrintFormat("Error %d",GetLastError());
      //--- release model
         OnnxRelease(model);
      //---
         return(true);
        }
      //+------------------------------------------------------------------+
      //| Script program start function                                    |
      //+------------------------------------------------------------------+
      int OnStart(void)
        {
      //--- test ONNX regression model for float
         TestRegressionModel(ModelName,TestFloatModel);
      //--- test ONNX regression model for double
         TestRegressionModel(ModelName,TestDoubleModel);
      //---
         return(0);
        }
      //+------------------------------------------------------------------+

      출력:

      HistGradientBoostingRegressor (EURUSD,H1)       Testing ONNX float: HistGradientBoostingRegressor (hist_gradient_boosting_regressor_float.onnx)
      HistGradientBoostingRegressor (EURUSD,H1)       MQL5:   R-Squared (Coefficient of determination): 0.9833421351962779
      HistGradientBoostingRegressor (EURUSD,H1)       MQL5:   Mean Absolute Error: 9.0705649779904292
      HistGradientBoostingRegressor (EURUSD,H1)       MQL5:   Mean Squared Error: 220.4295003053665312
      HistGradientBoostingRegressor (EURUSD,H1)       
      HistGradientBoostingRegressor (EURUSD,H1)       Testing ONNX double: HistGradientBoostingRegressor (hist_gradient_boosting_regressor_double.onnx)
      HistGradientBoostingRegressor (EURUSD,H1)       ONNX: cannot create session (OrtStatus: 1 'Type Error: Type (tensor(double)) of output arg (variable) of node (TreeEnsembleRegressor) does not match expected type (tensor(float)).'), inspect code 'Scripts\Regression\HistGradientBoostingRegressor.mq5' (133:16)
      HistGradientBoostingRegressor (EURUSD,H1)       model_name=HistGradientBoostingRegressor OnnxCreate error 5800
      

      플로트 ONNX 모델은 정상적으로 실행되었지만 ONNX 모델을 더블로 실행할 때는 오류가 발생했습니다.

      파이썬의 원래 배정밀도 모델과 비교:

      Testing ONNX float: HistGradientBoostingRegressor (hist_gradient_boosting_regressor_float.onnx)
      Python  Mean Absolute Error: 9.070567104488434
      MQL5:   Mean Absolute Error: 9.0705649779904292

      ONNX float MAE의 정확도: 소수점 이하 5자리


      2.2.9.3. hist_gradient_boosting_regressor_float.onnx 및 hist_gradient_boosting_regressor_double.onnx의 ONNX 표현  


      Fig.125. Netron에서 hist_gradient_boosting_regressor_float.onnx의 ONNX 표현

      그림 125. 네트론에서 hist_gradient_boosting_regressor_float.onnx의 ONNX 표현


      그림 126. Netron에서 hist_gradient_boosting_regressor_double.onnx의 ONNX 표현

      그림 126. 네트론에서 hist_gradient_boosting_regressor_double.onnx의 ONNX 표현



      2.2.10. sklearn.svm.SVR

      SVR(Support Vector Regression)는 회귀 작업에 사용되는 머신 러닝 메서드입니다. 분류를 위한 Support Vector Machine(SVM)과 동일한 개념을 기반으로 하지만 회귀에 맞게 조정되었습니다. SVR의 주요 목표는 데이터 포인트와 회귀선 사이의 최대 평균 거리에 의존하여 대상 변수의 연속 값을 예측하는 것입니다.

      SVR 작동 방식:

      1. 경계 정의: SVM과 마찬가지로 SVR은 서로 다른 클래스의 데이터 포인트를 구분하는 경계를 구성합니다. 클래스 분리 대신 SVR은 데이터 포인트 주위에 '튜브'를 만들고, 튜브의 너비는 하이퍼파라미터로 제어하는 것을 목표로 합니다.
      2. 목표 변수 및 손실 함수: 분류에서와 같이 클래스를 사용하는 대신 SVR은 대상 변수의 연속형 값을 처리합니다. 예측값과 실제값의 제곱 차이와 같은 손실 함수를 사용하여 측정한 예측 오차를 최소화합니다.
      3. 정규화: 또한 SVR은 정규화를 지원하여 모델 복잡성을 제어하고 과적합을 방지하는 데 도움을 줍니다.
      4. 커널 함수: SVR은 일반적으로 피처와 대상 변수 간의 비선형 종속성을 처리할 수 있는 커널 함수를 사용합니다. 널리 사용되는 커널 함수에는 방사형 기저 함수(RBF), 다항식, 선형 함수가 있습니다.

      SVR의 장점:

      • 이상값에 대한 견고성: SVR은 예측 오류를 최소화하는 것을 목표로 하므로 데이터의 이상값을 처리할 수 있습니다.
      • 비선형 종속성 지원: 커널 함수를 사용하면 SVR이 피처와 대상 변수 간의 복잡하고 비선형적인 종속성을 모델링할 수 있습니다.
      • 높은 예측 품질: 정밀한 예측이 필요한 회귀 작업에서 SVR은 고품질의 결과를 제공할 수 있습니다.

      SVR의 한계:

      • 하이퍼파라미터에 대한 민감도: 커널 함수 및 튜브 너비(하이퍼파라미터)와 같은 모델 파라미터를 선택하려면 신중한 튜닝과 최적화가 필요할 수 있습니다.
      • 계산의 복잡성: 특히 복잡한 커널 함수와 대규모 데이터 세트를 사용할 때 SVR 모델을 훈련하는 것은 상당한 계산력을 필요로할 수 있습니다.

      SVR은 데이터 포인트 주위에 '튜브'를 구성하여 예측 오류를 최소화하는 방식을 기반으로 하는 회귀 작업을 위한 머신 러닝 메서드입니다. 이상값에 대한 견고성과 비선형 종속성을 처리할 수 있어 다양한 회귀 작업에 유용합니다.

      2.2.10.1. SVR 모델을 생성하고 float 및 double을 사용하는 ONNX로 내보내기 위한 코드

      이 코드는 sklearn.svm.SVR 모델을 생성하고 합성 데이터로 학습시키고 모델을 ONNX 형식으로 저장하고 플로트 및 더블 입력 데이터를 모두 사용하여 예측을 수행합니다. 또한 원본 모델과 ONNX로 내보낸 모델 모두의 정확도를 평가합니다.

      # SVR.py
      # 이 코드는 SVR 모델을 학습하고 이를 ONNX 형식(float 및 double)으로 내보내고 ONNX 모델을 사용하여 예측하는 과정을 보여 줍니다.
      # Copyright 2023, MetaQuotes Ltd.
      # https://www.mql5.com


      # 소수점 이하 자릿수를 비교학 위한 함수
      def compare_decimal_places(value1, value2):
      # 두 값을 모두 문자열로 변환
          str_value1 = str(value1)
          str_value2 = str(value2)

      # 문자열에서 소수점 위치 찾기
          dot_position1 = str_value1.find(".")
          dot_position2 = str_value2.find(".")

      # 값 중 소수점이 없는 경우 0을 반환합니다.
          if dot_position1 == -1 or dot_position2 == -1:
              return 0

          # 소수점 이하 자릿수 계산
          decimal_places1 = len(str_value1) - dot_position1 - 1
          decimal_places2 = len(str_value2) - dot_position2 - 1

          # 소수점 이하 두 자리 수 중 최소값 찾기
          min_decimal_places = min(decimal_places1, decimal_places2)

          # 소수점 이하 자릿수가 일치하도록 카운트를 초기화
          matching_count = 0

          # 소수점 이하 문자 비교
          for i in range(1, min_decimal_places + 1):
              if str_value1[dot_position1 + i] == str_value2[dot_position2 + i]:
                  matching_count += 1
              else:
                  break

          return matching_count

      # 필요한 라이브러리 가져오기
      import numpy as np
      import matplotlib.pyplot as plt
      에서 sklearn.svm import SVR
      from sklearn.metrics import r2_score,mean_absolute_error,mean_squared_error
      import onnx
      import onnxruntime as ort
      from skl2onnx import convert_sklearn
      from skl2onnx.common.data_types import FloatTensorType
      from skl2onnx.common.data_types import DoubleTensorType
      from sys import argv

      # 모델 저장 경로를 정의합니다.
      data_path = argv[0]
      last_index = data_path.rfind("\\")+ 1
      data_path = data_path[0:last_index]

      # 회귀를 위한 합성 데이터 생성
      X = np.arange(0,100,1).reshape(-1,1)
      y = 4*X + 10*np.sin(X*0.5)

      model_name = "SVR"
      onnx_model_filename = data_path + "svr"

      # SVR 모델 만들기
      regression_model = SVR()

      # 데이터에 모델 맞추기
      regression_model.fit(X, y.ravel())

      # 전체 데이터 집합에 대한 값 예측
      y_pred = regression_model.predict(X)

      # 모델의 성능 평가
      r2 = r2_score(y, y_pred)
      MSE = MEAN_SQUARE_ERROR(Y, Y_PED)
      mae = mean_absolute_error(y, y_pred)

      print("\n"+model_name+" Original model (double)")
      print("R-squared (Coefficient of determination):", r2)
      print("Mean Absolute Error:", mae)
      print("Mean Squared Error:", mse)

      # ONNX 모델로 변환 (float)
      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.

      initial_type_float = [('float_input', FloatTensorType([None, X.shape[1]])]]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_float = convert_sklearn(regression_model, initial_types=initial_type_float, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_float.onnx"
      onnx.save_model(onnx_model_float, onnx_filename)

      print("\n"+model_name+" ONNX model (float)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.
      initial_type_float = X.astype(np.float32)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_float = onnx_session.run([output_name], {input_name: initial_type_loat})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_float = r2_score(y, y_pred_onnx_float)
      mse_onnx_float = mean_squared_error(y, y_pred_onnx_float)
      mae_onnx_float = mean_absolute_error(y, y_pred_onnx_float)
      print("R-squared (Coefficient of determination)", r2_onnx_float)
      print("Mean Absolute Error:", mae_onnx_float)
      print("Mean Squared Error:", mse_onnx_float)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_float))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_float))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_float))
      print("float ONNX model precision: ",compare_decimal_places(mae, mae_onnx_float))

      # 그림 크기 설정
      plt.figure(figsize=(8,5))
      # 원본 데이터와 회귀선을 그림으로 표시합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with float ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_float.png')

      # ONNX 모델로 변환 (double)
      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.

      initial_type_double = [('double_input', DoubleTensorType([None, X.shape[1]]))]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_double = convert_sklearn(regression_model, initial_types=initial_type_double, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_double.onnx"
      onnx.save_model(onnx_model_double, onnx_filename)

      print("\n"+model_name+" ONNX model (double)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.
      initial_type_double = X.astype(np.float64)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_double = onnx_session.run([output_name], {input_name: initial_type_double})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_double = r2_score(y, y_pred_onnx_double)
      mse_onnx_double = mean_squared_error(y, y_pred_onnx_double)
      mae_onnx_double = mean_absolute_error(y, y_pred_onnx_double)
      print("R-squared (Coefficient of determination)", r2_onnx_double)
      print("Mean Absolute Error:", mae_onnx_double)
      print("Mean Squared Error:", mse_onnx_double)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_double))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_double))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_double))
      print("double ONNX model precision: ",compare_decimal_places(mae, mae_onnx_double))

      # 그림 크기 설정
      plt.figure(figsize=(8,5))
      # 원본 데이터와 회귀선을 그림으로 표시합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with double ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_double.png')

      출력:

      Python  SVR Original model (double)
      Python  R-squared (Coefficient of determination): 0.398243655775797
      Python  Mean Absolute Error: 73.63683696034649
      Python  Mean Squared Error: 7962.89631509593
      Python  
      Python  SVR ONNX model (float)
      Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\svr_float.onnx
      Python  Information about input tensors in ONNX:
      Python  1. Name: float_input, Data Type: tensor(float), Shape: [None, 1]
      Python  Information about output tensors in ONNX:
      Python  1. Name: variable, Data Type: tensor(float), Shape: [None, 1]
      Python  R-squared (Coefficient of determination) 0.3982436352100983
      Python  Mean Absolute Error: 73.63683840363255
      Python  Mean Squared Error: 7962.896587236852
      Python  R^2 matching decimal places:  7
      Python  MAE matching decimal places:  5
      Python  MSE matching decimal places:  3
      Python  float ONNX model precision:  5
      Python  
      Python  SVR ONNX model (double)
      Python  ONNX model saved to C:\Users\user\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Scripts\Regression\svr_double.onnx
      

      Fig.127. SVR.py (float ONNX) 결과

      그림 127. SVR.py 결과(float ONNX)


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

      이 코드는 저장된 svr_float.onnxsvr_double.onnx 모델을 실행하고 MQL5에서 회귀 메트릭을 사용하는 방법을 보여줍니다.

      //+------------------------------------------------------------------+
      //|                                                          SVR.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"
      
      #define   ModelName          "SVR"
      #define   ONNXFilenameFloat  "svr_float.onnx"
      #define   ONNXFilenameDouble "svr_double.onnx"
      
      #resource ONNXFilenameFloat  as const uchar ExtModelFloat[];
      #resource ONNXFilenameDouble as const uchar ExtModelDouble[];
      
      #define   TestFloatModel  1
      #define   TestDoubleModel 2
      
      //+------------------------------------------------------------------+
      //| Calculate regression using float values                          |
      //+------------------------------------------------------------------+
      bool RunModelFloat(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         float input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=(float)input_vector[k];
      //--- prepare output tensor
         float output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      //+------------------------------------------------------------------+
      //| Calculate regression using double values                         |
      //+------------------------------------------------------------------+
      bool RunModelDouble(long model,vector &input_vector, vector &output_vector)
        {
      //--- check number of input samples
         ulong batch_size=input_vector.Size();
         if(batch_size==0)
            return(false);
      //--- prepare output array
         output_vector.Resize((int)batch_size);
      //--- prepare input tensor
         double input_data[];
         ArrayResize(input_data,(int)batch_size);
      //--- set input shape
         ulong input_shape[]= {batch_size, 1};
         OnnxSetInputShape(model,0,input_shape);
      //--- copy data to the input tensor
         for(int k=0; k<(int)batch_size; k++)
            input_data[k]=input_vector[k];
      //--- prepare output tensor
         double output_data[];
         ArrayResize(output_data,(int)batch_size);
      //--- set output shape
         ulong output_shape[]= {batch_size,1};
         OnnxSetOutputShape(model,0,output_shape);
      //--- run the model
         bool res=OnnxRun(model,ONNX_DEBUG_LOGS,input_data,output_data);
      //--- copy output to vector
         if(res)
           {
            for(int k=0; k<(int)batch_size; k++)
               output_vector[k]=output_data[k];
           }
      //---
         return(res);
        }
      
      //+------------------------------------------------------------------+
      //| Generate synthetic data                                          |
      //+------------------------------------------------------------------+
      bool GenerateData(const int n,vector &x,vector &y)
        {
         if(n<=0)
            return(false);
      //--- prepare arrays
         x.Resize(n);
         y.Resize(n);
      //---
         for(int i=0; i<n; i++)
           {
            x[i]=(double)1.0*i;
            y[i]=(double)(4*x[i] + 10*sin(x[i]*0.5));
           }
      //---
         return(true);
        }
      
      //+------------------------------------------------------------------+
      //| TestRegressionModel                                              |
      //+------------------------------------------------------------------+
      bool TestRegressionModel(const string model_name,const int model_type)
        {
      //---
         long  model=INVALID_HANDLE;
         ulong flags=ONNX_DEFAULT;
      
         if(model_type==TestFloatModel)
           {
            PrintFormat("\nTesting ONNX float: %s (%s)",model_name,ONNXFilenameFloat);
            model=OnnxCreateFromBuffer(ExtModelFloat,flags);
           }
         else
            if(model_type==TestDoubleModel)
              {
               PrintFormat("\nTesting ONNX double: %s (%s)",model_name,ONNXFilenameDouble);
               model=OnnxCreateFromBuffer(ExtModelDouble,flags);
              }
            else
              {
               PrintFormat("Model type is not incorrect.");
               return(false);
              }
      //--- check
         if(model==INVALID_HANDLE)
           {
            PrintFormat("model_name=%s OnnxCreate error %d",model_name,GetLastError());
            return(false);
           }
      //---
         vector x_values= {};
         vector y_true= {};
         vector y_predicted= {};
      //---
         int n=100;
         GenerateData(n,x_values,y_true);
      //---
         bool run_result=false;
         if(model_type==TestFloatModel)
           {
            run_result=RunModelFloat(model,x_values,y_predicted);
           }
         else
            if(model_type==TestDoubleModel)
              {
               run_result=RunModelDouble(model,x_values,y_predicted);
              }
      //---
         if(run_result)
           {
            PrintFormat("MQL5:   R-Squared (Coefficient of determination): %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_R2));
            PrintFormat("MQL5:   Mean Absolute Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MAE));
            PrintFormat("MQL5:   Mean Squared Error: %.16f",y_predicted.RegressionMetric(y_true,REGRESSION_MSE));
           }
         else
            PrintFormat("Error %d",GetLastError());
      //--- release model
         OnnxRelease(model);
      //---
         return(true);
        }
      //+------------------------------------------------------------------+
      //| Script program start function                                    |
      //+------------------------------------------------------------------+
      int OnStart(void)
        {
      //--- test ONNX regression model for float
         TestRegressionModel(ModelName,TestFloatModel);
      //--- test ONNX regression model for double
         TestRegressionModel(ModelName,TestDoubleModel);
      //---
         return(0);
        }
      //+------------------------------------------------------------------+

      출력:

      SVR (EURUSD,H1) Testing ONNX float: SVR (svr_float.onnx)
      SVR (EURUSD,H1) MQL5:   R-Squared (Coefficient of determination): 0.3982436352100981
      SVR (EURUSD,H1) MQL5:   Mean Absolute Error: 73.6368384036325523
      SVR (EURUSD,H1) MQL5:   Mean Squared Error: 7962.8965872368517012
      SVR (EURUSD,H1) 
      SVR (EURUSD,H1) Testing ONNX double: SVR (svr_double.onnx)
      SVR (EURUSD,H1) ONNX: cannot create session (OrtStatus: 9 'Could not find an implementation for SVMRegressor(1) node with name 'SVM''), inspect code 'Scripts\R\SVR.mq5' (133:16)
      SVR (EURUSD,H1) model_name=SVR OnnxCreate error 5800
      

      플로트 ONNX 모델은 정상적으로 실행되었지만 ONNX 모델을 더블로 실행할 때는 오류가 발생했습니다.

      파이썬의 원래 배정밀도 모델과 비교:

      Testing ONNX float: SVR (svr_float.onnx)
      Python  Mean Absolute Error: 73.63683696034649
      MQL5:   Mean Absolute Error: 73.6368384036325523

      ONNX float MAE의 정확도: 소수점 이하 5자리


      2.2.10.3. svr_float.onnx 및 svr_double.onnx의 ONNX 표현

      Fig.128. 넷트론에서 svr_float.onnx의 ONNX 표현

      그림 128. 네트론에서 svr_float.onnx의 ONNX 표현


      Fig.129. 넷트론에서 svr_double.onnx의 ONNX 표현

      그림 129. 네트론에서 svr_double.onnx의 ONNX 표현


      2.3. ONNX로 변환할 때 문제가 발생한 회귀 모델

      일부 회귀 모델은 sklearn-onnx 변환기를 통해 ONNX 형식으로 변환할 수 없습니다.


      2.3.1. sklearn.dummy.DummyRegressor

      https://scikit-learn.org/stable/modules/generated/sklearn.dummy.DummyRegressor.html titletitleDummyRegressor는 회귀 작업에서 간단한 규칙을 사용하여 대상 변수를 예측하는 기준 모델을 만드는 데 사용되는 머신 러닝 메서드이며 다른 복잡한 모델과 비교하고 성능을 평가하는 데 유용합니다. 이 메서드는 다른 회귀 모델의 품질을 평가하는 맥락에서 자주 사용됩니다.

      DummyRegressor는 예측을 위한 몇 가지 전략을 제공합니다:

      1. "mean"(기본값): DummyRegressor는 훈련 데이터 세트에서 목표 변수의 평균값을 예측합니다. 이 전략은 단순히 평균을 예측하는 것보다 다른 모델이 얼마나 더 나은지 판단하는 데 유용합니다.
      2. "median": DummyRegressor는 훈련 데이터 세트에서 목표 변수의 중앙값을 예측합니다.
      3. "quantile": DummyRegressor는 훈련 데이터 세트에서 대상 변수의 사분위수 값(사분위수 매개변수로 지정)을 예측합니다.
      4. "constant": DummyRegressor는 사용자가 설정한 상수 값을 예측합니다(전략 매개변수 사용).

      DummyRegressor의 장점:

      • 성과 평가: DummyRegressor는 복잡한 모델의 성능을 평가하는 데 유용합니다. 모델이 DummyRegressor의 예측을 능가하지 못한다면 모델에 문제가 있는 것일 수 있습니다.
      • 기준 모델과의 비교: DummyRegressor를 사용하면 더 복잡한 모델의 성능을 기준(예: 평균 또는 중앙값)과 비교할 수 있습니다.
      • 사용자 친화적: DummyRegressor는 비교 분석을 쉽게 구현하고 사용할 수 있습니다.

      DummyRegressor의 한계:

      • 정확한 예측을 위한 것이 아닙니다: DummyRegressor는 기본적인 기준 예측만 제공하며 정확한 예측을 위한 것이 아닙니다.
      • 복잡한 종속성을 무시합니다: DummyRegressor는 복잡한 데이터 구조와 피처 종속성을 무시합니다.
      • 정확한 예측이 필요한 작업에는 적합하지 않습니다: 실제 예측 작업에서는 대상 변수를 예측하는 데 DummyRegressor를 사용하는 것만으로는 충분하지 않습니다.

      DummyRegressor는 다른 회귀 모델을 빠르게 평가하고 성능을 비교하기 위한 도구로 유용하지만 독립적으로 기능하는 모델은 아닙니다.


      2.3.1.1. DummyRegressor 모델 생성 코드

      # DummyRegressor.py
      # 이 코드는 DummyRegressor 모델을 학습하고 이를 ONNX 형식(float 및 double 모두)으로 내보내고 ONNX 모델을 사용하여 예측하는 과정을 보여줍니다.
      # Copyright 2023, MetaQuotes Ltd.
      # https://www.mql5.com


      # 소수점 이하 자릿수를 비교학 위한 함수
      def compare_decimal_places(value1, value2):
      # 두 값을 모두 문자열로 변환
          str_value1 = str(value1)
          str_value2 = str(value2)

      # 문자열에서 소수점 위치 찾기
          dot_position1 = str_value1.find(".")
          dot_position2 = str_value2.find(".")

      # 값 중 소수점이 없는 경우 0을 반환합니다.
          if dot_position1 == -1 or dot_position2 == -1:
              return 0

          # 소수점 이하 자릿수 계산
          decimal_places1 = len(str_value1) - dot_position1 - 1
          decimal_places2 = len(str_value2) - dot_position2 - 1

          # 소수점 이하 두 자리 수 중 최소값 찾기
          min_decimal_places = min(decimal_places1, decimal_places2)

          # 소수점 이하 자릿수가 일치하도록 카운트를 초기화
          matching_count = 0

          # 소수점 이하 문자 비교
          for i in range(1, min_decimal_places + 1):
              if str_value1[dot_position1 + i] == str_value2[dot_position2 + i]:
                  matching_count += 1
              else:
                  break

          return matching_count

      # 필요한 라이브러리 가져오기
      import numpy as np
      import matplotlib.pyplot as plt
      from sklearn.dummy import DummyRegressor
      from sklearn.metrics import r2_score,mean_absolute_error,mean_squared_error
      import onnx
      import onnxruntime as ort
      from skl2onnx import convert_sklearn
      from skl2onnx.common.data_types import FloatTensorType
      from skl2onnx.common.data_types import DoubleTensorType
      from sys import argv

      # 모델 저장 경로를 정의합니다.
      data_path = argv[0]
      last_index = data_path.rfind("\\")+ 1
      data_path = data_path[0:last_index]

      # 회귀를 위한 합성 데이터 생성
      X = np.arange(0,100,1).reshape(-1,1)
      y = 4*X + 10*np.sin(X*0.5)

      model_name = "DummyRegressor"
      onnx_model_filename = data_path + "dummy_regressor"

      # Dummy Regressor 모델 만들기
      regression_model = DummyRegressor(strategy="mean")

      # 데이터에 모델 맞추기
      regression_model.fit(X, y.ravel())

      # 전체 데이터 집합에 대한 값 예측
      y_pred = regression_model.predict(X)

      # 모델의 성능 평가
      r2 = r2_score(y, y_pred)
      MSE = MEAN_SQUARE_ERROR(Y, Y_PED)
      MAE = MEAN_ABSOLUTE_ERROR(Y, Y_PED)

      print("\n"+model_name+" Original model (double)")
      print("R-squared (Coefficient of determination):", r2)
      print("Mean Absolute Error:", mae)
      print("Mean Squared Error:", mse)

      # ONNX 모델로 변환 (float)
      #입력 데이터 유형을 FloatTensorType으로 정의합니다.

      initial_type_flo
      at = [('float_input', FloatTensorType([None, X.shape[1]]))]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_float = convert_sklearn(regression_model, initial_types=initial_type_float, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_float.onnx"
      onnx.save_model(onnx_model_float, onnx_filename)

      print("\n"+model_name+" ONNX model (float)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.
      initial_type_float = X.astype(np.float32)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_float = onnx_session.run([output_name], {input_name: initial_type_float})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_float = r2_score(y, y_pred_onnx_float)
      mse_onnx_float = mean_squared_error(y, y_pred_onnx_float)
      mae_onnx_float = mean_absolute_error(y, y_pred_onnx_float)
      print("R-squared (Coefficient of determination)", r2_onnx_float)
      print("Mean Absolute Error:", mae_onnx_float)
      print("Mean Squared Error:", mse_onnx_float)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_float))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_float))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_float))
      print("float ONNX model precision: ",compare_decimal_places(mae, mae_onnx_float))

      # 그림 크기 설정
      plt.figure(figsize=(8,5))
      # 원본 데이터와 회귀선을 그림으로 표시합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with float ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_float.png')

      # ONNX 모델로 변환 (double)
      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.

      initial_type_double = [('double_input', DoubleTensorType([None, X.shape[1]]))]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_double = convert_sklearn(regression_model, initial_types=initial_type_double, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_double.onnx"
      onnx.save_model(onnx_model_double, onnx_filename)

      print("\n"+model_name+" ONNX model (double)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.
      initial_type_double = X.astype(np.float64)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_double = onnx_session.run([output_name], {input_name: initial_type_double})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_double = r2_score(y, y_pred_onnx_double)
      mse_onnx_double = mean_squared_error(y, y_pred_onnx_double)
      mae_onnx_double = mean_absolute_error(y, y_pred_onnx_double)
      print("R-squared (Coefficient of determination)", r2_onnx_double)
      print("Mean Absolute Error:", mae_onnx_double)
      print("Mean Squared Error:", mse_onnx_double)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_double))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_double))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_double))
      print("double ONNX model precision: ",compare_decimal_places(mae, mae_onnx_double))

      # 그림 크기 설정
      plt.figure(figsize=(8,5))
      # 원본 데이터와 회귀선을 그림으로 표시합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with double ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_double.png')

      출력:

      Python  DummyRegressor Original model (double)
      Python  R-squared (Coefficient of determination): 0.0
      Python  Mean Absolute Error: 100.00329851715793
      Python  Mean Squared Error: 13232.758393867645
      

      오류 탭:

      DummyRegressor.py started       DummyRegressor.py       1       1
      Traceback (most recent call last):      DummyRegressor.py       1       1
          onnx_model_float = convert_sklearn(regression_model, initial_types=initial_type_float, target_opset=12)     DummyRegressor.py       87      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.DummyRegressor'>'. _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
      DummyRegressor.py finished in 2565 ms           19      1
      


      2.3.2. sklearn.kernel_ridge.KernelRidge

      KernelRidge는 회귀 작업에 사용되는 머신 러닝 메서드로 서포트 벡터 머신의 커널 방식(커널 SVM)과 회귀를 결합한 것입니다. KernelRidge는 커널 함수를 사용하여 피처와 대상 변수 간의 복잡한 비선형 관계를 모델링할 수 있도록 지원합니다.

      KernelRidge의 작동 원리:

      • 데이터 입력: 피처(독립 변수)와 그에 해당하는 목표 변수 값이 포함된 원본 데이터 집합으로 시작합니다.
      • 커널 함수: KernelRidge는 데이터를 고차원 공간으로 변환하는 커널 함수(다항식, 방사형 기저 함수 등)를 사용하여 보다 복잡한 비선형 관계를 모델링할 수 있습니다.
      • 모델 훈련: 모델은 예측값과 실제 목표 변수 값 간의 평균 제곱 오차를 최소화하여 데이터를 학습합니다. 커널 함수는 복잡한 종속성을 설명하는 데 사용됩니다.
      • 예측: 학습 후에는 동일한 커널 함수를 사용하여 새로운 데이터의 목표 변수 값을 예측하는 데 모델을 사용할 수 있습니다.

      KernelRidge의 장점:

      • 복잡한 비선형 관계 모델링: KernelRidge를 사용하면 피처와 대상 변수 간의 복잡하고 비선형적인 종속성을 모델링할 수 있습니다.
      • 다양한 커널 선택: 데이터와 작업의 성격에 따라 다른 커널을 선택할 수 있습니다.
      • 정규화: 이 메서드에는 정규화가 포함되어 있어 모델 과적합을 방지하는 데 도움이 됩니다.

      KernelRidge의 한계:

      • 해석 가능성 부족: 많은 비선형 메서드와 마찬가지로 KernelRidge는 선형 모델보다 해석하기 어렵습니다.
      • 계산의 복잡성: 커널 함수를 사용하면 데이터의 양이 많거나 차원이 높은 경우 계산 비용이 많이 들 수 있습니다.
      • 매개변수 조정 요구 사항: 적절한 커널 및 모델 매개변수를 선택하려면 튜닝과 전문적인 지식이 필요합니다.

      KernelRidge는 데이터에 복잡한 비선형 종속성이 나타나고 이러한 관계를 상정할 수 있는 모델이 필요한 회귀 작업에 유용합니다. 또한 커널 함수를 활용하여 데이터를 보다 유익한 표현으로 변환할 수 있는 작업에도 유용합니다.


      2.3.2.1. KernelRidge 모델을 생성하기 위한 코드

      # KernelRidge.py
      # 이 코드는 KernelRidge 모델을 학습하고 이를 ONNX 형식(float 및 double 모두)으로 내보내고 ONNX 모델을 사용하여 예측하는 과정을 보여줍니다.
      # Copyright 2023, MetaQuotes Ltd.
      # https://www.mql5.com


      # 소수점 이하 자릿수를 비교학 위한 함수
      def compare_decimal_places(value1, value2):
      # 두 값을 모두 문자열로 변환
          str_value1 = str(value1)
          str_value2 = str(value2)

      # 문자열에서 소수점 위치 찾기
          dot_position1 = str_value1.find(".")
          dot_position2 = str_value2.find(".")

      # 값 중 소수점이 없는 경우 0을 반환합니다.
          dot_position1 == -1 또는 dot_position2 ==-1:
              return 0

          # 소수점 이하 자릿수 계산
          decimal_places1 = len(str_value1) - dot_position1 - 1
          decimal_places2 = len(str_value2) - dot_position2 - 1

          # 소수점 이하 두 자리 수 중 최소값 찾기
          min_decimal_places = min(decimal_places1, decimal_places2)

          # 소수점 이하 자릿수가 일치하도록 카운트를 초기화
          matching_count = 0

          # 소수점 이하 문자 비교
          for i in range(1, min_decimal_places + 1):
              if str_value1[dot_position1 + i] == str_value2[dot_position2 + i]:
                  matching_count += 1
              else:
                  break

          return matching_count

      # 필요한 라이브러리 가져오기
      import numpy as np
      import matplotlib.pyplot as plt
      from sklearn.kernel_ridge import KernelRidge
      from sklearn.metrics import r2_score,mean_absolute_error,mean_squared_error
      import onnx
      import onnxruntime as ort
      from skl2onnx import convert_sklearn
      from skl2onnx.common.data_types import FloatTensorType
      from skl2onnx.common.data_types import DoubleTensorType
      from sys import argv

      # 모델 저장 경로를 정의합니다.
      data_path = argv[0]
      last_index = data_path.rfind("\\") + 1
      data_path = data_path[0:last_index]

      # 회귀를 위한 합성 데이터 생성
      X = np.arange(0,100,1).reshape(-1,1)
      y = 4*X + 10*np.sin(X*0.5)

      model_name = "KernelRidge"
      onnx_model_filename = data_path + "kernel_ridge"

      # KernelRidge 모델 생성
      regression_model = KernelRidge(alpha=1.0, kernel='linear')

      # 데이터에 모델 맞추기
      regression_model.fit(X, y.ravel())

      # 전체 데이터 집합에 대한 값 예측
      y_pred = regression_model.predict(X)

      # 모델의 성능 평가
      r2 = r2_score(y, y_pred)
      MSE = MEAN_SQUARE_ERROR(Y, Y_PED)
      MAE = MEAN_ABSOLUTE_ERROR(Y, Y_PED)

      print("\n"+model_name+" Original model (double)")
      print("R-squared (Coefficient of determination):", r2)
      print("Mean Absolute Error:", mae)
      print("Mean Squared Error:", mse)

      # ONNX 모델로 변환 (float)
      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.

      initial_type_float = [('float_input', FloatTensorType([None, X.shape[1]])]]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_float = convert_sklearn(regression_model, initial_types=initial_type_float, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_float.onnx"
      onnx.save_model(onnx_model_float, onnx_filename)

      print("\n"+model_name+" ONNX model (float)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.
      initial_type_float = X.astype(np.float32)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_float = onnx_session.run([output_name], {input_name: initial_type_float})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_float = r2_score(y, y_pred_onnx_float)
      mse_onnx_float = mean_squared_error(y, y_pred_onnx_float)
      mae_onnx_float = mean_absolute_error(y, y_pred_onnx_float)
      print("R-squared (Coefficient of determination)", r2_onnx_float)
      print("Mean Absolute Error:", mae_onnx_float)
      print("Mean Squared Error:", mse_onnx_float)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_float))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_float))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_float))
      print("float ONNX model precision: ",compare_decimal_places(mae, mae_onnx_float))

      # 그림 크기 설정
      plt.figure(figsize=(8, 5))
      # 원본 데이터와 회귀선을 그림으로 표시합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with float ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_float.png')

      # ONNX 모델로 변환 (double)
      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.

      initial_type_double = [('double_input', DoubleTensorType([None, X.shape[1]]))]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_double = convert_sklearn(regression_model, initial_types=initial_type_double, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_double.onnx"
      onnx.save_model(onnx_model_double, onnx_filename)

      print("\n"+model_name+" ONNX model (double)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.
      initial_type_double = X.astype(np.float64)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_double = onnx_session.run([output_name], {input_name: initial_type_double})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_double = r2_score(y, y_pred_onnx_double)
      mse_onnx_double = mean_squared_error(y, y_pred_onnx_double)
      mae_onnx_double = mean_absolute_error(y, y_pred_onnx_double)
      print("R-squared (Coefficient of determination)", r2_onnx_double)
      print("Mean Absolute Error:", mae_onnx_double)
      print("Mean Squared Error:", mse_onnx_double)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_double))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_double))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_double))
      print("double ONNX model precision: ",compare_decimal_places(mae, mae_onnx_double))

      # 그림 크기 설정
      plt.figure(figsize=(8,5))
      # 원본 데이터와 회귀선을 그림으로 표시합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with double ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_double.png')

      출력:

      Python  KernelRidge Original model (double)
      Python  R-squared (Coefficient of determination): 0.9962137909675411
      Python  Mean Absolute Error: 6.36977985227399
      Python  Mean Squared Error: 50.10198935520715
      

      오류 탭:

      KernelRidge.py started  KernelRidge.py  1       1
      Traceback (most recent call last):      KernelRidge.py  1       1
          onnx_model_float = convert_sklearn(regression_model, initial_types=initial_type_float, target_opset=12)     KernelRidge.py  87      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.kernel_ridge.KernelRidge'>'.     _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
      KernelRidge.py finished in 2516 ms              19      1
      


      2.3.3. sklearn.isotonic.IsotonicRegression

      IsotonicRegression - 피처와 대상 변수 간의 단조로운 관계를 모델링하는 회귀 작업에 사용되는 머신 러닝 메서드입니다. 여기서 '단조로움'이란 피처 중 하나의 값이 증가하면 목표 변수의 값이 증가하거나 감소하는 동시에 변화의 방향이 유지되는 것을 의미합니다.

      IsotonicRegression의 작동 원리:

      1. 데이터 입력: 피처(독립 변수)와 그에 해당하는 목표 변수 값이 포함된 원본 데이터 집합으로 시작합니다.
      2. 단조로운 회귀: IsotonicRegression는 피처와 대상 변수 간의 관계를 설명하는 최적의 단조 함수를 찾는 것을 목표로 합니다. 이 함수는 선형 또는 비선형일 수 있지만 단조로움을 유지해야 합니다.
      3. 모델 훈련: 모델은 데이터에서 학습을 통해 단조 함수의 매개변수를 결정합니다. 학습하는 동안 모델은 예측과 실제 목표 변수 값 사이의 제곱 오차의 합을 최소화하려고 합니다.
      4. 예측: 학습 후에는 단조로운 관계를 유지하면서 새로운 데이터의 목표 변수 값을 예측하는 데 모델을 사용할 수 있습니다.

      IsotonicRegression의 장점:

      • 단조로운 관계 모델링: 이 메서드는 데이터가 단조로운 종속성을 보여줄 때 이상적인 선택이며 모델에서 이러한 특성을 유지하는 것이 중요합니다.
      • 해석 가능성: 단조로운 모델은 대상 변수에 대한 각 피처의 영향의 방향을 명확하게 정의할 수 있으므로 해석이 더 쉬워집니다.

      IsotonicRegression의 한계:

      • 복잡하고 비선형적인 관계에는 적합하지 않습니다: 이 메서드는 단조로운 관계를 모델링하는 데 제한되어 있으므로 복잡한 비선형 종속성을 모델링하는 데는 적합하지 않습니다.
      • 매개변수 조정: 일부 IsotonicRegression의 구현에는 최적의 성능을 달성하기 위해 조정이 필요한 매개변수가 있을 수 있습니다.

      IsotonicRegression는 피처과 대상 변수 간의 관계의 단조로움이 중요한 요소로 간주되는 작업에서 이 특성을 보존하는 모델을 구축할 필요가 있는 경우에 유용합니다.


      2.3.3.1. 아이소토닉 회귀 모델 생성 코드

      # IsotonicRegression.py
      # 이 코드는 IsotonicRegression 모델을 학습하고 이를 ONNX 형식(float 및 double 모두)으로 내보내고 ONNX 모델을 사용하여 예측하는 과정을 보여 줍니다.
      # Copyright 2023, MetaQuotes Ltd.
      # https://www.mql5.com


      # 소수점 이하 자릿수를 비교학 위한 함수
      def compare_decimal_places(value1, value2):
      # 두 값을 모두 문자열로 변환
          str_value1 = str(value1)
          str_value2 = str(value2)

      # 문자열에서 소수점 위치 찾기
          dot_position1 = str_value1.find(".")
          dot_position2 = str_value2.find(".")

      # 값 중 소수점이 없는 경우 0을 반환합니다.
          if dot_position1 == -1 or dot_position2 == -1:
              return 0

          # 소수점 이하 자릿수 계산
          decimal_places1 = len(str_value1) - dot_position1 - 1
          decimal_places2 = len(str_value2) - dot_position2 - 1

          # 소수점 이하 두 자리 수 중 최소값 찾기
          min_decimal_places = min(decimal_places1, decimal_places2)

          # 소수점 이하 자릿수가 일치하도록 카운트를 초기화
          matching_count = 0

          # 소수점 이하 문자 비교
          for i in range(1, min_decimal_places + 1):
              if str_value1[dot_position1 + i] == str_value2[dot_position2 + i]:
                  matching_count += 1
              else:
                  break

          return matching_count

      # 필요한 라이브러리 가져오기
      import numpy as np
      import matplotlib.pyplot as plt
      from sklearn.isotonic import IsotonicRegression
      from sklearn.metrics import r2_score,mean_absolute_error,mean_squared_error
      import onnx
      import onnxruntime as ort
      from skl2onnx import convert_sklearn
      from skl2onnx.common.data_types import FloatTensorType
      from skl2onnx.common.data_types import DoubleTensorType
      from sys import argv

      # 모델 저장 경로를 정의합니다.
      data_path = argv[0]
      last_index = data_path.rfind("\\")+ 1
      data_path = data_path[0:last_index]

      # 회귀를 위한 합성 데이터 생성
      X = np.arange(0,100,1).reshape(-1,1)
      y = 4*X + 10*np.sin(X*0.5)

      model_name = "IsotonicRegression"
      onnx_model_filename = data_path + "isotonic_regression"

      #IsotonicRegression 모델 만들기
      regression_model = IsotonicRegression()

      # 데이터에 모델 맞추기
      regression_model.fit(X, y.ravel())

      # 전체 데이터 집합에 대한 값 예측
      y_pred = regression_model.predict(X)

      # 모델의 성능 평가
      r2 = r2_score(y, y_pred)
      MSE = MEAN_SQUARE_ERROR(Y, Y_PED)
      MAE = MEAN_ABSOLUTE_ERROR(Y, Y_PED)

      print("\n"+model_name+" Original model (double)")
      print("R-squared (Coefficient of determination):", r2)
      print("Mean Absolute Error:", mae)
      print("Mean Squared Error:", mse)

      # ONNX 모델로 변환 (float)
      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.

      initial_type_float = [('float_input', FloatTensorType([None, X.shape[1]])]]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_float = convert_sklearn(regression_model, initial_types=initial_type_float, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_float.onnx"
      onnx.save_model(onnx_model_float, onnx_filename)

      print("\n"+model_name+" ONNX model (float)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.
      initial_type_float = X.astype(np.float32)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_float = onnx_session.run([output_name], {input_name: initial_type_float})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_float = r2_score(y, y_pred_onnx_float)
      mse_onnx_float = mean_squared_error(y, y_pred_onnx_float)
      mae_onnx_float = mean_absolute_error(y, y_pred_onnx_float)
      print("R-squared (Coefficient of determination)", r2_onnx_float)
      print("Mean Absolute Error:", mae_onnx_float)
      print("Mean Squared Error:", mse_onnx_float)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_float))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_float))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_float))
      print("float ONNX model precision: ",compare_decimal_places(mae, mae_onnx_float))

      # 그림 크기 설정
      plt.figure(figsize=(8,5))
      # 원본 데이터와 회귀선을 그림으로 표시합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with float ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_float.png')

      # ONNX 모델로 변환 (double)
      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.

      initial_type_double = [('double_input', DoubleTensorType([None, X.shape[1]]))]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_double = convert_sklearn(regression_model, initial_types=initial_type_double, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_double.onnx"
      onnx.save_model(onnx_model_double, onnx_filename)

      print("\n"+model_name+" ONNX model (double)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.
      initial_type_double = X.astype(np.float64)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_double = onnx_session.run([output_name], {input_name: initial_type_double})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_double = r2_score(y, y_pred_onnx_double)
      mse_onnx_double = mean_squared_error(y, y_pred_onnx_double)
      mae_onnx_double = mean_absolute_error(y, y_pred_onnx_double)
      print("R-squared (Coefficient of determination)", r2_onnx_double)
      print("Mean Absolute Error:", mae_onnx_double)
      print("Mean Squared Error:", mse_onnx_double)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_double))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_double))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_double))
      print("double ONNX model precision: ",compare_decimal_places(mae, mae_onnx_double))

      # 그림 크기 설정
      plt.figure(figsize=(8,5))
      # 원본 데이터와 회귀선을 그림으로 표시합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with double ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_double.png')

      출력:

      Python  IsotonicRegression Original model (double)
      Python  R-squared (Coefficient of determination): 0.9999898125037958
      Python  Mean Absolute Error: 0.20093409873424467
      Python  Mean Squared Error: 0.13480867590911208
      

      오류 탭:

      IsotonicRegression.py started   IsotonicRegression.py   1       1
      Traceback (most recent call last):      IsotonicRegression.py   1       1
          onnx_model_float = convert_sklearn(regression_model, initial_types=initial_type_float, target_opset=12)     IsotonicRegression.py   87      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.isotonic.IsotonicRegression'>'.  _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
      IsotonicRegression.py finished in 2499 ms               19      1
      


      2.3.4. sklearn.cross_decomposition.PLSCanonical

      PLSCanonical (부분 최소 제곱 정식)은 캐노니컬 상관 관계 문제를 해결하는 데 사용되는 머신 러닝 메서드로 부분 최소자승법(PLS)의 확장된 메서드으로 두 변수 세트 간의 관계를 분석하고 모델링하는 데 적용됩니다.

      PLSCanonical의 작동 원리:

      1. 데이터 입력: 두 개의 데이터 세트(X와 Y)로 시작하며 각 세트는 변수(피처)의 집합을 나타냅니다. 일반적으로 X와 Y에는 상관관계가 있는 데이터가 포함되며 이들 간의 상관관계를 극대화하는 선형적인 피처 조합을 찾는 것이 과제입니다.
      2. 선형 조합 선택: PLSCanonical은 두 데이터 세트의 구성 요소 간의 상관관계를 최대화하기 위해 X와 Y 모두에서 선형 조합(구성 요소)을 찾습니다. 이러한 구성 요소를 캐노니컬 변수라고 합니다.
      3. 최대 상관관계 검색: PLSCanonical의 주요 목표는 X와 Y 간의 상관관계를 극대화하는 캐노니컬 변수를 찾아 두 데이터 세트 간에 가장 유익한 관계를 강조하는 것입니다.
      4. 모델 훈련: 캐노니컬 변수를 찾으면 이를 사용하여 X를 기반으로 Y 값을 예측하는 모델을 만들 수 있습니다.
      5. 예측 생성하기: 학습 후에는 해당 X 값을 사용하여 새로운 데이터의 Y 값을 예측하는 데 모델을 사용할 수 있습니다.

      PLSCanonical의 장점:

      • 상관관계 분석: PLSCanonical을 사용하면 두 데이터 세트 간의 상관관계를 분석하고 모델링할 수 있으므로 변수 간의 관계를 이해하는 데 유용할 수 있습니다.
      • 차원 축소: 이 메서드는 데이터 차원을 줄여 가장 중요한 구성 요소를 강조하는 데에도 사용할 수 있습니다.

      PLSCanonical의 한계:

      • 구성 요소 수 선택에 대한 민감도: 최적의 캐노니컬 변수의 수를 선택하려면 약간의 실험적인 도전이 필요할 수 있습니다.
      • 데이터 구조에 대한 종속성: PLSCanonical의 결과는 데이터 구조와 데이터 간의 상관관계에 따라 크게 달라질 수 있습니다.

      PLSCanonical은 두 변수 세트 간의 상관관계를 분석하고 모델링하는 데 사용되는 머신 러닝 메서드입니다. 이 메서드를 사용하면 데이터 간의 관계를 연구할 수 있으며 데이터 차원을 줄이고 상호 연관된 구성 요소를 기반으로 값을 예측하는 데 유용할 수 있습니다.

      2.3.4.1. PLSCanonical 생성 코드

      # PLSCanonical.py
      # 이 코드는 PLSCanonical 모델을 학습하고 이를 ONNX 형식(float 및 double 모두)으로 내보내고 ONNX 모델을 사용하여 예측하는 과정을 보여 줍니다.
      # Copyright 2023, MetaQuotes Ltd.
      # https://www.mql5.com


      # 소수점 이하 자릿수를 비교학 위한 함수
      def compare_decimal_places(value1, value2):
      # 두 값을 모두 문자열로 변환
          str_value1 = str(value1)
          str_value2 = str(value2)

      # 문자열에서 소수점 위치 찾기
          dot_position1 = str_value1.find(".")
          dot_position2 = str_value2.find(".")

      # 값 중 소수점이 없는 경우 0을 반환합니다.
          if dot_position1 == -1 or dot_position2 == -1:
              return 0

          # 소수점 이하 자릿수 계산
          decimal_places1 = len(str_value1) - dot_position1 - 1
          decimal_places2 = len(str_value2) - dot_position2 - 1

          # 소수점 이하 두 자리 수 중 최소값 찾기
          min_decimal_places = min(decimal_places1, decimal_places2)

          # 소수점 이하 자릿수가 일치하도록 카운트를 초기화
          matching_count = 0

          # 소수점 이하 문자 비교
          for i in range(1, min_decimal_places + 1):
              if str_value1[dot_position1 + i] == str_value2[dot_position2 + i]:
                  matching_count += 1
              else:
                  break

          return matching_count

      # 필요한 라이브러리 가져오기
      import numpy as np
      import matplotlib.pyplot as plt
      from sklearn.cross_decomposition import PLSCanonical
      from sklearn.metrics import r2_score,mean_absolute_error,mean_squared_error
      import onnx
      import onnxruntime as ort
      from skl2onnx import convert_sklearn
      from skl2onnx.common.data_types import FloatTensorType
      from skl2onnx.common.data_types import DoubleTensorType
      from sys import argv

      # 모델 저장 경로를 정의합니다.
      data_path = argv[0]
      last_index = data_path.rfind("\\")+ 1
      data_path = data_path[0:last_index]

      # 회귀를 위한 합성 데이터 생성
      X = np.arange(0,100,1).reshape(-1,1)
      y = 4*X + 10*np.sin(X*0.5)

      model_name = "PLSCanonical"
      onnx_model_filename = data_path + "pls_canonical"

      # PLSCanonical 모델 생성
      regression_model = PLSCanonical(n_components=1)

      # 데이터에 모델 맞추기
      regression_model.fit(X, y.ravel())

      # 전체 데이터 집합에 대한 값 예측
      y_pred = regression_model.predict(X)

      # 모델의 성능 평가
      r2 = r2_score(y, y_pred)
      MSE = MEAN_SQUARE_ERROR(Y, Y_PED)
      MAE = MEAN_ABSOLUTE_ERROR(Y, Y_PED)

      print("\n"+model_name+" Original model (double)")
      print("R-squared (Coefficient of determination):", r2)
      print("Mean Absolute Error:", mae)
      print("Mean Squared Error:", mse)

      # ONNX 모델로 변환 (float)
      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.

      initial_type_float = [('float_input', FloatTensorType([None, X.shape[1]]))]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_float = convert_sklearn(regression_model, initial_types=initial_type_float, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_float.onnx"
      onnx.save_model(onnx_model_float, onnx_filename)

      print("\n"+model_name+" ONNX model (float)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.
      initial_type_float = X.astype(np.float32)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_float = onnx_session.run([output_name], {input_name: initial_type_float})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_float = r2_score(y, y_pred_onnx_float)
      mse_onnx_float = mean_squared_error(y, y_pred_onnx_float)
      mae_onnx_float = mean_absolute_error(y, y_pred_onnx_float)
      print("R-squared (Coefficient of determination)", r2_onnx_float)
      print("Mean Absolute Error:", mae_onnx_float)
      print("Mean Squared Error:", mse_onnx_float)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_float))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_float))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_float))
      print("float ONNX model precision: ",compare_decimal_places(mae, mae_onnx_float))

      # 그림 크기 설정
      plt.figure(figsize=(8, 5))
      # 원본 데이터와 회귀선을 그림으로 표시합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with float ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_float.png')

      # ONNX 모델로 변환 (double)
      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.

      initial_type_double = [('double_input', DoubleTensorType([None, X.shape[1]]))]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_double = convert_sklearn(regression_model, initial_types=initial_type_double, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_double.onnx"
      onnx.save_model(onnx_model_double, onnx_filename)

      print("\n"+model_name+" ONNX model (double)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.
      initial_type_double = X.astype(np.float64)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_double = onnx_session.run([output_name], {input_name: initial_type_double})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_double = r2_score(y, y_pred_onnx_double)
      mse_onnx_double = mean_squared_error(y, y_pred_onnx_double)
      mae_onnx_double = mean_absolute_error(y, y_pred_onnx_double)
      print("R-squared (Coefficient of determination)", r2_onnx_double)
      print("Mean Absolute Error:", mae_onnx_double)
      print("Mean Squared Error:", mse_onnx_double)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_double))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_double))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_double))
      print("double ONNX model precision: ",compare_decimal_places(mae, mae_onnx_double))

      # 그림 크기 설정
      plt.figure(figsize=(8,5))
      # 원본 데이터와 회귀선을 그림으로 표시합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with double ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_double.png')

      출력:

      Python  
      Python  PLSCanonical Original model (double)
      Python  R-squared (Coefficient of determination): 0.9962347199278333
      Python  Mean Absolute Error: 6.3561407034365995
      Python  Mean Squared Error: 49.82504148022689
      

      오류 탭:

      PLSCanonical.py started PLSCanonical.py 1       1
      Traceback (most recent call last):      PLSCanonical.py 1       1
          onnx_model_float = convert_sklearn(regression_model, initial_types=initial_type_float, target_opset=12)     PLSCanonical.py 87      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.cross_decomposition._pls.PLSCanonical'>'.        _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
      PLSCanonical.py finished in 2513 ms             19      1
      


      2.3.5. sklearn.cross_decomposition.CCA

      캐노니컬 상관관계 분석 (CCA)은 두 변수 집합(집합 X와 집합 Y) 간의 관계를 연구하는 데 사용되는 다변량 통계 분석 메서드입니다. CCA의 주요 목표는 변수 X와 Y의 상관관계를 최대화하는 선형 조합을 찾는 것입니다. 이러한 선형 조합을 캐노니컬 변수라고 합니다.

      CCA의 작동 원리:

      1. 데이터 입력: CCA는 두 세트의 변수 X와 Y로 시작하며 이 세트에는 변수가 얼마든지 있을 수 있고 변수 간의 상관관계를 최대화하는 선형 조합을 찾으려고 시도합니다.
      2. 캐노니컬 변수 구성: CCA는 X와 Y의 상관관계를 극대화하는 캐노니컬 변수를 식별합니다. 이러한 캐노니컬 변수는 각 캐노니컬 지표에 대해 하나씩 원래 변수의 선형 조합입니다.
      3. 상관관계 평가: CCA는 캐노니컬 변수 쌍 간의 상관관계를 평가합니다. 캐노니컬 변수는 일반적으로 상관관계가 낮은 순서로 정렬되므로 첫 번째 쌍은 상관 관계가 가장 높고, 두 번째 쌍은 그 다음으로 높은 순서로 정렬됩니다.
      4. 해석: 정규 변수는 상관관계와 변수 가중치를 고려하여 해석할 수 있습니다. 이를 통해 집합 X와 Y에서 어떤 변수가 가장 밀접하게 연관되어 있는지 파악할 수 있습니다.

      CCA의 장점:

      • 숨겨진 연결을 표시합니다: CCA는 초기 분석 중에 명확하지 않을 수 있는 두 변수 세트 간의 숨겨진 상관관계를 발견하는 데 도움이 됩니다.
      • 소음에 강합니다: CCA는 데이터의 노이즈를 설명하고 가장 중요한 상관관계에 집중할 수 있습니다.
      • 여러 애플리케이션: CCA는 통계, 생물정보학, 금융 등 다양한 분야에서 변수 집합 간의 관계를 연구하는 데 사용할 수 있습니다.

      CCA의 한계:

      • 더 많은 데이터가 필요합니다: CCA는 상관관계를 안정적으로 추정하기 위해서는 다른 분석 메서드보다 더 많은 양의 데이터가 필요할 수 있습니다.
      • 선형 관계: CCA는 변수 간의 선형 관계를 가정하기 때문에 경우에 따라서는 불충분할 수 있습니다.
      • 해석의 복잡성: 캐노니컬 변수를 해석하는 것은 복잡할 수 있으며 특히 X와 Y 집합에 많은 변수가 있는 경우 더욱 그렇습니다.

      CCA는 두 변수 집합 간의 관계를 연구하고 숨겨진 상관관계를 밝혀내야 하는 작업에 유용합니다.


      2.3.5.1. CCA 모델 생성 코드

      # CCA.py
      # 이 코드는 CCA 모델을 학습하고 이를 ONNX 형식(float 및 double 모두)으로 내보내고 ONNX 모델을 사용하여 예측하는 과정을 보여 줍니다.
      # Copyright 2023, MetaQuotes Ltd.
      # https://www.mql5.com

      # 소수점 이하 자릿수를 비교학 위한 함수
      def compare_decimal_places(value1, value2):
      # 두 값을 모두 문자열로 변환
          str_value1 = str(value1)
          str_value2 = str(value2)

      # 문자열에서 소수점 위치 찾기
          dot_position1 = str_value1.find(".")
          dot_position2 = str_value2.find(".")

      # 값 중 소수점이 없는 경우 0을 반환합니다.
          if dot_position1 == -1 or dot_position2 == -1:
              return 0

          # 소수점 이하 자릿수 계산
          decimal_places1 = len(str_value1) - dot_position1 - 1
          decimal_places2 = len(str_value2) - dot_position2 - 1

          # 소수점 이하 두 자리 수 중 최소값 찾기
          min_decimal_places = min(decimal_places1, decimal_places2)

          # 소수점 이하 자릿수가 일치하도록 카운트를 초기화
          matching_count = 0

          # 소수점 이하 문자 비교
          for i in range(1, min_decimal_places + 1):
              if str_value1[dot_position1 + i] == str_value2[dot_position2 + i]:
                  matching_count += 1
              else:
                  break

          return matching_count

      # 필요한 라이브러리 가져오기
      import numpy as np
      import matplotlib.pyplot as plt
      from sklearn.cross_decomposition import CCA
      from sklearn.metrics import r2_score,mean_absolute_error,mean_squared_error
      import onnx
      import onnxruntime as ort
      from skl2onnx import convert_sklearn
      from skl2onnx.common.data_types import FloatTensorType
      from skl2onnx.common.data_types import DoubleTensorType
      from sys import argv

      # 모델 저장 경로를 정의합니다.
      data_path = argv[0]
      last_index = data_path.rfind("\\")+ 1
      data_path = data_path[0:last_index]

      # 회귀를 위한 합성 데이터 생성
      X = np.arange(0,100,1).reshape(-1,1)
      y = 4*X + 10*np.sin(X*0.5)

      model_name="CCA"
      onnx_model_filename = data_path + "cca"

      # CCA 모델 만들기
      regression_model = CCA(n_components=1)

      # 데이터에 모델 맞추기
      regression_model.fit(X, y.ravel())

      # 전체 데이터 집합에 대한 값 예측
      y_pred = regression_model.predict(X)

      # 모델의 성능 평가
      r2 = r2_score(y, y_pred)
      MSE = MEAN_SQUARE_ERROR(Y, Y_PED)
      MAE = MEAN_ABSOLUTE_ERROR(Y, Y_PED)

      print("\n"+model_name+" Original model (double)")
      print("R-squared (Coefficient of determination):", r2)
      print("Mean Absolute Error:", mae)
      print("Mean Squared Error:", mse)

      # ONNX 모델로 변환 (float)
      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.

      initial_type_float = [('float_input', FloatTensorType([None, X.shape[1]]))]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_float = convert_sklearn(regression_model, initial_types=initial_type_float, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_float.onnx"
      onnx.save_model(onnx_model_float, onnx_filename)

      print("\n"+model_name+" ONNX model (float)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 FloatTensorType으로 정의합니다.
      initial_type_float = X.astype(np.float32)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_float = onnx_session.run([output_name], {input_name: initial_type_float})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_float = r2_score(y, y_pred_onnx_float)
      mse_onnx_float = mean_squared_error(y, y_pred_onnx_float)
      mae_onnx_float = mean_absolute_error(y, y_pred_onnx_float)
      print("R-squared (Coefficient of determination)", r2_onnx_float)
      print("Mean Absolute Error:", mae_onnx_float)
      print("Mean Squared Error:", mse_onnx_float)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_float))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_float))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_float))
      print("float ONNX model precision: ",compare_decimal_places(mae, mae_onnx_float))

      # 그림 크기 설정
      plt.figure(figsize=(8,5))
      # 원본 데이터와 회귀선을 그림으로 표시합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with float ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_float.png')

      # ONNX 모델로 변환 (double)
      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.

      initial_type_double = [('double_input', DoubleTensorType([None, X.shape[1]]))]

      # 모델을 ONNX 형식으로 내보내기
      onnx_model_double = convert_sklearn(regression_model, initial_types=initial_type_double, target_opset=12)

      # 파일에 모델 저장
      onnx_filename=onnx_model_filename+"_double.onnx"
      onnx.save_model(onnx_model_double, onnx_filename)

      print("\n"+model_name+" ONNX model (double)")
      # 모델 경로 인쇄
      print(f"ONNX model saved to {onnx_filename}")

      # ONNX 모델을 로드하고 예측을 수행합니다.
      onnx_session = ort.InferenceSession(onnx_filename)
      input_name = onnx_session.get_inputs()[0].name
      output_name = onnx_session.get_outputs()[0].name

      # ONNX에서 입력 텐서에 대한 정보를 표시합니다.
      print("Information about input tensors in ONNX:")
      for i, input_tensor in enumerate(onnx_session.get_inputs()):
          print(f"{i + 1}. Name: {input_tensor.name}, Data Type: {input_tensor.type}, Shape: {input_tensor.shape}")

      # ONNX에서 출력 텐서에 대한 정보를 표시합니다.
      print("Information 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}")

      # 입력 데이터 유형을 DoubleTensorType으로 정의합니다.
      initial_type_double = X.astype(np.float64)

      # ONNX를 사용하여 전체 데이터 세트의 값을 예측합니다.
      y_pred_onnx_double = onnx_session.run([output_name], {input_name: initial_type_double})[0]

      # 원본 및 ONNX 모델에 대한 오류를 계산하고 표시합니다.
      r2_onnx_double = r2_score(y, y_pred_onnx_double)
      mse_onnx_double = mean_squared_error(y, y_pred_onnx_double)
      mae_onnx_double = mean_absolute_error(y, y_pred_onnx_double)
      print("R-squared (Coefficient of determination)", r2_onnx_double)
      print("Mean Absolute Error:", mae_onnx_double)
      print("Mean Squared Error:", mse_onnx_double)
      print("R^2 matching decimal places: ",compare_decimal_places(r2, r2_onnx_double))
      print("MAE matching decimal places: ",compare_decimal_places(mae, mae_onnx_double))
      print("MSE matching decimal places: ",compare_decimal_places(mse, mse_onnx_double))
      print("double ONNX model precision: ",compare_decimal_places(mae, mae_onnx_double))

      # 그림 크기 설정
      plt.figure(figsize=(8,5))
      # 원본 데이터와 회귀선을 그림으로 표시합니다.
      plt.scatter(X, y, label='Original Data', marker='o')
      plt.scatter(X, y_pred, color='blue', label='Scikit-Learn '+model_name+' Output', marker='o')
      plt.scatter(X, y_pred_onnx_float, color='red', label='ONNX '+model_name+' Output', marker='o', linestyle='--')
      plt.xlabel('X')
      plt.ylabel('y')
      plt.legend()
      plt.title(model_name+' Comparison (with double ONNX)')
      #plt.show()
      plt.savefig(data_path + model_name+'_plot_double.png')

      출력:

      Python  CCA Original model (double)
      Python  R-squared (Coefficient of determination): 0.9962347199278333
      Python  Mean Absolute Error: 6.3561407034365995
      Python  Mean Squared Error: 49.82504148022689
      

      오류 탭:

      CCA.py started  CCA.py  1       1
      Traceback (most recent call last):      CCA.py  1       1
          onnx_model_float = convert_sklearn(regression_model, initial_types=initial_type_float, target_opset=12)     CCA.py  87      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.cross_decomposition._pls.CCA'>'. _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
      CCA.py finished in 2543 ms              19      1
      


      결론

      이 문서에서 우리는 Scikit-learn 라이브러리 버전 1.3.2에서 사용할 수 있는 45개의 회귀 모델을 검토했습니다.

      1. 이 중 5개 모델은 ONNX 형식으로 변환할 때 어려움을 겪었습니다:

      1. DummyRegressor(더미 회귀분석기);
      2. KernelRidge (커널 릿지 회귀);
      3. IsotonicRegression (아이소토닉 회귀);
      4. PLSCanonical (부분 최소 제곱 캐노니컬 분석);
      5. CCA (캐노니컬 상관관계 분석).

      이러한 모델은 구조나 로직이 너무 복잡할 수 있으며 ONNX 형식과 완전히 호환되지 않는 특정 데이터 구조나 알고리즘을 사용할 수 있습니다.

      2. 나머지 40개 모델은 float 정밀도 계산을 통해 ONNX로 성공적으로 변환되었습니다.

      1. ARDRegression: 자동 관련성 결정 회귀(ARD);
      2. BayesianRidge: 정규화를 사용한 BayesianRidge 회귀;
      3. ElasticNet: 과적합을 완화하기 위한 L1 및 L2 정규화 조합;
      4. ElasticNetCV: 자동 정규화 매개변수 선택 기능이 있는 Elastic Net;
      5. HuberRegressor: 이상값에 대한 민감도가 감소된 회귀;
      6. Lars: 최소 각도 회귀;
      7. LarsCV: 교차 검증 최소 각 회귀;
      8. Lasso: 피처 선택을 위한 L1-정규화된 회귀;
      9. LassoCV: 교차 검증된 Lasso regression;
      10. LassoLars: 회귀를 위한 Lasso와 LARS의 조합;
      11. LassoLarsCV: 교차 검증된 LassoLars 회귀;
      12. LassoLarsIC: LassoLars 매개변수 선택에 대한 정보 기준;
      13. LinearRegression: 단순 선형 회귀;
      14. Ridge: L2 정규화를 사용한 선형 회귀;
      15. RidgeCV: 교차 검증된 릿지 회귀;
      16. OrthogonalMatchingPursuit: 직교 피처 선택을 사용한 회귀;
      17. PassiveAggressiveRegressor: 수동적-공격적 학습 접근 방식을 사용한 회귀;
      18. QuantileRegressor: 사분위수 회귀;
      19. RANSACRegressor: RANdom 단순 합의 메서드를 사용한 회귀;
      20. TheilSenRegressor: Theil-Sen 메서드를 기반으로 한 비선형 회귀.
      21. LinearSVR: 선형 지원 벡터 회귀;
      22. MLPRegressor: 다층 퍼셉트론을 사용한 회귀;
      23. PLSRegression: 부분 최소 제곱 회귀;
      24. TweedieRegressor: 트위디 분포 기반 회귀;
      25. PoissonRegressor: 푸아송 분산 데이터 모델링을 위한 회귀;
      26. RadiusNeighborsRegressor: 반경 이웃을 기반으로 한 회귀;
      27. KNeighborsRegressor: k-최근접 이웃을 기반으로 한 회귀;
      28. GaussianProcessRegressor: 가우스 프로세스 기반 회귀;
      29. GammaRegressor: 감마 분산 데이터 모델링을 위한 회귀;
      30. SGDRegressor: 스토캐스틱 경사 하강을 기반으로 한 회귀;
      31. AdaBoostRegressor: AdaBoost 알고리즘을 사용한 회귀;
      32. BaggingRegressor: 배깅 메서드를 사용한 회귀;
      33. DecisionTreeRegressor: 의사 결정 트리 기반 회귀;
      34. ExtraTreeRegressor: 추가 의사 결정 트리 기반의 회귀;
      35. ExtraTreesRegressor: 추가 의사 결정 트리를 사용한 회귀;
      36. NuSVR: 연속 선형 지원 벡터 회귀(SVR);
      37. RandomForestRegressor: 의사 결정 트리 앙상블을 사용한 회귀(랜덤 포레스트);
      38. GradientBoostingRegressor: 경사 부스팅을 사용한 회귀;
      39. HistGradientBoostingRegressor: 히스토그램 경사 부스팅을 사용한 회귀;
      40. SVR: 벡터 회귀 메서드를 지원합니다.

      3. 회귀 모델을 배정밀도 계산을 통해 ONNX로 변환할 수 있는 가능성

      ONNX에서 모델을 배정밀도로 변환하는 과정에서 발생하는 심각한 문제는 ML 연산자 ai.onnx.ml.LinearRegressor, ai.onnx.ml.SVMRegressor, ai.onnx.ml.TreeEnsembleRegressor의 매개변수 및 출력 값이 float 유형이라는 한계입니다. 기본적으로 이러한 요소는 정밀도를 감소 시키는 구성 요소이며 배정밀도 계산에서 실행이 되는지 의심스러운 요소입니다. 이러한 이유로 ONNX 런타임 라이브러리에서 ONNX 모델에 대한 일부 연산자를 double 정밀도로 구현하지 않았습니다( NOT_IMPLEMENTED 성격의 오류가 발생할 수 있음): 'Could not find an implementation for the node LinearRegressor:LinearRegressor(1)', 'Could not find an implementation for SVMRegressor(1) node with name 'SVM', and so on). 와 같은 오류가 발생합니다. 따라서 현재 ONNX 사양에서는 이러한 ML 연산자에 대한 완전한 배정밀도 연산이 불가능합니다.

      선형 회귀 모델의 경우 sklearn-onnx 변환기를 사용하면 LinearRegressor 제한을 우회할 수 있습니다: MatMul()Add() ONNX 연산자가 대신 사용됩니다. 이 접근 방식 덕분에 이전 목록의 처음 30개 모델은 배정밀도 계산을 통해 ONNX 모델로 성공적으로 변환되었으며 이러한 모델은 원래 모델의 배정밀도의 정확도를 그대로 유지했습니다.

      그러나 SVMRegressorTreeEnsembleRegressor와 같은 더 복잡한 ML 연산자의 경우 이를 달성하지 못했습니다. 따라서 AdaBoostRegressor, BaggingRegressor, DecisionTreeRegressor, ExtraTreeRegressor, ExtraTreesRegressor, NuSVR, RandomForestRegressor, GradientBoostingRegressor, HistGradientBostingRegressorSVR과 같은 모델은 현재 계산이 float로 이루어지는 ONNX 모델에서만 사용할 수 있습니다.


      요약

      이 문서에서는 Scikit-learn 라이브러리 버전 1.3.2의 45개 회귀 모델과 그 결과를 float 및 배정밀도 계산을 위한 ONNX 형식으로 변환하는 방법을 다룹니다.

      검토한 모든 모델 중 5개 모델은 ONNX 변환이 복잡한 것으로 나타났습니다. 이러한 모델에는 DummyRegressor, KernelRidge, IsotonicRegression, PLSCanonical 및 CCA입니다. 복잡한 구조나 로직은 성공적인 ONNX 변환을 위해 추가적인 조정이 필요할 수 있습니다.

      나머지 40개의 회귀 모델은 float 용 ONNX 형식으로 성공적으로 변환되었습니다. 그 중 30개 모델은 정확도를 유지하면서 배정밀도의 ONNX 형식으로 변환하는 데 성공했습니다.

      TreeEnsembleRegressor, AdaBoostRegressor, BaggingRegressor, DecisionTreeRegressor, ExtraTreeRegressor, ExtraTreesRegressor, NuSVR, RandomForestRegressor, GradientBoostingRegressor, HistGradientBoostingRegressor 그리고 SVR은 현재 float 계산이 가능한 ONNX 모델에서만 사용할 수 있습니다. 이는 이들에 대한 ML 연산자가 제한적이기 때문입니다.


      이 문서의 모든 스크립트는 public project MQL5\Shared Projects\Scikit.Regression.ONNX.에서 찾을 수 있습니다.

      MetaQuotes 소프트웨어 사를 통해 러시아어가 번역됨.
      원본 기고글: https://www.mql5.com/ru/articles/13538

      MQL5에서 라인을 다루는 방법 MQL5에서 라인을 다루는 방법
      이 글에서는 추세선, 지지선, 저항선과 같은 가장 중요한 선을 MQL5로 다루는 방법을 알아보세요.
      Scikit-Learn 라이브러리의 분류 모델 및 ONNX로 내보내기 Scikit-Learn 라이브러리의 분류 모델 및 ONNX로 내보내기
      이 글에서는 피셔의 붓꽃 데이터 세트의 분류 작업을 해결하기 위해서 Scikit-Learn 라이브러리에서 사용할 수 있는 모든 분류 모델을 적용하는 방법을 살펴봅니다. 우리는 이러한 모델을 ONNX 형식으로 변환하고 그 결과 모델을 MQL5 프로그램에서 활용하려고 합니다. 또한 전체 붓꽃 데이터 세트에서 원래 모델의 정확도를 ONNX 버전과 비교합니다.
      게이토 오실레이터로 트레이딩 시스템을 설계하는 방법 알아보기 게이토 오실레이터로 트레이딩 시스템을 설계하는 방법 알아보기
      인기 보조지표를 기반으로 트레이딩 시스템을 설계하는 방법을 알아보는 시리즈의 새로운 글, 게이터 오실레이터 보조지표와 간단한 전략을 통해 트레이딩 시스템을 만드는 방법
      MQL5의 ALGLIB 수치 해석 라이브러리 MQL5의 ALGLIB 수치 해석 라이브러리
      이 글에서는 금융 데이터 분석의 효율성을 향상시킬 수 있는 ALGLIB 3.19 수치 분석 라이브러리와 그 응용 프로그램 및 새로운 알고리즘에 대해 간략히 살펴봅니다.