PyCaretのモデルをONNXに変換すーる（回帰分析、Mac）

# はじめに
PyCaretで作成したモデルをONNXに変換しまーす

# 開発環境
- MacBookPro 2018
- Python 3.8

# 導入

1.anaconda環境作成

~~~
conda create -n py38 python=3.8
~~~

<!-- 
pip install pycaret[full]はエラー 
  Downloading Boruta-0.1.5.tar.gz (55 kB)
     ---------------------------------------- 55.8/55.8 kB ? eta 0:00:00
  Preparing metadata (setup.py) ... done
  Downloading Boruta-0.1.4.tar.gz (8.9 kB)
  Preparing metadata (setup.py) ... done
  Downloading Boruta-0.1.3.tar.gz (8.9 kB)
  Preparing metadata (setup.py) ... done
  Downloading Boruta-0.1.2.tar.gz (10 kB)
  Preparing metadata (setup.py) ... error
  error: subprocess-exited-with-error

  × python setup.py egg_info did not run successfully.
  │ exit code: 1
  ╰─> [24 lines of output]
      C:\Users\good_\anaconda3\envs\pycaret\lib\site-packages\setuptools\dist.py:771: UserWarning: Usage of dash-separated 'description-file' will not be supported in future versions. Please use the underscore name 'description_file' instead
        warnings.warn(
      C:\Users\good_\anaconda3\envs\pycaret\lib\site-packages\setuptools\installer.py:27: SetuptoolsDeprecationWarning: setuptools.installer is deprecated. Requirements should be satisfied by a PEP 517 installer.
        warnings.warn(
      Traceback (most recent call last):
        File "<string>", line 2, in <module>
        File "<pip-setuptools-caller>", line 34, in <module>
        File "C:\Users\good_\AppData\Local\Temp\pip-install-fyfleqp1\boruta_f3cc7b6a708748c1923568573170dee2\setup.py", line 3, in <module>
          setup(name='Boruta',
        File "C:\Users\good_\anaconda3\envs\pycaret\lib\site-packages\setuptools\__init__.py", line 87, in setup
          return distutils.core.setup(**attrs)
        File "C:\Users\good_\anaconda3\envs\pycaret\lib\site-packages\setuptools\_distutils\core.py", line 147, in setup
          _setup_distribution = dist = klass(attrs)
        File "C:\Users\good_\anaconda3\envs\pycaret\lib\site-packages\setuptools\dist.py", line 476, in __init__
          _Distribution.__init__(
        File "C:\Users\good_\anaconda3\envs\pycaret\lib\site-packages\setuptools\_distutils\dist.py", line 282, in __init__
          self.finalize_options()
        File "C:\Users\good_\anaconda3\envs\pycaret\lib\site-packages\setuptools\dist.py", line 900, in finalize_options
          ep(self)
        File "C:\Users\good_\anaconda3\envs\pycaret\lib\site-packages\setuptools\dist.py", line 920, in _finalize_setup_keywords
          ep.load()(self, ep.name, value)
        File "c:\users\good_\appdata\local\temp\pip-install-fyfleqp1\boruta_f3cc7b6a708748c1923568573170dee2\.eggs\setuptools_markdown-0.4.1-py3.8.egg\setuptools_markdown.py", line 43, in long_description_markdown_filename
          output = pypandoc.convert(markdown_filename, 'rst', format='md')
      AttributeError: module 'pypandoc' has no attribute 'convert'
      [end of output]

  note: This error originates from a subprocess, and is likely not a problem with pip.
error: metadata-generation-failed

× Encountered error while generating package metadata.
╰─> See above for output.

note: This is an issue with the package mentioned above, not pip.
hint: See above for details.
-->

2.ライブラリのインストール

~~~
pip install pycaret
pip install skl2onnx
pip install onnxruntime==1.10.0
pip install numpy==1.20.3
~~~

<!--
pip install onnxruntime

ERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts.
numba 0.54.1 requires numpy<1.21,>=1.17, but you have numpy 1.23.5 which is incompatible.

pip install numpy==1.20

ERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts.
pandas 1.5.2 requires numpy>=1.20.3; python_version < "3.10", but you have numpy 1.20.0 which is incompatible.
onnxruntime 1.13.1 requires numpy>=1.21.6, but you have numpy 1.20.0 which is incompatible.

(pycaret) D:\PythonProjects\PyCaret>pip check
numba 0.54.1 has requirement numpy<1.21,>=1.17, but you have numpy 1.21.6.

pip install -U numba

ERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts.
pycaret 2.3.10 requires numba<0.55, but you have numba 0.56.4 which is incompatible.

pip install numba==0.54.1

pip install onnxruntime==1.12.1

python export_onnx.py

    raise ImportError("Numba needs NumPy 1.20 or less")
ImportError: Numba needs NumPy 1.20 or less

pip install numpy==1.20

ERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts.
pandas 1.5.2 requires numpy>=1.20.3; python_version < "3.10", but you have numpy 1.20.0 which is incompatible.
onnxruntime 1.12.1 requires numpy>=1.21.0, but you have numpy 1.20.0 which is incompatible.

pip install onnxruntime==1.12.0

ERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts.
numba 0.54.1 requires numpy<1.21,>=1.17, but you have numpy 1.23.5 which is incompatible.

(from versions: 1.2.0, 1.3.0, 1.4.0, 1.5.1, 1.5.2, 1.6.0, 1.7.0, 1.8.0, 1.8.1, 1.9.0, 1.10.0, 1.11.0, 1.11.1, 1.12.0, 1.12.1, 1.13.1)

pip install onnxruntime==1.10.0
pip install numpy==1.20.3
-->

~~~
brew install libomp
~~~

3.[insurance.csv](https://github.com/pycaret/pycaret/blob/master/datasets/insurance.csv
)を回帰分析し、モデルをonnxに変換します

※簡単にage,bmi,children,chargesのみにしています
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63863/dba3f427-81a1-0220-4974-758f240f4deb.png)

~~~pycaret2onnx.py
from pycaret.regression import *
import pandas as pd
from skl2onnx import to_onnx

df = pd.read_csv("insurance.csv")
s = setup(df, target = 'charges')

best = compare_models()
# save_model(best, 'insurance') # insurance.pkl

X_sample = get_config('X_train')[:1]
print(X_sample)

onnx_model = to_onnx(best, X_sample.to_numpy(), target_opset={'':15, 'ai.onnx.ml':2})

with open("insurance.onnx", "wb") as f:
    f.write(onnx_model.SerializeToString())
~~~

<!--
onnx形式で保存できたが読み込めない

onnxruntime.capi.onnxruntime_pybind11_state.InvalidArgument: [ONNXRuntimeError] : 2 : INVALID_ARGUMENT : Failed to load model with error: D:\a\_work\1\s\onnxruntime\core/graph/model_load_utils.h:57 onnxruntime::model_load_utils::ValidateOpsetForDomain ONNX Runtime only *guarantees* support for models stamped with official released onnx opset versions. Opset 17 is under development and support for this is limited. The operator schemas and or other functionality may change before next ONNX release and in this case ONNX Runtime will not guarantee backward compatibility. Current official support for domain ai.onnx is till opset 15.
-->

4.変換したONNXを用いて、Pythonで推論します

~~~predict.py
import onnxruntime as rt
import numpy as np

session = rt.InferenceSession("insurance.onnx")

print(session.get_inputs()[0].name) # X
print(session.get_outputs()[0].name) # variable

# age, bmi, children_0, children_1, children_2, children_3, children_4, children_5
inputs = np.array([[19.0,27.9,1.0,0.0,0.0,0.0,0.0,0.0]],dtype=np.float32)
outputs = session.run(["variable"],{"X": inputs})[0]
print(outputs[0][0])
~~~

5.C#で推論します

~~~csharp
using Microsoft.ML.OnnxRuntime;
using Microsoft.ML.OnnxRuntime.Tensors;

Console.WriteLine(Directory.GetCurrentDirectory());
var session = new InferenceSession("../../../insurance.onnx");

Tensor<float> input = new DenseTensor<float>(new[] { 1, 8 });
input[0, 0] = 19.0f;
input[0, 1] = 27.9f;
input[0, 2] = 1.0f;
input[0, 3] = 0.0f;
input[0, 4] = 0.0f;
input[0, 5] = 0.0f;
input[0, 6] = 0.0f;
input[0, 7] = 0.0f;

var inputs = new List<NamedOnnxValue>() {
    NamedOnnxValue.CreateFromTensor<float>("X",input)
};

var results = session.Run(inputs);
Console.WriteLine(results.First().AsTensor<float>()[0]);
~~~

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63863/4f2379a8-4f87-2943-a55d-cc2474562a81.png)


# 参考文献

https://towardsdatascience.com/deploy-pycaret-models-on-edge-devices-with-onnx-runtime-c6d060a2e1a6

https://qiita.com/SatoshiGachiFujimoto/private/96bef334535ce960dc7a

https://qiita.com/SatoshiGachiFujimoto/private/9f10d4ab0cae1ebd2fc3