MLIRコンパイラ基盤におけるONNXモデルの表現と参照低減
GitHubでプロジェクトを見る onnx/onnx-mlir
このプロジェクトはonnxによってメンテナンスされています
GitHub Pagesでホストされています — テーマはorderedlist
Onnx-mlirには、PythonでONNXモデルをコンパイルして実行するためのランタイムユーティリティがあります。これらのユーティリティは、OnnxMlirCompiler
コンパイラインターフェース(include/OnnxMlirCompiler.h)とExecutionSession
クラス(src/Runtime/ExecutionSession.hpp)によって実装されています。両方のユーティリティには、pybindライブラリによって生成された関連するPythonバインディングがあります。
pybindを使用すると、C/C++バイナリをPythonインタープリターで直接インポートできます。onnx-mlirの場合、5つのライブラリがあり、1つはonnx-mlirモデルをコンパイルするため、2つはモデルを実行するため、残りの2つはモデルをコンパイルおよび実行するためのものです。
PyOMCompileSession
(src/Compiler/PyOMCompileSession.hpp)によって生成され、build/Debug/lib/PyCompile.cpython-<target>.so
として共有ライブラリとしてビルドされます。PyExecutionSession
(src/Runtime/PyExecutionSession.hpp)によって生成され、build/Debug/lib/PyRuntimeC.cpython-<target>.so
として共有ライブラリとしてビルドされます。PyOMCompileExecutionSessionC
(src/Runtime/PyOMCompileExecutionSession.hpp)によって生成され、build/Debug/lib/PyCompileAndRuntimeC.cpython-<target>.so
として共有ライブラリとしてビルドされます。モジュールは、PYTHONPATHにある限り、Pythonインタープリターで通常どおりインポートできます。別の方法は、作業ディレクトリにシンボリックリンクを作成することです。
cd <working directory>
ln -s <path to the shared library to copmpile onnx-mlir models>(e.g. `build/Debug/lib/PyCompile.cpython-<target>.so`) .
ln -s <path to the shared library to run onnx-mlir models>(e.g. `build/Debug/lib/PyRuntimeC.cpython-<target>.so`) .
ln -s <path to the Python library to run onnx-mlir models>(e.g. src/Runtime/python/PyRuntime.py) .
ln -s <path to the shared library to compile and run onnx-mlir models>(e.g. `build/Debug/lib/PyCompileAndRuntimeC.cpython-<target>.so`) .
ln -s <path to the Python library to compile and run onnx-mlir models>(e.g. src/Runtime/python/PyCompileAndRuntime.py) .
python3
ONNXモデルは計算グラフであり、多くの場合、グラフには計算をトリガーするための単一のエントリーポイントがあります。以下は、単一のエントリーポイントを持つモデルの推論を実行する例です。
import numpy as np
from PyRuntime import OMExecutionSession
model = 'model.so' # LeNet from ONNX Zoo compiled with onnx-mlir
# Create a session for this model.
session = OMExecutionSession(shared_lib_path=model)
# Input and output signatures of the default entry point.
print("input signature in json", session.input_signature())
print("output signature in json",session.output_signature())
# Do inference using the default entry point.
a = np.full((1, 1, 28, 28), 1, np.dtype(np.float32))
outputs = session.run(input=[a])
for output in outputs:
print(output.shape)
計算グラフに複数のエントリーポイントがある場合、ユーザーは推論を実行するために特定のエントリーポイントを設定する必要があります。以下は、複数のエントリーポイントを使用して推論を実行する例です。
import numpy as np
from PyRuntime import OMExecutionSession
model = 'multi-entry-points-model.so'
# Create a session for this model.
session = OMExecutionSession(shared_lib_path=model, use_default_entry_point=False) # False to manually set an entry point.
# Query entry points in the model.
entry_points = session.entry_points()
for entry_point in entry_points:
# Set the entry point to do inference.
session.set_entry_point(name=entry_point)
# Input and output signatures of the current entry point.
print("input signature in json", session.input_signature())
print("output signature in json",session.output_signature())
# Do inference using the current entry point.
a = np.arange(10).astype('float32')
b = np.arange(10).astype('float32')
outputs = session.run(input=[a, b])
for output in outputs:
print(output.shape)
モデルが--tag
を使用してコンパイルされた場合、--tag
の値はOMExecutionSessionに渡す必要があります。タグの使用は、同じPythonスクリプトに複数のモデルの複数のセッションがある場合に役立ちます。以下は、タグを使用して複数の推論を実行する例です。
import numpy as np
from PyRuntime import OMExecutionSession
encoder_model = 'encoder/model.so' # Assumed that the model was compiled using `--tag=encoder`
decoder_model = 'decoder/model.so' # Assumed that the model was compiled using `--tag=decoder`
# Create a session for the encoder model.
encoder_sess = OMExecutionSession(shared_lib_path=encoder_model, tag="encoder")
# Create a session for the decoder model.
decoder_sess = OMExecutionSession(shared_lib_path=decoder_model, tag="decoder")
2つのモデルが--tag
を使用してコンパイルされなかった場合、同じプロセスで使用する場合は、異なる.soファイル名でコンパイルする必要があります。実際、タグが指定されていない場合は、ファイル名をデフォルトのタグとして使用します。以下は、タグを使用せずに複数の推論を実行する例です。
import numpy as np
from PyRuntime import OMExecutionSession
encoder_model = 'my_encoder.so'
decoder_model = 'my_decoder.so'
# Create a session for the encoder model.
encoder_sess = OMExecutionSession(shared_lib_path=encoder_model) # tag will be `my_encoder` by default.
# Create a session for the decoder model.
decoder_sess = OMExecutionSession(shared_lib_path=decoder_model) # tag will be `my_decoder` by default.
run_main_graph
などのタグなしで関数を使用するには、tag = "NONE"
を設定します。
OMExecutionSession
への完全なインターフェースは、前述のソースで確認できます。ただし、コンストラクターとrunメソッドを使用するだけで、推論を実行するのに十分です。
def __init__(self, shared_lib_path: str, tag: str, use_default_entry_point: bool):
"""
Args:
shared_lib_path: relative or absolute path to your .so model.
tag: a string that was passed to `--tag` when compiling the .so model. By default, it is the output file name without its extension, namely, `filename` in `filename.so`
use_default_entry_point: use the default entry point that is `run_main_graph_{tag}` or not. Set to True by default.
"""
def run(self, input: List[ndarray]) -> List[ndarray]:
"""
Args:
input: A list of NumPy arrays, the inputs of your model.
Returns:
A list of NumPy arrays, the outputs of your model.
"""
def input_signature(self) -> str:
"""
Returns:
A string containing a JSON representation of the model's input signature.
"""
def output_signature(self) -> str:
"""
Returns:
A string containing a JSON representation of the model's output signature.
"""
def entry_points(self) -> List[str]:
"""
Returns:
A list of entry point names.
"""
def set_entry_point(self, name: str):
"""
Args:
name: an entry point name.
"""
ONNXモデルは、コマンドラインから直接コンパイルできます。結果として得られるライブラリは、前のセクションで示したように、Pythonを使用して実行できます。場合によっては、Pythonでモデルを直接コンパイルするのも便利な場合があります。このセクションでは、そうするためのPythonメソッドについて説明します。
OMCompileSessionオブジェクトは、構築中にファイル名を受け取ります。コンパイルの場合、compile()
は、環境変数から設定されたデフォルトオプションを上書きするflags
文字列を入力として受け取ります。
import numpy as np
from PyCompile import OMCompileSession
# Load onnx model and create OMCompileSession object.
file = './mnist.onnx'
compiler = OMCompileSession(file)
# Generate the library file. Success when rc == 0 while set the opt as "-O3"
rc = compiler.compile("-O3")
# Get the output file name
model = compiler.get_compiled_file_name()
if rc:
print("Failed to compile with error code", rc)
exit(1)
print("Compiled onnx file", file, "to", model, "with rc", rc)
PyCompile
モジュールは、ONNXモデルを実行可能モデルにコンパイルするためのOMCompileSession
クラスをエクスポートします。通常、コンパイラオブジェクトは、ONNXモデルのファイル名を指定することで、特定のモデルに対して作成されます。次に、すべてのコンパイラオプションをstd::string
全体として設定して、目的の実行可能ファイルを生成できます。最後に、コンパイル自体は、ユーザーがこの関数の入力としてオプション文字列を渡すcompile()
コマンドを呼び出すことによって実行されます。
compile()
コマンドは、コンパイルのステータスを反映するリターンコードを返します。ゼロ値は成功を示し、ゼロ以外の値はエラーコードを反映します。オペレーティングシステムによってライブラリのサフィックスが異なる場合があるため、出力ファイル名はget_compiled_file_name()
メソッドを使用して取得できます。
OnnxMlirCompilerへの完全なインターフェースは、前述のソースで確認できます。ただし、コンストラクターと以下のメソッドを使用するだけで、モデルをコンパイルするのに十分です。
def __init__(self, file_name: str):
"""
Constructor for an ONNX model contained in a file.
Args:
file_name: relative or absolute path to your ONNX model.
"""
def __init__(self, input_buffer: void *, buffer_size: int):
"""
Constructor for an ONNX model contained in an input buffer.
Args:
input_buffer: buffer containing the protobuf representation of the model.
buffer_size: byte size of the input buffer.
"""
def compile(self, flags: str):
"""
Method to compile a model from a file.
Args:
flags: all the options users would like to set.
Returns:
Zero on success, error code on failure.
"""
def compile_from_array(self, output_base_name: str, target: OnnxMlirTarget):
"""
Method to compile a model from an array.
Args:
output_base_name: base name (relative or absolute, without suffix)
where the compiled model should be written into.
target: target for the compiler's output. Typical values are
OnnxMlirTarget.emit_lib or emit_jni.
Returns:
Zero on success, error code on failure.
"""
def get_compiled_file_name(self):
"""
Method to provide the full (absolute or relative) output compiled file name, including
its suffix.
Returns:
String containing the fle name after successful compilation; empty string on failure.
"""
def get_error_message(self):
"""
Method to provide the compilation error message.
Returns:
String containing the error message; empty string on success.
"""
import numpy as np
from PyCompileAndRuntime import OMCompileExecutionSession
# Load onnx model and create OMCompileExecutionSession object.
inputFileName = './mnist.onnx'
# Set the full name of compiled model
sharedLibPath = './mnist.so'
# Set the compile option as "-O3"
session = OMCompileExecutionSession(inputFileName,sharedLibPath,"-O3")
# Print the models input/output signature, for display.
# Signature functions for info only, commented out if they cause problems.
session.print_input_signature()
session.print_output_signature()
# Do inference using the default entry point.
a = np.full((1, 1, 28, 28), 1, np.dtype(np.float32))
outputs = session.run(input=[a])
for output in outputs:
print(output.shape)
PyCompileAndRuntimeは、コンパイルと実行を組み合わせた新しいクラスです。そのコンストラクターは.onnx
入力ファイルを受け取り、ユーザーが指定したオプションでモデルをコンパイルしてから、入力を指定してモデルを実行します。
def __init__(self, input_model_path: str, compiled_file_path: str, flags: str, use_default_entry_point: bool):
"""
Constructor for an ONNX model contained in a file.
Args:
input_model_path: relative or absolute path to your ONNX model.
compiled_file_path: relative or absolute path to your compiled file.
flags: all the options users would like to set.
use_default_entry_point: use the default entry point that is `run_main_graph` or not. Set to True by default.
"""
def get_compiled_result(self):
"""
Method to provide the results of the compilation.
Returns:
Int containing the results. 0 represents successful compilation; others on failure.
"""
def get_compiled_file_name(self):
"""
Method to provide the full (absolute or relative) output file name, including
its suffix.
Returns:
String containing the fle name after successful compilation; empty string on failure.
"""
def get_error_message(self):
"""
Method to provide the compilation error message.
Returns:
String containing the error message; empty string on success.
"""
def entry_points(self) -> List[str]:
"""
Returns:
A list of entry point names.
"""
def set_entry_point(self, name: str):
"""
Args:
name: an entry point name.
"""
def run(self, input: List[ndarray]) -> List[ndarray]:
"""
Args:
input: A list of NumPy arrays, the inputs of your model.
Returns:
A list of NumPy arrays, the outputs of your model.
"""
def input_signature(self) -> str:
"""
Returns:
A string containing a JSON representation of the model's input signature.
"""
def output_signature(self) -> str:
"""
Returns:
A string containing a JSON representation of the model's output signature.
"""