Skip to content

Commit c80d3e0

Browse files
authored
[RunONNXModel.py] Add options to print out input/output signatures and support big models (#2982)
* Add options to print out input/output signatures and support big models Signed-off-by: Tung D. Le <tung@jp.ibm.com> --------- Signed-off-by: Tung D. Le <tung@jp.ibm.com>
1 parent ee7feba commit c80d3e0

File tree

1 file changed

+124
-93
lines changed

1 file changed

+124
-93
lines changed

utils/RunONNXModel.py

Lines changed: 124 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import json
2424
import importlib.util
2525
import shlex
26+
import shutil
2627

2728
from onnx import numpy_helper
2829
from onnx.mapping import TENSOR_TYPE_TO_NP_TYPE
@@ -92,6 +93,11 @@ def check_non_negative(argname, value):
9293
action="store_true",
9394
help="Print out inference outputs produced by onnx-mlir",
9495
)
96+
parser.add_argument(
97+
"--print-signatures",
98+
action="store_true",
99+
help="Print out the input and output signatures of the model",
100+
)
95101
parser.add_argument(
96102
"--save-onnx",
97103
metavar="PATH",
@@ -134,24 +140,24 @@ def check_non_negative(argname, value):
134140

135141
lib_group = parser.add_mutually_exclusive_group()
136142
lib_group.add_argument(
137-
"--save-so",
143+
"--save-model",
138144
metavar="PATH",
139145
type=str,
140-
help="File path to save the generated shared library of" " the model",
146+
help="Path to a folder to save the compiled model",
141147
)
142148
lib_group.add_argument(
143-
"--load-so",
149+
"--load-model",
144150
metavar="PATH",
145151
type=str,
146-
help="File path to load a generated shared library for "
152+
help="Path to a folder to load a compiled model for "
147153
"inference, and the ONNX model will not be re-compiled",
148154
)
149155

150156
parser.add_argument(
151157
"--save-ref",
152158
metavar="PATH",
153159
type=str,
154-
help="Path to a folder to save the inputs and outputs" " in protobuf",
160+
help="Path to a folder to save the inputs and outputs in protobuf",
155161
)
156162
data_group = parser.add_mutually_exclusive_group()
157163
data_group.add_argument(
@@ -617,17 +623,21 @@ class onnxruntime.InferenceSession(path_or_bytes: str | bytes | os.PathLike, ses
617623
Another argument, 'options' is added for onnxmlir to specify options for RunONNXModel.py
618624
"""
619625

620-
def __init__(self, model_name, **kwargs):
626+
def __init__(self, model_file, **kwargs):
621627
global args
622628
if "options" in kwargs.keys():
623629
options = kwargs["options"]
624630
args = parser.parse_args(shlex.split(options))
625631

626-
if model_name:
627-
if model_name.endswith(".onnx") or model_name.endswith(".mlir"):
628-
args.model = model_name
632+
if model_file:
633+
if model_file.endswith(".onnx") or model_file.endswith(".mlir"):
634+
args.model = model_file
629635
else:
630-
args.load_so = compiled_name
636+
args.load_model = compiled_name
637+
638+
# Default model name that will be used for the compiled model.
639+
# e.g. model.so, model.constants.bin, ...
640+
self.default_model_name = "model"
631641

632642
# Get shape information if given.
633643
# args.shape_info in the form of 'input_index:d1xd2, input_index:d1xd2'
@@ -662,95 +672,111 @@ def __init__(self, model_name, **kwargs):
662672
print("Saving modified onnx model to ", args.save_onnx, "\n")
663673
onnx.save(model, args.save_onnx)
664674

665-
# Compile, run, and verify.
666-
with tempfile.TemporaryDirectory() as temp_dir:
667-
print("Temporary directory has been created at {}".format(temp_dir))
668-
669-
shared_lib_path = ""
670-
671-
# If a shared library is given, use it without compiling the ONNX model.
672-
# Otherwise, compile the ONNX model.
673-
if args.load_so:
674-
shared_lib_path = args.load_so
675-
else:
676-
print("Compiling the model ...")
677-
# Prepare input and output paths.
678-
output_path = os.path.join(temp_dir, "model")
679-
shared_lib_path = os.path.join(temp_dir, "model.so")
680-
if args.model.endswith(".onnx"):
681-
input_model_path = os.path.join(temp_dir, "model.onnx")
675+
# If a shared library is given, use it without compiling the ONNX model.
676+
# Otherwise, compile the ONNX model.
677+
if args.load_model:
678+
self.model_dir = args.load_model
679+
else:
680+
# Compile the ONNX model.
681+
self.temp_dir = tempfile.TemporaryDirectory()
682+
print("Temporary directory has been created at {}\n".format(self.temp_dir))
683+
print("Compiling the model ...")
684+
self.model_dir = self.temp_dir.name
685+
# Prepare input and output paths.
686+
output_path = os.path.join(self.model_dir, self.default_model_name)
687+
if args.model.endswith(".onnx"):
688+
if args.verify and args.verify == "onnxruntime" and args.verify_all_ops:
689+
input_model_path = os.path.join(
690+
self.model_dir, f"{self.default_model_name}.onnx"
691+
)
682692
onnx.save(model, input_model_path)
683-
elif args.model.endswith(".mlir") or args.model.endswith(".onnxtext"):
684-
input_model_path = args.model
685693
else:
686-
print("Invalid input model path. Must end with .onnx or .mlir")
687-
exit(1)
688-
689-
# Prepare compiler arguments.
690-
command_str = [ONNX_MLIR]
691-
if args.compile_args:
692-
command_str += args.compile_args.split()
693-
if args.compile_using_input_shape:
694-
# Use shapes of the reference inputs to compile the model.
695-
assert (
696-
args.load_ref or args.load_ref_from_numpy
697-
), "No data folder given"
698-
assert "shapeInformation" not in command_str, "shape info was set"
699-
shape_info = "--shapeInformation="
700-
for i in range(len(inputs)):
701-
shape_info += (
702-
str(i)
703-
+ ":"
704-
+ "x".join([str(d) for d in inputs[i].shape])
705-
+ ","
706-
)
707-
shape_info = shape_info[:-1]
708-
command_str += [shape_info]
709-
warning(
710-
"the shapes of the model's inputs will be "
711-
"changed to the shapes of the inputs in the data folder"
712-
)
713-
command_str += [input_model_path]
714-
command_str += ["-o", output_path]
694+
input_model_path = args.model
695+
elif args.model.endswith(".mlir") or args.model.endswith(".onnxtext"):
696+
input_model_path = args.model
697+
else:
698+
print(
699+
"Invalid input model path. Must end with .onnx or .mlir or .onnxtext"
700+
)
701+
exit(1)
715702

716-
# Compile the model.
717-
start = time.perf_counter()
718-
ok, msg = execute_commands(command_str)
719-
# Dump the compilation log into a file.
720-
if args.log_to_file:
721-
log_file = (
722-
args.log_to_file
723-
if args.log_to_file.startswith("/")
724-
else os.path.join(os.getcwd(), args.log_to_file)
703+
# Prepare compiler arguments.
704+
command_str = [ONNX_MLIR]
705+
if args.compile_args:
706+
command_str += args.compile_args.split()
707+
if args.compile_using_input_shape:
708+
# Use shapes of the reference inputs to compile the model.
709+
assert args.load_ref or args.load_ref_from_numpy, "No data folder given"
710+
assert "shapeInformation" not in command_str, "shape info was set"
711+
shape_info = "--shapeInformation="
712+
for i in range(len(inputs)):
713+
shape_info += (
714+
str(i) + ":" + "x".join([str(d) for d in inputs[i].shape]) + ","
725715
)
726-
print(" Compilation log is dumped into {}".format(log_file))
727-
with open(log_file, "w") as f:
728-
f.write(msg)
729-
if not ok:
730-
print(msg)
731-
exit(1)
732-
end = time.perf_counter()
733-
print(" took ", end - start, " seconds.\n")
734-
735-
# Save the generated .so file of the model if required.
736-
if args.save_so:
737-
print("Saving the shared library to", args.save_so, "\n")
738-
execute_commands(["rsync", "-ar", shared_lib_path, args.save_so])
739-
740-
# Exit if only compiling the model.
741-
if args.compile_only:
742-
exit(0)
716+
shape_info = shape_info[:-1]
717+
command_str += [shape_info]
718+
warning(
719+
"the shapes of the model's inputs will be "
720+
"changed to the shapes of the inputs in the data folder"
721+
)
722+
command_str += [input_model_path]
723+
command_str += ["-o", output_path]
743724

744-
# Use the generated shared library to create an execution session.
745-
print("Loading the compiled model ...")
725+
# Compile the model.
746726
start = time.perf_counter()
747-
if args.load_so:
748-
sess = OMExecutionSession(shared_lib_path, tag="None")
749-
else:
750-
sess = OMExecutionSession(shared_lib_path)
727+
ok, msg = execute_commands(command_str)
728+
# Dump the compilation log into a file.
729+
if args.log_to_file:
730+
log_file = (
731+
args.log_to_file
732+
if args.log_to_file.startswith("/")
733+
else os.path.join(os.getcwd(), args.log_to_file)
734+
)
735+
print(" Compilation log is dumped into {}".format(log_file))
736+
with open(log_file, "w") as f:
737+
f.write(msg)
738+
if not ok:
739+
print(msg)
740+
exit(1)
751741
end = time.perf_counter()
752742
print(" took ", end - start, " seconds.\n")
753-
self.sess = sess
743+
744+
# Save the generated .so and .constants.bin files of the model if required.
745+
if args.save_model:
746+
if not os.path.exists(args.save_model):
747+
os.makedirs(args.save_model)
748+
if not os.path.isdir(args.save_model):
749+
print("Path to --save-model is not a folder")
750+
exit(0)
751+
shared_lib_path = self.model_dir + f"/{self.default_model_name}.so"
752+
if os.path.exists(shared_lib_path):
753+
print("Saving the shared library to", args.save_model)
754+
shutil.copy2(shared_lib_path, args.save_model)
755+
constants_file_path = os.path.join(
756+
self.model_dir, f"{self.default_model_name}.constants.bin"
757+
)
758+
if os.path.exists(constants_file_path):
759+
print("Saving the constants file to ", args.save_model, "\n")
760+
shutil.copy2(constants_file_path, args.save_model)
761+
762+
# Exit if only compiling the model.
763+
if args.compile_only:
764+
exit(0)
765+
766+
# Use the generated shared library to create an execution session.
767+
start = time.perf_counter()
768+
shared_lib_path = self.model_dir + f"/{self.default_model_name}.so"
769+
if not os.path.exists(shared_lib_path):
770+
print(f"Input model {shared_lib_path} does not exist")
771+
exit(0)
772+
print("Loading the compiled model ...")
773+
if args.load_model:
774+
sess = OMExecutionSession(shared_lib_path, tag="None")
775+
else:
776+
sess = OMExecutionSession(shared_lib_path)
777+
end = time.perf_counter()
778+
print(" took ", end - start, " seconds.\n")
779+
self.sess = sess
754780

755781
"""
756782
From onnxruntime API:
@@ -788,6 +814,9 @@ def run(self, outputname, input_feed, **kwargs):
788814
output_signature = self.sess.output_signature()
789815
input_names = get_names_in_signature(input_signature)
790816
output_names = get_names_in_signature(output_signature)
817+
if args.print_signatures:
818+
print("Model's input signature: ", input_signature.strip())
819+
print("Model's output signature: ", output_signature.strip())
791820

792821
inputs = []
793822
# Get input from input_feed, if input_feed is provided
@@ -834,6 +863,8 @@ def run(self, outputname, input_feed, **kwargs):
834863

835864
# Running inference.
836865
print("Running inference ...")
866+
# Let onnx-mlir know where to find the constants file.
867+
os.environ["OM_CONSTANT_PATH"] = self.model_dir
837868
for i in range(args.warmup):
838869
start = time.perf_counter()
839870
outs = self.sess.run(inputs)
@@ -957,8 +988,8 @@ def run(self, outputname, input_feed, **kwargs):
957988

958989
# Standalone driver
959990
def main():
960-
if not (args.model or args.load_so):
961-
print("error: no input model, use argument --model and/or --load-so.")
991+
if not (args.model or args.load_model):
992+
print("error: no input model, use argument --model and/or --load-model.")
962993
print(parser.format_usage())
963994
exit(1)
964995

0 commit comments

Comments
 (0)