|
23 | 23 | import json
|
24 | 24 | import importlib.util
|
25 | 25 | import shlex
|
| 26 | +import shutil |
26 | 27 |
|
27 | 28 | from onnx import numpy_helper
|
28 | 29 | from onnx.mapping import TENSOR_TYPE_TO_NP_TYPE
|
@@ -92,6 +93,11 @@ def check_non_negative(argname, value):
|
92 | 93 | action="store_true",
|
93 | 94 | help="Print out inference outputs produced by onnx-mlir",
|
94 | 95 | )
|
| 96 | +parser.add_argument( |
| 97 | + "--print-signatures", |
| 98 | + action="store_true", |
| 99 | + help="Print out the input and output signatures of the model", |
| 100 | +) |
95 | 101 | parser.add_argument(
|
96 | 102 | "--save-onnx",
|
97 | 103 | metavar="PATH",
|
@@ -134,24 +140,24 @@ def check_non_negative(argname, value):
|
134 | 140 |
|
135 | 141 | lib_group = parser.add_mutually_exclusive_group()
|
136 | 142 | lib_group.add_argument(
|
137 |
| - "--save-so", |
| 143 | + "--save-model", |
138 | 144 | metavar="PATH",
|
139 | 145 | 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", |
141 | 147 | )
|
142 | 148 | lib_group.add_argument(
|
143 |
| - "--load-so", |
| 149 | + "--load-model", |
144 | 150 | metavar="PATH",
|
145 | 151 | 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 " |
147 | 153 | "inference, and the ONNX model will not be re-compiled",
|
148 | 154 | )
|
149 | 155 |
|
150 | 156 | parser.add_argument(
|
151 | 157 | "--save-ref",
|
152 | 158 | metavar="PATH",
|
153 | 159 | 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", |
155 | 161 | )
|
156 | 162 | data_group = parser.add_mutually_exclusive_group()
|
157 | 163 | data_group.add_argument(
|
@@ -617,17 +623,21 @@ class onnxruntime.InferenceSession(path_or_bytes: str | bytes | os.PathLike, ses
|
617 | 623 | Another argument, 'options' is added for onnxmlir to specify options for RunONNXModel.py
|
618 | 624 | """
|
619 | 625 |
|
620 |
| - def __init__(self, model_name, **kwargs): |
| 626 | + def __init__(self, model_file, **kwargs): |
621 | 627 | global args
|
622 | 628 | if "options" in kwargs.keys():
|
623 | 629 | options = kwargs["options"]
|
624 | 630 | args = parser.parse_args(shlex.split(options))
|
625 | 631 |
|
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 |
629 | 635 | 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" |
631 | 641 |
|
632 | 642 | # Get shape information if given.
|
633 | 643 | # args.shape_info in the form of 'input_index:d1xd2, input_index:d1xd2'
|
@@ -662,95 +672,111 @@ def __init__(self, model_name, **kwargs):
|
662 | 672 | print("Saving modified onnx model to ", args.save_onnx, "\n")
|
663 | 673 | onnx.save(model, args.save_onnx)
|
664 | 674 |
|
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 | + ) |
682 | 692 | onnx.save(model, input_model_path)
|
683 |
| - elif args.model.endswith(".mlir") or args.model.endswith(".onnxtext"): |
684 |
| - input_model_path = args.model |
685 | 693 | 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) |
715 | 702 |
|
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]) + "," |
725 | 715 | )
|
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] |
743 | 724 |
|
744 |
| - # Use the generated shared library to create an execution session. |
745 |
| - print("Loading the compiled model ...") |
| 725 | + # Compile the model. |
746 | 726 | 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) |
751 | 741 | end = time.perf_counter()
|
752 | 742 | 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 |
754 | 780 |
|
755 | 781 | """
|
756 | 782 | From onnxruntime API:
|
@@ -788,6 +814,9 @@ def run(self, outputname, input_feed, **kwargs):
|
788 | 814 | output_signature = self.sess.output_signature()
|
789 | 815 | input_names = get_names_in_signature(input_signature)
|
790 | 816 | 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()) |
791 | 820 |
|
792 | 821 | inputs = []
|
793 | 822 | # Get input from input_feed, if input_feed is provided
|
@@ -834,6 +863,8 @@ def run(self, outputname, input_feed, **kwargs):
|
834 | 863 |
|
835 | 864 | # Running inference.
|
836 | 865 | print("Running inference ...")
|
| 866 | + # Let onnx-mlir know where to find the constants file. |
| 867 | + os.environ["OM_CONSTANT_PATH"] = self.model_dir |
837 | 868 | for i in range(args.warmup):
|
838 | 869 | start = time.perf_counter()
|
839 | 870 | outs = self.sess.run(inputs)
|
@@ -957,8 +988,8 @@ def run(self, outputname, input_feed, **kwargs):
|
957 | 988 |
|
958 | 989 | # Standalone driver
|
959 | 990 | 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.") |
962 | 993 | print(parser.format_usage())
|
963 | 994 | exit(1)
|
964 | 995 |
|
|
0 commit comments