optimizer.py 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620
  1. # -------------------------------------------------------------------------
  2. # Copyright (c) Microsoft Corporation. All rights reserved.
  3. # Licensed under the MIT License.
  4. # --------------------------------------------------------------------------
  5. # Convert Bert ONNX model converted from TensorFlow or exported from PyTorch to use Attention, Gelu,
  6. # SkipLayerNormalization and EmbedLayerNormalization ops to optimize
  7. # performance on NVidia GPU and CPU.
  8. #
  9. # For Bert model exported from PyTorch, OnnxRuntime has bert model optimization support internally.
  10. # You can use the option --use_onnxruntime to check optimizations from OnnxRuntime.
  11. # For Bert model file like name.onnx, optimized model for GPU or CPU from OnnxRuntime will output as
  12. # name_ort_gpu.onnx or name_ort_cpu.onnx in the same directory.
  13. #
  14. # This script is retained for experiment purpose. Useful scenarios like the following:
  15. # (1) Change model from fp32 to fp16 for mixed precision inference in GPU with Tensor Core.
  16. # (2) Change input data type from int64 to int32.
  17. # (3) Some model cannot be handled by OnnxRuntime, and you can modify this script to get optimized model.
  18. import argparse
  19. import logging
  20. import os
  21. import tempfile
  22. from pathlib import Path
  23. from fusion_options import FusionOptions
  24. from onnx import ModelProto, load_model
  25. from onnx_model import OnnxModel
  26. from onnx_model_bart import BartOnnxModel
  27. from onnx_model_bert import BertOnnxModel
  28. from onnx_model_bert_keras import BertOnnxModelKeras
  29. from onnx_model_bert_tf import BertOnnxModelTF
  30. from onnx_model_clip import ClipOnnxModel
  31. from onnx_model_conformer import ConformerOnnxModel
  32. from onnx_model_gpt2 import Gpt2OnnxModel
  33. from onnx_model_mmdit import MmditOnnxModel
  34. from onnx_model_phi import PhiOnnxModel
  35. from onnx_model_sam2 import Sam2OnnxModel
  36. from onnx_model_t5 import T5OnnxModel
  37. from onnx_model_tnlr import TnlrOnnxModel
  38. from onnx_model_unet import UnetOnnxModel
  39. from onnx_model_vae import VaeOnnxModel
  40. from onnx_utils import extract_raw_data_from_model, has_external_data
  41. import onnxruntime
  42. logger = logging.getLogger(__name__)
  43. # Map model type to tuple: optimizer class, export tools (pytorch, tf2onnx, keras2onnx), and default opt_level
  44. MODEL_TYPES = {
  45. "bart": (BartOnnxModel, "pytorch", 1),
  46. "bert": (BertOnnxModel, "pytorch", 1),
  47. "bert_tf": (BertOnnxModelTF, "tf2onnx", 0),
  48. "bert_keras": (BertOnnxModelKeras, "keras2onnx", 0),
  49. "clip": (ClipOnnxModel, "pytorch", 1), # Clip in Stable Diffusion
  50. "conformer": (ConformerOnnxModel, "pytorch", 1),
  51. "gpt2": (Gpt2OnnxModel, "pytorch", 1),
  52. "gpt2_tf": (Gpt2OnnxModel, "tf2onnx", 0), # might add a class for GPT2OnnxModel for TF later.
  53. "gpt_neox": (BertOnnxModel, "pytorch", 0), # GPT-NeoX
  54. "phi": (PhiOnnxModel, "pytorch", 0),
  55. "sam2": (Sam2OnnxModel, "pytorch", 1),
  56. "swin": (BertOnnxModel, "pytorch", 1),
  57. "tnlr": (TnlrOnnxModel, "pytorch", 1),
  58. "t5": (T5OnnxModel, "pytorch", 2),
  59. "unet": (UnetOnnxModel, "pytorch", 1), # UNet in Stable Diffusion
  60. "vae": (VaeOnnxModel, "pytorch", 1), # UAE in Stable Diffusion
  61. "vit": (BertOnnxModel, "pytorch", 1),
  62. "mmdit": (MmditOnnxModel, "pytorch", 1),
  63. }
  64. def optimize_by_onnxruntime(
  65. onnx_model: str | ModelProto | None = None,
  66. use_gpu: bool = False,
  67. optimized_model_path: str | None = None,
  68. opt_level: int | None = 99,
  69. disabled_optimizers: list[str] = [], # noqa: B006
  70. verbose: bool = False,
  71. save_as_external_data: bool = False,
  72. external_data_filename: str = "",
  73. external_data_file_threshold: int = 1024,
  74. *,
  75. provider: str | None = None,
  76. **deprecated_kwargs,
  77. ) -> str:
  78. """
  79. Use onnxruntime to optimize model.
  80. Args:
  81. onnx_model (str | ModelProto): the path of input onnx model or ModelProto.
  82. use_gpu (bool): whether the optimized model is targeted to run in GPU.
  83. optimized_model_path (str or None): the path of optimized model.
  84. opt_level (int): graph optimization level.
  85. disabled_optimizers (List[str]): a list of names of disabled optimizers
  86. save_as_external_data (bool): whether to save external data outside of ONNX model
  87. external_data_filename (str): name of external data file. If not provided, name is automatically created from ONNX model.
  88. external_data_file_threshold (int): threshold to decide whether to save tensor in ONNX model or in external data file
  89. provider (str or None): execution provider to use if use_gpu
  90. Returns:
  91. optimized_model_path (str): the path of optimized model
  92. """
  93. assert opt_level in [1, 2, 99]
  94. from torch import version as torch_version # noqa: PLC0415
  95. if onnx_model is None:
  96. onnx_model = deprecated_kwargs.pop("onnx_model_path", None)
  97. assert onnx_model is not None
  98. if (
  99. use_gpu
  100. and provider is None
  101. and set(onnxruntime.get_available_providers()).isdisjoint(
  102. ["CUDAExecutionProvider", "MIGraphXExecutionProvider"]
  103. )
  104. ):
  105. logger.error("There is no gpu for onnxruntime to do optimization.")
  106. return onnx_model
  107. model = (
  108. OnnxModel(load_model(onnx_model, load_external_data=False))
  109. if isinstance(onnx_model, str)
  110. else OnnxModel(onnx_model)
  111. )
  112. if model.use_float16() and not use_gpu:
  113. logger.warning(
  114. "This model uses float16 in the graph, use_gpu=False might cause extra Cast nodes. "
  115. "Most operators have no float16 implementation in CPU, so Cast nodes are added to compute them in float32. "
  116. "If the model is intended to use in GPU, please set use_gpu=True. "
  117. "Otherwise, consider exporting onnx in float32 and optional int8 quantization for better performance. "
  118. )
  119. sess_options = onnxruntime.SessionOptions()
  120. if opt_level == 1:
  121. sess_options.graph_optimization_level = onnxruntime.GraphOptimizationLevel.ORT_ENABLE_BASIC
  122. elif opt_level == 2:
  123. sess_options.graph_optimization_level = onnxruntime.GraphOptimizationLevel.ORT_ENABLE_EXTENDED
  124. elif opt_level == 3:
  125. sess_options.graph_optimization_level = onnxruntime.GraphOptimizationLevel.ORT_ENABLE_LAYOUT
  126. else:
  127. sess_options.graph_optimization_level = onnxruntime.GraphOptimizationLevel.ORT_ENABLE_ALL
  128. if optimized_model_path is None:
  129. if isinstance(onnx_model, str):
  130. path_prefix = str(Path(onnx_model).with_suffix("")) # remove .onnx suffix
  131. else:
  132. path_prefix = "optimized_model"
  133. optimized_model_path = "{}_o{}_{}.onnx".format(path_prefix, opt_level, "gpu" if use_gpu else "cpu")
  134. sess_options.optimized_model_filepath = optimized_model_path
  135. if save_as_external_data:
  136. if len(external_data_filename) == 0:
  137. # Set external data filename to model_name.onnx.data
  138. external_data_filename = os.path.basename(optimized_model_path) + ".data"
  139. sess_options.add_session_config_entry(
  140. "session.optimized_model_external_initializers_file_name", external_data_filename
  141. )
  142. sess_options.add_session_config_entry(
  143. "session.optimized_model_external_initializers_min_size_in_bytes", str(external_data_file_threshold)
  144. )
  145. if verbose:
  146. print("Using onnxruntime to optimize model - Debug level Set to verbose")
  147. sess_options.log_severity_level = 0
  148. kwargs = {}
  149. if disabled_optimizers:
  150. kwargs["disabled_optimizers"] = disabled_optimizers
  151. if not use_gpu:
  152. providers = ["CPUExecutionProvider"]
  153. elif provider is not None:
  154. if provider == "dml":
  155. providers = ["DmlExecutionProvider"]
  156. elif provider == "migraphx":
  157. providers = ["MIGraphXExecutionProvider"]
  158. elif provider == "cuda":
  159. providers = ["CUDAExecutionProvider"]
  160. elif provider == "tensorrt":
  161. providers = ["TensorrtExecutionProvider", "CUDAExecutionProvider"]
  162. else:
  163. providers = ["CUDAExecutionProvider"]
  164. providers.append("CPUExecutionProvider")
  165. else:
  166. providers = []
  167. if torch_version.hip:
  168. providers.append("MIGraphXExecutionProvider")
  169. else:
  170. providers.append("CUDAExecutionProvider")
  171. # For large model, extract external data from model and add to session options
  172. if isinstance(onnx_model, ModelProto):
  173. if has_external_data(onnx_model):
  174. raise ValueError(
  175. "ModelProto has external data not loaded into memory, ORT cannot create session. "
  176. "Please load external data before calling this function. "
  177. "See https://onnx.ai/onnx/repo-docs/ExternalData.html for more information."
  178. )
  179. external_names, external_values = extract_raw_data_from_model(onnx_model)
  180. sess_options.add_external_initializers(list(external_names), list(external_values))
  181. # Inference session is only used to optimize the model.
  182. onnx_model = onnx_model.SerializeToString() if isinstance(onnx_model, ModelProto) else onnx_model
  183. onnxruntime.InferenceSession(onnx_model, sess_options, providers=providers, **kwargs)
  184. assert os.path.exists(optimized_model_path) and os.path.isfile(optimized_model_path)
  185. logger.debug("Save optimized model by onnxruntime to %s", optimized_model_path)
  186. return optimized_model_path
  187. def optimize_by_fusion(
  188. model: ModelProto,
  189. model_type: str = "bert",
  190. num_heads: int = 0,
  191. hidden_size: int = 0,
  192. optimization_options: FusionOptions | None = None,
  193. ) -> OnnxModel:
  194. """Optimize Model by graph fusion logic.
  195. Note that ONNXRuntime graph optimizations (like constant folding) will not be applied. So it is better to enable
  196. constant folding during exporting ONNX model, or run optimize_by_onnxruntime on the model first like optimize_model.
  197. For BERT model, num_heads and hidden_size are optional. For other model types, you need to specify these parameters.
  198. Args:
  199. model (ModelProto): model object
  200. model_type (str, optional): model type - like bert, bert_tf, bert_keras or gpt2. Defaults to 'bert'.
  201. num_heads (int, optional): number of attention heads. Defaults to 0.
  202. 0 allows detect the parameter from graph automatically.
  203. hidden_size (int, optional): hidden size. Defaults to 0.
  204. 0 allows detect the parameter from graph automatically.
  205. optimization_options (FusionOptions, optional): optimization options that turn on/off some fusions.
  206. Defaults to None.
  207. Returns:
  208. object of an optimizer class.
  209. """
  210. if model_type not in ["bert", "t5", "swin", "unet", "vae", "clip", "sam2", "mmdit"] and (
  211. num_heads == 0 or hidden_size == 0
  212. ):
  213. logger.warning(f"Please specify parameters of num_heads and hidden_size for model_type {model_type}")
  214. if model_type not in MODEL_TYPES:
  215. logger.warning(f"Unsupported model type: {model_type} for graph fusion, directly return model.")
  216. return OnnxModel(model)
  217. (optimizer_class, producer, _) = MODEL_TYPES[model_type]
  218. if model.producer_name and producer != model.producer_name:
  219. logger.warning(
  220. f'Model producer not matched: Expected "{producer}", Got "{model.producer_name}".'
  221. "Please specify correct --model_type parameter."
  222. )
  223. if optimization_options is None:
  224. optimization_options = FusionOptions(model_type)
  225. optimizer = optimizer_class(model, num_heads, hidden_size)
  226. optimizer.optimize(optimization_options)
  227. optimizer.topological_sort()
  228. optimizer.model.producer_name = "onnxruntime.transformers"
  229. from onnxruntime import __version__ as onnxruntime_version # noqa: PLC0415
  230. optimizer.model.producer_version = onnxruntime_version
  231. return optimizer
  232. def optimize_model(
  233. input: str | ModelProto,
  234. model_type: str = "bert",
  235. num_heads: int = 0,
  236. hidden_size: int = 0,
  237. optimization_options: FusionOptions | None = None,
  238. opt_level: int | None = None,
  239. use_gpu: bool = False,
  240. only_onnxruntime: bool = False,
  241. verbose: bool = False,
  242. *,
  243. provider: str | None = None,
  244. ) -> OnnxModel:
  245. """Optimize Model by OnnxRuntime and/or python fusion logic.
  246. ONNX Runtime has graph optimizations (https://onnxruntime.ai/docs/performance/model-optimizations/graph-optimizations.html).
  247. However, the coverage is limited. We also have graph fusions that implemented in Python to improve the coverage.
  248. They can combined: ONNX Runtime will run first when opt_level > 0, then graph fusions in Python will be applied.
  249. To use ONNX Runtime only and no Python fusion logic, use only_onnxruntime flag and a positive opt_level like
  250. optimize_model(input, opt_level=1, use_gpu=False, only_onnxruntime=True)
  251. When opt_level is None, we will choose default optimization level according to model type.
  252. When opt_level is 0 and only_onnxruntime is False, only python fusion logic is used and onnxruntime is disabled.
  253. When opt_level > 1, use_gpu shall set properly
  254. since the optimized graph might contain operators for GPU or CPU only.
  255. If your model is intended for GPU inference only (especially float16 or mixed precision model), it is recommended to
  256. set use_gpu to be True, otherwise the model is not optimized for GPU inference.
  257. For BERT model, num_heads and hidden_size are optional. For other model types, you need specify these parameters.
  258. Args:
  259. input (str | ModelProto): input model path or ModelProto.
  260. model_type (str, optional): model type - like bert, bert_tf, bert_keras or gpt2. Defaults to 'bert'.
  261. num_heads (int, optional): number of attention heads. Defaults to 0.
  262. 0 allows detect the parameter from graph automatically.
  263. hidden_size (int, optional): hidden size. Defaults to 0.
  264. 0 allows detect the parameter from graph automatically.
  265. optimization_options (FusionOptions, optional): optimization options that turn on/off some fusions.
  266. Defaults to None.
  267. opt_level (int, optional): onnxruntime graph optimization level (0, 1, 2 or 99) or None. Defaults to None.
  268. When the value is None, default value (1 for bert and gpt2, 0 for other model types) will be used.
  269. When the level > 0, onnxruntime will be used to optimize model first.
  270. use_gpu (bool, optional): use gpu or not for onnxruntime. Defaults to False.
  271. only_onnxruntime (bool, optional): only use onnxruntime to optimize model, and no python fusion.
  272. Defaults to False.
  273. provider (str, optional): execution provider to use if use_gpu. Defaults to None.
  274. Returns:
  275. object of an optimizer class.
  276. """
  277. assert opt_level is None or opt_level in [0, 1, 2, 99]
  278. if model_type not in MODEL_TYPES:
  279. logger.warning(f"Unsupported model type: {model_type} for optimization, directly return model.")
  280. return OnnxModel(load_model(input)) if isinstance(input, str) else OnnxModel(input)
  281. (optimizer_class, _, default_opt_level) = MODEL_TYPES[model_type]
  282. if opt_level is None:
  283. opt_level = default_opt_level
  284. # Disable constant sharing to avoid model proto str mismatch in test. Ideally the optimizer should not
  285. # affect other fusions. We can update the expected model proto once the ConstantSharing optimizer logic becomes
  286. # stable.
  287. disabled_optimizers = ["ConstantSharing"]
  288. temp_model_path = None
  289. temp_dir = tempfile.TemporaryDirectory()
  290. optimized_model_name = "model_o{}_{}.onnx".format(opt_level, "gpu" if use_gpu else "cpu")
  291. optimized_model_path = os.path.join(temp_dir.name, optimized_model_name)
  292. # Auto detect if input model has external data
  293. has_external_data_file = False
  294. original_model = load_model(input, load_external_data=False) if isinstance(input, str) else input
  295. if has_external_data(original_model):
  296. has_external_data_file = True
  297. del original_model
  298. if opt_level > 1:
  299. # Disable some optimizers that might cause failure in symbolic shape inference or attention fusion.
  300. disabled_optimizers += (
  301. []
  302. if only_onnxruntime
  303. else [
  304. "MatMulScaleFusion",
  305. "MatMulAddFusion",
  306. "MatmulTransposeFusion",
  307. "GemmActivationFusion",
  308. "BiasSoftmaxFusion",
  309. ]
  310. )
  311. temp_model_path = optimize_by_onnxruntime(
  312. input,
  313. use_gpu=use_gpu,
  314. provider=provider,
  315. optimized_model_path=optimized_model_path,
  316. opt_level=opt_level,
  317. disabled_optimizers=disabled_optimizers,
  318. verbose=verbose,
  319. save_as_external_data=has_external_data_file,
  320. )
  321. elif opt_level == 1:
  322. # basic optimizations (like constant folding and cast elimination) are not specified to execution provider.
  323. # Note that use_gpu=False might cause extra Cast nodes for float16 model since most operators does not support float16 in CPU.
  324. # Sometime, use_gpu=True might cause extra memory copy nodes when some operators are supported only in CPU.
  325. # We might need remove GPU memory copy nodes as preprocess of optimize_by_fusion if they cause no matching in fusion.
  326. temp_model_path = optimize_by_onnxruntime(
  327. input,
  328. use_gpu=use_gpu,
  329. provider=provider,
  330. optimized_model_path=optimized_model_path,
  331. opt_level=1,
  332. disabled_optimizers=disabled_optimizers,
  333. verbose=verbose,
  334. save_as_external_data=has_external_data_file,
  335. )
  336. if only_onnxruntime and not temp_model_path:
  337. logger.warning("Please specify a positive value for opt_level when only_onnxruntime is True")
  338. if temp_model_path is not None:
  339. model = load_model(temp_model_path)
  340. elif isinstance(input, str):
  341. model = load_model(input)
  342. else:
  343. model = input
  344. if only_onnxruntime:
  345. optimizer = optimizer_class(model, num_heads, hidden_size)
  346. else:
  347. optimizer = optimize_by_fusion(model, model_type, num_heads, hidden_size, optimization_options)
  348. # remove the temporary directory
  349. temp_dir.cleanup()
  350. return optimizer
  351. def get_fusion_statistics(optimized_model_path: str) -> dict[str, int]:
  352. """
  353. Get counter of fused operators in optimized model.
  354. Args:
  355. optimized_model_path (str): the path of onnx model.
  356. Returns:
  357. A dictionary with operator type as key, and count as value
  358. """
  359. model = load_model(optimized_model_path, format=None, load_external_data=True)
  360. optimizer = BertOnnxModel(model)
  361. return optimizer.get_fused_operator_statistics()
  362. def _parse_arguments():
  363. parser = argparse.ArgumentParser(
  364. description="Graph optimization tool for ONNX Runtime."
  365. "It transforms ONNX graph to use optimized operators for Transformer models."
  366. )
  367. parser.add_argument("--input", required=True, type=str, help="input onnx model path")
  368. parser.add_argument("--output", required=True, type=str, help="optimized onnx model path")
  369. parser.add_argument(
  370. "--model_type",
  371. required=False,
  372. type=str.lower,
  373. default="bert",
  374. choices=list(MODEL_TYPES.keys()),
  375. help="Model type selected in the list: " + ", ".join(MODEL_TYPES.keys()),
  376. )
  377. parser.add_argument(
  378. "--num_heads",
  379. required=False,
  380. type=int,
  381. default=0,
  382. help="number of attention heads like 12 for bert-base and 16 for bert-large. "
  383. "Default is 0 to detect automatically for BERT."
  384. "For other model type, this parameter need specify correctly.",
  385. )
  386. parser.add_argument(
  387. "--hidden_size",
  388. required=False,
  389. type=int,
  390. default=0,
  391. help="hidden size like 768 for bert-base and 1024 for bert-large. "
  392. "Default is 0 to detect automatically for BERT. "
  393. "For other model type, this parameter need specify correctly.",
  394. )
  395. parser.add_argument(
  396. "--input_int32",
  397. required=False,
  398. action="store_true",
  399. help="Use int32 (instead of int64) inputs. "
  400. "It could avoid unnecessary data cast when EmbedLayerNormalization is fused for BERT.",
  401. )
  402. parser.set_defaults(input_int32=False)
  403. parser.add_argument(
  404. "--float16",
  405. required=False,
  406. action="store_true",
  407. help="Convert all weights and nodes in float32 to float16. "
  408. "It has potential loss in precision compared to mixed precision conversion.",
  409. )
  410. parser.set_defaults(float16=False)
  411. FusionOptions.add_arguments(parser)
  412. parser.add_argument("--verbose", required=False, action="store_true", help="show debug information.")
  413. parser.set_defaults(verbose=False)
  414. parser.add_argument(
  415. "--use_gpu",
  416. required=False,
  417. action="store_true",
  418. help="Use GPU for inference. Set this flag if your model is intended for GPU when opt_level > 1.",
  419. )
  420. parser.set_defaults(use_gpu=False)
  421. parser.add_argument(
  422. "--provider",
  423. required=False,
  424. type=str,
  425. default=None,
  426. help="Execution provider to use if use_gpu",
  427. )
  428. parser.add_argument(
  429. "--only_onnxruntime",
  430. required=False,
  431. action="store_true",
  432. help="optimized by onnxruntime only, and no graph fusion in Python",
  433. )
  434. parser.set_defaults(only_onnxruntime=False)
  435. parser.add_argument(
  436. "--opt_level",
  437. required=False,
  438. type=int,
  439. choices=[0, 1, 2, 3, 99],
  440. default=None,
  441. help="onnxruntime optimization level. 0 will disable onnxruntime graph optimization. "
  442. "The recommended value is 1. When opt_level > 1 is used, optimized model for GPU might not run in CPU. "
  443. "Level 2, Level 3 and 99 are intended for --only_onnxruntime.",
  444. )
  445. parser.add_argument(
  446. "--use_external_data_format",
  447. required=False,
  448. action="store_true",
  449. help="use external data format to store large model (>2GB)",
  450. )
  451. parser.set_defaults(use_external_data_format=False)
  452. parser.add_argument(
  453. "--disable_symbolic_shape_infer",
  454. required=False,
  455. action="store_true",
  456. help="disable symbolic shape inference",
  457. )
  458. parser.set_defaults(disable_symbolic_shape_infer=False)
  459. parser.add_argument(
  460. "--convert_to_packing_mode",
  461. required=False,
  462. action="store_true",
  463. help="convert the model to packing mode. Only available for BERT like model",
  464. )
  465. parser.set_defaults(convert_to_packing_mode=False)
  466. parser.add_argument(
  467. "--convert_attribute",
  468. required=False,
  469. action="store_true",
  470. help="convert attributes when using a rewritten ONNX model (e.g. Dynamo-exported model from ONNX Script)",
  471. )
  472. parser.set_defaults(convert_attribute=False)
  473. args = parser.parse_args()
  474. return args
  475. def _setup_logger(verbose):
  476. if verbose:
  477. logging.basicConfig(
  478. format="[%(filename)s:%(lineno)s - %(funcName)20s()] %(message)s", level=logging.DEBUG, force=True
  479. )
  480. else:
  481. logging.basicConfig(format="%(funcName)20s: %(message)s", level=logging.INFO, force=True)
  482. def main():
  483. args = _parse_arguments()
  484. _setup_logger(args.verbose)
  485. logger.debug(f"arguments:{args}")
  486. if os.path.realpath(args.input) == os.path.realpath(args.output):
  487. logger.warning("Specified the same input and output path. Note that this may overwrite the original model")
  488. optimization_options = FusionOptions.parse(args)
  489. optimizer = optimize_model(
  490. args.input,
  491. args.model_type,
  492. args.num_heads,
  493. args.hidden_size,
  494. opt_level=args.opt_level,
  495. optimization_options=optimization_options,
  496. use_gpu=args.use_gpu,
  497. provider=args.provider,
  498. only_onnxruntime=args.only_onnxruntime,
  499. )
  500. if args.float16:
  501. optimizer.convert_float_to_float16(keep_io_types=True)
  502. if args.input_int32:
  503. optimizer.change_graph_inputs_to_int32()
  504. # Print the operator statistics might help end user.
  505. optimizer.get_operator_statistics()
  506. fused_op_count = optimizer.get_fused_operator_statistics()
  507. if "bert" in args.model_type and optimizer.is_fully_optimized(fused_op_count):
  508. logger.info("The model has been fully optimized.")
  509. else:
  510. logger.info("The model has been optimized.")
  511. if args.convert_to_packing_mode:
  512. if args.model_type == "bert":
  513. optimizer.convert_to_packing_mode(not args.disable_symbolic_shape_infer)
  514. else:
  515. logger.warning("Packing mode only supports BERT like models")
  516. optimizer.save_model_to_file(args.output, args.use_external_data_format, convert_attribute=args.convert_attribute)
  517. if __name__ == "__main__":
  518. main()