preprocess.py 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. # -------------------------------------------------------------------------
  2. # Copyright (c) Microsoft Corporation. All rights reserved.
  3. # Licensed under the MIT License. See License.txt in the project root for
  4. # license information.
  5. # --------------------------------------------------------------------------
  6. """Provide entry point to preprocess ONNX model especially for QNN."""
  7. import argparse
  8. import pathlib
  9. import onnx
  10. from onnxruntime.quantization.execution_providers import qnn
  11. def _parse_arguments():
  12. """Parse cmdline arguments."""
  13. parser = argparse.ArgumentParser(description="Arguments for QNN model preprocess.")
  14. parser.add_argument("--input_model_path", "-i", required=True, help="Path to the input ONNX model.")
  15. parser.add_argument("--output_model_path", "-o", required=True, help="Path to the output ONNX model.")
  16. # Save preprocessed model with external data.
  17. parser.add_argument(
  18. "--save_as_external_data",
  19. action="store_true",
  20. help="Whether the output model would be saved with external data.",
  21. )
  22. parser.add_argument(
  23. "--all_tensors_to_one_file",
  24. action="store_true",
  25. help="Whether to save all external data in one file or save each tensor to a file named with the tensor name.",
  26. )
  27. parser.add_argument(
  28. "--external_data_location",
  29. help="Filename of the external file where all tensors are saved. The path is relative to the model path.",
  30. )
  31. parser.add_argument(
  32. "--external_data_size_threshold",
  33. default=1024,
  34. type=int,
  35. help="Tensors with data size larger than this threshold are converted to external data.",
  36. )
  37. parser.add_argument(
  38. "--external_data_convert_attribute",
  39. action="store_true",
  40. help="Whether to save all tensors, including attribute tensors, to external data.",
  41. )
  42. # Preprocess options.
  43. parser.add_argument(
  44. "--fuse_layernorm",
  45. action="store_true",
  46. help="Whether to fuse matched sequences into LayerNormalization nodes if possible.",
  47. )
  48. # I/O layouts.
  49. parser.add_argument(
  50. "--inputs_to_make_channel_last",
  51. nargs="+",
  52. default=None,
  53. help="List of graph input names to be transposed into channel-last.",
  54. )
  55. parser.add_argument(
  56. "--outputs_to_make_channel_last",
  57. nargs="+",
  58. default=None,
  59. help="List of graph output names to be transposed into channel-last.",
  60. )
  61. # Fix dynamic input shapes.
  62. parser.add_argument(
  63. "--dynamic_input_shapes",
  64. nargs=2,
  65. action="append",
  66. type=str,
  67. default=None,
  68. help="Model input name and desired static shape in comma seprated format, for example: 'input' 1,3,256,256",
  69. )
  70. # Exclude initializer from input
  71. parser.add_argument(
  72. "--exclude_initializer_from_input",
  73. action="store_true",
  74. help="Whether to exclude initializer from input if model.ir_version >= 4",
  75. )
  76. return parser.parse_args()
  77. def qnn_preprocess_model(
  78. model_input: str | pathlib.Path | onnx.ModelProto,
  79. model_output: str | pathlib.Path,
  80. fuse_layernorm: bool = False,
  81. save_as_external_data: bool = False,
  82. all_tensors_to_one_file: bool = False,
  83. external_data_location: str | None = None,
  84. external_data_size_threshold: int = 1024,
  85. external_data_convert_attribute: bool = False,
  86. inputs_to_make_channel_last: list[str] | None = None,
  87. outputs_to_make_channel_last: list[str] | None = None,
  88. dynamic_input_shapes: list[tuple[str, str]] | None = None,
  89. exclude_initializer_from_input: bool = False,
  90. ) -> bool:
  91. """Preprocess ONNX model for QNN.
  92. Args:
  93. model_input: A path or ONNX ModelProto specifiying the model to be preprocessed.
  94. model_output: A path specifying where the preprocessed model to be saved.
  95. fuse_layernorm: A bool specifying whether to fuse the matched sequence into a single LayerNormalization node.
  96. Defaults to False.
  97. save_as_external_data: A bool specifying whether to save model with external data. Defaults to False.
  98. all_tensors_to_one_file: A bool specifying whether to save all external data in one file or save each tensor to
  99. a file named with the tensor name. This argument is effective only when `save_as_external_data` is True.
  100. Defaults to False.
  101. external_data_location: A str specifying where to save the external data. The path is relative to the model
  102. path. This argument is effective only when `save_as_external_data` is True. Defaults to the model name.
  103. external_data_size_threshold: An int specifying the threshold of data size for tensors be saved as external
  104. data. This argument is effective only when `save_as_external_data` is True. Defaults to 1024.
  105. external_data_convert_attribute: A bool specifying whether to save all tensors including attributes as external
  106. data. This argument is effective only when `save_as_external_data` is True. Defaults to False.
  107. inputs_to_make_channel_last: A list of strs specifying graph input names to be transposed into channel-last.
  108. Defaults to None.
  109. outputs_to_make_channel_last: A list of strs specifying graph output names to be transposed into channel-last.
  110. Defaults to None.
  111. dynamic_input_shapes: A list of tuples specifying model input name to and its static shape in comma seprated
  112. format, for example: [('input', '1,3,256,256')]. Defaults to None.
  113. exclude_initializer_from_input: A bool specifying whether to exclude initializer from input. Defaults to False.
  114. Returns:
  115. A bool indicating whether the model is modified.
  116. """
  117. return qnn.qnn_preprocess_model(
  118. model_input,
  119. model_output,
  120. fuse_layernorm=fuse_layernorm,
  121. save_as_external_data=save_as_external_data,
  122. all_tensors_to_one_file=all_tensors_to_one_file,
  123. external_data_location=external_data_location,
  124. external_data_size_threshold=external_data_size_threshold,
  125. external_data_convert_attribute=external_data_convert_attribute,
  126. inputs_to_make_channel_last=inputs_to_make_channel_last,
  127. outputs_to_make_channel_last=outputs_to_make_channel_last,
  128. dynamic_input_shapes=dynamic_input_shapes,
  129. exclude_initializer_from_input=exclude_initializer_from_input,
  130. )
  131. if __name__ == "__main__":
  132. args = _parse_arguments()
  133. qnn_preprocess_model(
  134. args.input_model_path,
  135. args.output_model_path,
  136. fuse_layernorm=args.fuse_layernorm,
  137. save_as_external_data=args.save_as_external_data,
  138. all_tensors_to_one_file=args.all_tensors_to_one_file,
  139. external_data_location=args.external_data_location,
  140. external_data_size_threshold=args.external_data_size_threshold,
  141. external_data_convert_attribute=args.external_data_convert_attribute,
  142. inputs_to_make_channel_last=args.inputs_to_make_channel_last,
  143. outputs_to_make_channel_last=args.outputs_to_make_channel_last,
  144. dynamic_input_shapes=args.dynamic_input_shapes,
  145. exclude_initializer_from_input=args.exclude_initializer_from_input,
  146. )