polygraphy 0.50.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (197) hide show
  1. polygraphy/__init__.py +3 -0
  2. polygraphy/backend/__init__.py +0 -0
  3. polygraphy/backend/base/__init__.py +2 -0
  4. polygraphy/backend/base/loader.py +40 -0
  5. polygraphy/backend/base/runner.py +258 -0
  6. polygraphy/backend/base/util.py +53 -0
  7. polygraphy/backend/common/__init__.py +1 -0
  8. polygraphy/backend/common/loader.py +105 -0
  9. polygraphy/backend/onnx/__init__.py +1 -0
  10. polygraphy/backend/onnx/loader.py +997 -0
  11. polygraphy/backend/onnx/util.py +388 -0
  12. polygraphy/backend/onnxrt/__init__.py +2 -0
  13. polygraphy/backend/onnxrt/loader.py +91 -0
  14. polygraphy/backend/onnxrt/runner.py +90 -0
  15. polygraphy/backend/pluginref/__init__.py +17 -0
  16. polygraphy/backend/pluginref/references.py +102 -0
  17. polygraphy/backend/pluginref/runner.py +83 -0
  18. polygraphy/backend/pyt/__init__.py +1 -0
  19. polygraphy/backend/pyt/runner.py +81 -0
  20. polygraphy/backend/tensorrt_rtx/__init__.py +1 -0
  21. polygraphy/backend/tensorrt_rtx/config.py +124 -0
  22. polygraphy/backend/tf/__init__.py +39 -0
  23. polygraphy/backend/tf/loader.py +496 -0
  24. polygraphy/backend/tf/runner.py +107 -0
  25. polygraphy/backend/tf/util.py +204 -0
  26. polygraphy/backend/trt/__init__.py +9 -0
  27. polygraphy/backend/trt/algorithm_selector.py +423 -0
  28. polygraphy/backend/trt/calibrator.py +295 -0
  29. polygraphy/backend/trt/config.py +716 -0
  30. polygraphy/backend/trt/file_reader.py +95 -0
  31. polygraphy/backend/trt/loader.py +1219 -0
  32. polygraphy/backend/trt/profile.py +215 -0
  33. polygraphy/backend/trt/runner.py +761 -0
  34. polygraphy/backend/trt/util.py +951 -0
  35. polygraphy/common/__init__.py +2 -0
  36. polygraphy/common/interface.py +192 -0
  37. polygraphy/common/struct.py +203 -0
  38. polygraphy/comparator/__init__.py +5 -0
  39. polygraphy/comparator/comparator.py +483 -0
  40. polygraphy/comparator/compare.py +2016 -0
  41. polygraphy/comparator/data_loader.py +531 -0
  42. polygraphy/comparator/postprocess.py +64 -0
  43. polygraphy/comparator/struct.py +409 -0
  44. polygraphy/comparator/util.py +405 -0
  45. polygraphy/config.py +65 -0
  46. polygraphy/constants.py +32 -0
  47. polygraphy/cuda/__init__.py +1 -0
  48. polygraphy/cuda/cuda.py +521 -0
  49. polygraphy/datatype/__init__.py +6 -0
  50. polygraphy/datatype/datatype.py +274 -0
  51. polygraphy/datatype/numpy.py +77 -0
  52. polygraphy/datatype/onnx.py +86 -0
  53. polygraphy/datatype/onnxrt.py +72 -0
  54. polygraphy/datatype/tensorrt.py +75 -0
  55. polygraphy/datatype/torch.py +68 -0
  56. polygraphy/exception/__init__.py +1 -0
  57. polygraphy/exception/exception.py +52 -0
  58. polygraphy/func/__init__.py +1 -0
  59. polygraphy/func/func.py +199 -0
  60. polygraphy/json/__init__.py +1 -0
  61. polygraphy/json/serde.py +442 -0
  62. polygraphy/logger/__init__.py +1 -0
  63. polygraphy/logger/logger.py +686 -0
  64. polygraphy/mod/__init__.py +3 -0
  65. polygraphy/mod/exporter.py +362 -0
  66. polygraphy/mod/importer.py +345 -0
  67. polygraphy/mod/trt_importer.py +41 -0
  68. polygraphy/mod/util.py +41 -0
  69. polygraphy/tools/__init__.py +3 -0
  70. polygraphy/tools/_main.py +88 -0
  71. polygraphy/tools/args/__init__.py +19 -0
  72. polygraphy/tools/args/backend/__init__.py +21 -0
  73. polygraphy/tools/args/backend/onnx/__init__.py +16 -0
  74. polygraphy/tools/args/backend/onnx/loader.py +770 -0
  75. polygraphy/tools/args/backend/onnxrt/__init__.py +17 -0
  76. polygraphy/tools/args/backend/onnxrt/loader.py +85 -0
  77. polygraphy/tools/args/backend/onnxrt/runner.py +42 -0
  78. polygraphy/tools/args/backend/pluginref/__init__.py +16 -0
  79. polygraphy/tools/args/backend/pluginref/runner.py +47 -0
  80. polygraphy/tools/args/backend/runner_select.py +107 -0
  81. polygraphy/tools/args/backend/tf/__init__.py +18 -0
  82. polygraphy/tools/args/backend/tf/config.py +77 -0
  83. polygraphy/tools/args/backend/tf/loader.py +266 -0
  84. polygraphy/tools/args/backend/tf/runner.py +70 -0
  85. polygraphy/tools/args/backend/trt/__init__.py +18 -0
  86. polygraphy/tools/args/backend/trt/config.py +993 -0
  87. polygraphy/tools/args/backend/trt/helper.py +34 -0
  88. polygraphy/tools/args/backend/trt/loader.py +894 -0
  89. polygraphy/tools/args/backend/trt/runner.py +107 -0
  90. polygraphy/tools/args/base.py +212 -0
  91. polygraphy/tools/args/comparator/__init__.py +19 -0
  92. polygraphy/tools/args/comparator/comparator.py +340 -0
  93. polygraphy/tools/args/comparator/compare.py +407 -0
  94. polygraphy/tools/args/comparator/data_loader.py +269 -0
  95. polygraphy/tools/args/comparator/postprocess.py +143 -0
  96. polygraphy/tools/args/logger/__init__.py +16 -0
  97. polygraphy/tools/args/logger/logger.py +172 -0
  98. polygraphy/tools/args/model.py +227 -0
  99. polygraphy/tools/args/util/__init__.py +1 -0
  100. polygraphy/tools/args/util/util.py +428 -0
  101. polygraphy/tools/base/__init__.py +1 -0
  102. polygraphy/tools/base/tool.py +212 -0
  103. polygraphy/tools/check/__init__.py +1 -0
  104. polygraphy/tools/check/check.py +32 -0
  105. polygraphy/tools/check/subtool/__init__.py +1 -0
  106. polygraphy/tools/check/subtool/lint.py +1000 -0
  107. polygraphy/tools/convert/__init__.py +1 -0
  108. polygraphy/tools/convert/convert.py +115 -0
  109. polygraphy/tools/data/__init__.py +1 -0
  110. polygraphy/tools/data/data.py +33 -0
  111. polygraphy/tools/data/subtool/__init__.py +2 -0
  112. polygraphy/tools/data/subtool/concat.py +115 -0
  113. polygraphy/tools/data/subtool/merge.py +89 -0
  114. polygraphy/tools/debug/__init__.py +1 -0
  115. polygraphy/tools/debug/debug.py +72 -0
  116. polygraphy/tools/debug/subtool/__init__.py +4 -0
  117. polygraphy/tools/debug/subtool/base.py +159 -0
  118. polygraphy/tools/debug/subtool/build.py +46 -0
  119. polygraphy/tools/debug/subtool/iterative_debug_args.py +761 -0
  120. polygraphy/tools/debug/subtool/precision.py +281 -0
  121. polygraphy/tools/debug/subtool/reduce.py +594 -0
  122. polygraphy/tools/debug/subtool/repeat.py +46 -0
  123. polygraphy/tools/inspect/__init__.py +1 -0
  124. polygraphy/tools/inspect/inspect.py +44 -0
  125. polygraphy/tools/inspect/subtool/__init__.py +6 -0
  126. polygraphy/tools/inspect/subtool/capability.py +429 -0
  127. polygraphy/tools/inspect/subtool/data.py +146 -0
  128. polygraphy/tools/inspect/subtool/diff_tactics.py +128 -0
  129. polygraphy/tools/inspect/subtool/model/__init__.py +1 -0
  130. polygraphy/tools/inspect/subtool/model/args.py +62 -0
  131. polygraphy/tools/inspect/subtool/model/extractors/__init__.py +27 -0
  132. polygraphy/tools/inspect/subtool/model/extractors/_helpers.py +60 -0
  133. polygraphy/tools/inspect/subtool/model/extractors/_onnx.py +237 -0
  134. polygraphy/tools/inspect/subtool/model/extractors/_trt.py +332 -0
  135. polygraphy/tools/inspect/subtool/model/graph_data.py +108 -0
  136. polygraphy/tools/inspect/subtool/model/model.py +217 -0
  137. polygraphy/tools/inspect/subtool/model/text.py +298 -0
  138. polygraphy/tools/inspect/subtool/model/viewer/__init__.py +3 -0
  139. polygraphy/tools/inspect/subtool/model/viewer/_templates.py +1435 -0
  140. polygraphy/tools/inspect/subtool/model/viewer/_viewer.py +192 -0
  141. polygraphy/tools/inspect/subtool/model/viewer/colors.py +284 -0
  142. polygraphy/tools/inspect/subtool/model/viewer/elements.py +276 -0
  143. polygraphy/tools/inspect/subtool/model/viewer/layout.py +255 -0
  144. polygraphy/tools/inspect/subtool/model/viewer/node_info.py +228 -0
  145. polygraphy/tools/inspect/subtool/model/viewer/page.py +287 -0
  146. polygraphy/tools/inspect/subtool/sparsity.py +50 -0
  147. polygraphy/tools/inspect/subtool/tactics.py +38 -0
  148. polygraphy/tools/multi_device/__init__.py +9 -0
  149. polygraphy/tools/multi_device/multi_device.py +314 -0
  150. polygraphy/tools/multi_device/subtool/__init__.py +1 -0
  151. polygraphy/tools/multi_device/subtool/shard.py +960 -0
  152. polygraphy/tools/plugin/__init__.py +1 -0
  153. polygraphy/tools/plugin/plugin.py +35 -0
  154. polygraphy/tools/plugin/subtool/__init__.py +5 -0
  155. polygraphy/tools/plugin/subtool/autotune.py +261 -0
  156. polygraphy/tools/plugin/subtool/autotuner/__init__.py +6 -0
  157. polygraphy/tools/plugin/subtool/autotuner/auto_tuner.py +250 -0
  158. polygraphy/tools/plugin/subtool/autotuner/optimization_strategy.py +350 -0
  159. polygraphy/tools/plugin/subtool/autotuner/performance_analyzer.py +280 -0
  160. polygraphy/tools/plugin/subtool/autotuner/replacement_engine.py +181 -0
  161. polygraphy/tools/plugin/subtool/list_plugins.py +34 -0
  162. polygraphy/tools/plugin/subtool/match.py +40 -0
  163. polygraphy/tools/plugin/subtool/plugin_base.py +172 -0
  164. polygraphy/tools/plugin/subtool/replace.py +179 -0
  165. polygraphy/tools/registry.py +68 -0
  166. polygraphy/tools/run/__init__.py +1 -0
  167. polygraphy/tools/run/run.py +241 -0
  168. polygraphy/tools/script.py +490 -0
  169. polygraphy/tools/sparse.py +388 -0
  170. polygraphy/tools/surgeon/__init__.py +1 -0
  171. polygraphy/tools/surgeon/subtool/__init__.py +6 -0
  172. polygraphy/tools/surgeon/subtool/base.py +83 -0
  173. polygraphy/tools/surgeon/subtool/extract.py +215 -0
  174. polygraphy/tools/surgeon/subtool/insert.py +170 -0
  175. polygraphy/tools/surgeon/subtool/prune.py +58 -0
  176. polygraphy/tools/surgeon/subtool/sanitize.py +259 -0
  177. polygraphy/tools/surgeon/subtool/weight_reconstruct.py +111 -0
  178. polygraphy/tools/surgeon/subtool/weight_strip.py +467 -0
  179. polygraphy/tools/surgeon/surgeon.py +46 -0
  180. polygraphy/tools/template/__init__.py +1 -0
  181. polygraphy/tools/template/subtool/__init__.py +4 -0
  182. polygraphy/tools/template/subtool/base.py +30 -0
  183. polygraphy/tools/template/subtool/onnx_gs.py +89 -0
  184. polygraphy/tools/template/subtool/shard_hint.py +419 -0
  185. polygraphy/tools/template/subtool/trt_config.py +62 -0
  186. polygraphy/tools/template/subtool/trt_network.py +76 -0
  187. polygraphy/tools/template/template.py +30 -0
  188. polygraphy/tools/util.py +50 -0
  189. polygraphy/util/__init__.py +2 -0
  190. polygraphy/util/array.py +1522 -0
  191. polygraphy/util/util.py +1138 -0
  192. polygraphy-0.50.3.dist-info/METADATA +180 -0
  193. polygraphy-0.50.3.dist-info/RECORD +197 -0
  194. polygraphy-0.50.3.dist-info/WHEEL +5 -0
  195. polygraphy-0.50.3.dist-info/entry_points.txt +2 -0
  196. polygraphy-0.50.3.dist-info/top_level.txt +1 -0
  197. polygraphy-0.50.3.dist-info/zip-safe +1 -0
@@ -0,0 +1,997 @@
1
+ #
2
+ # SPDX-FileCopyrightText: Copyright (c) 1993-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3
+ # SPDX-License-Identifier: Apache-2.0
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+ import copy
18
+ import os
19
+ import sys
20
+ import tempfile
21
+
22
+ from polygraphy import constants, mod, util
23
+ from polygraphy.backend.base import BaseLoader
24
+ from polygraphy.backend.onnx import util as onnx_util
25
+ from polygraphy.datatype import DataType
26
+ from polygraphy.logger import G_LOGGER, LogMode
27
+
28
+ np = mod.lazy_import("numpy")
29
+ onnx = mod.lazy_import("onnx>=1.8.1")
30
+ onnxrt = mod.lazy_import("onnxruntime>=1.10.0")
31
+ onnxconverter_common = mod.lazy_import("onnxconverter_common>=1.12.2")
32
+ tf = mod.lazy_import("tensorflow<2.0")
33
+ tf2onnx = mod.lazy_import("tf2onnx")
34
+ tf_util = mod.lazy_import("polygraphy.backend.tf.util", log=False)
35
+ gs = mod.lazy_import("onnx_graphsurgeon>=0.3.27")
36
+ # ONNX-RT's shape inference also requires "sympy", but it is not reported as a dependency,
37
+ # so we work around it by checking for it manually.
38
+ onnxrt_symbolic_shape_inference = mod.lazy_import(
39
+ "onnxruntime.tools.symbolic_shape_infer>=1.10.0", requires=["sympy"]
40
+ )
41
+
42
+ LARGE_MODEL_THRESHOLD = 512 << 20 # 512 MiB
43
+ PROTOBUF_THRESHOLD = 2e9
44
+
45
+
46
+ class BaseLoadOnnxCopy(BaseLoader):
47
+ """
48
+ Abstract base class for loaders that require loading an ONNX model and potentially
49
+ making a copy.
50
+ """
51
+
52
+ def __init__(self, model, copy=None):
53
+ """
54
+ Args:
55
+ model (Union[onnx.ModelProto, Callable() -> onnx.ModelProto]):
56
+ An ONNX model or a callable that returns one.
57
+
58
+ copy (bool): Whether to create a copy of the model first. Defaults to False.
59
+ """
60
+ self._model = model
61
+ self.copy = util.default(copy, False)
62
+
63
+ def load(self):
64
+ model, _ = util.invoke_if_callable(self._model)
65
+ if self.copy:
66
+ model = copy.copy(model)
67
+ return model
68
+
69
+
70
+ class _GSGraphManager:
71
+ """
72
+ Imports an ONNX-GraphSurgeon graph.
73
+
74
+ If the provided model is already a graph, the graph is not
75
+ exported to ONNX.
76
+ """
77
+
78
+ def __init__(self, model):
79
+ self._model = model
80
+
81
+ def __enter__(self):
82
+ model, _ = util.invoke_if_callable(self._model)
83
+ self.USE_GS_GRAPH = isinstance(model, gs.Graph)
84
+ if self.USE_GS_GRAPH:
85
+ self.graph = model.copy()
86
+ else:
87
+ self.graph = gs_from_onnx(model)
88
+ return self
89
+
90
+ def __exit__(self, exc_type, exc_value, traceback):
91
+ if self.USE_GS_GRAPH:
92
+ self.retval = self.graph
93
+ else:
94
+ self.retval = gs.export_onnx(self.graph, do_type_check=False)
95
+
96
+
97
+ @mod.export(funcify=True)
98
+ class GsFromOnnx(BaseLoader):
99
+ """
100
+ Functor that creates an ONNX-GraphSurgeon graph from an ONNX ModelProto.
101
+ """
102
+
103
+ def __init__(self, model):
104
+ """
105
+ Creates an ONNX-GraphSurgeon graph from an ONNX ModelProto.
106
+
107
+ Args:
108
+ model (Union[onnx.ModelProto, Callable() -> onnx.ModelProto]):
109
+ An ONNX model or a callable that returns one.
110
+ """
111
+ self._model = model
112
+
113
+ @util.check_called_by("__call__")
114
+ def call_impl(self):
115
+ """
116
+ Returns:
117
+ onnx_graphsurgeon.Graph: The ONNX-GraphSurgeon representation of the ONNX model
118
+ """
119
+ model, _ = util.invoke_if_callable(self._model)
120
+ return gs.import_onnx(model)
121
+
122
+
123
+ @mod.export(funcify=True)
124
+ class OnnxFromPath(BaseLoader):
125
+ """
126
+ Functor that loads an ONNX model from a file.
127
+ """
128
+
129
+ def __init__(self, path, external_data_dir=None, ignore_external_data=None):
130
+ """
131
+ Loads an ONNX model from a file.
132
+
133
+ Args:
134
+ path (str): The path from which to load the model.
135
+
136
+ external_data_dir (str): The directory where external data for the model is stored.
137
+ ignore_external_data (bool):
138
+ Whether to ignore any external data and just load the model structure without any weights.
139
+ The model will be usable only for purposes that don't require weights, such as extracting
140
+ subgraphs or inspecting model structure.
141
+ This can be useful in cases where external data is not available.
142
+ Defaults to False.
143
+ """
144
+ self.path = path
145
+ self.external_data_dir = external_data_dir
146
+ self.ignore_external_data = util.default(ignore_external_data, False)
147
+
148
+ @util.check_called_by("__call__")
149
+ def call_impl(self):
150
+ """
151
+ Returns:
152
+ onnx.ModelProto: The ONNX model
153
+ """
154
+ G_LOGGER.info(f"Loading model: {self.path}")
155
+ # If external_data_dir is not None, we'll load external data ourselves
156
+ auto_load_ext_data = (
157
+ self.external_data_dir is None and not self.ignore_external_data
158
+ )
159
+ try:
160
+ model = onnx.load(self.path, load_external_data=auto_load_ext_data)
161
+ except FileNotFoundError:
162
+ if auto_load_ext_data:
163
+ G_LOGGER.warning(
164
+ "Failed to load model. This could be because external data could not be loaded.\n"
165
+ "Hint: If you don't need the model weights, try ignoring external data by setting `ignore_external_data=True` "
166
+ "or using the `--ignore-external-data` command-line option."
167
+ )
168
+ raise
169
+
170
+ if self.external_data_dir is not None:
171
+ G_LOGGER.verbose(f"Loading external data from: {self.external_data_dir}")
172
+ onnx.external_data_helper.load_external_data_for_model(
173
+ model, self.external_data_dir
174
+ )
175
+ return model
176
+
177
+
178
+ @mod.export(funcify=True)
179
+ class OnnxFromTfGraph(BaseLoader):
180
+ """
181
+ Functor that loads a TensorFlow graph and converts it to ONNX using the tf2onnx converter.
182
+ """
183
+
184
+ def __init__(self, graph, opset=None, optimize=None):
185
+ """
186
+ Converts a TensorFlow model into ONNX.
187
+
188
+ Args:
189
+ graph (Union[Tuple[tf.Graph, Sequence[str]], Callable() -> Tuple[tf.Graph, Sequence[str]]]):
190
+ A tuple containing a TensorFlow graph and output names or a callable that returns one.
191
+
192
+
193
+ opset (int): The ONNX opset to use during conversion.
194
+ optimize (bool): Whether to use tf2onnx's graph optimization pass.
195
+ """
196
+ self._graph = graph
197
+ self.opset = util.default(opset, 11)
198
+ self.optimize = util.default(optimize, True)
199
+
200
+ @util.check_called_by("__call__")
201
+ def call_impl(self):
202
+ """
203
+ Returns:
204
+ onnx.ModelProto: The ONNX model.
205
+ """
206
+ (graph, output_names), _ = util.invoke_if_callable(self._graph)
207
+ input_names = list(tf_util.get_input_metadata(graph).keys())
208
+
209
+ graphdef = graph.as_graph_def()
210
+ if self.optimize:
211
+ graphdef = tf2onnx.tfonnx.tf_optimize(
212
+ input_names, output_names, graph.as_graph_def()
213
+ )
214
+
215
+ with tf.Graph().as_default() as graph, tf.compat.v1.Session(
216
+ graph=graph
217
+ ) as sess:
218
+ tf.import_graph_def(graphdef, name="")
219
+
220
+ onnx_graph = tf2onnx.tfonnx.process_tf_graph(
221
+ graph,
222
+ input_names=input_names,
223
+ output_names=output_names,
224
+ opset=self.opset,
225
+ )
226
+ if self.optimize:
227
+ onnx_graph = tf2onnx.optimizer.optimize_graph(onnx_graph)
228
+ return onnx_graph.make_model("model")
229
+
230
+
231
+ @mod.export(funcify=True)
232
+ class ModifyOutputs(BaseLoadOnnxCopy):
233
+ """
234
+ Functor that modifies the outputs of an ONNX model.
235
+ """
236
+
237
+ def __init__(
238
+ self, model, outputs=None, exclude_outputs=None, copy=None, mark_types=None
239
+ ):
240
+ """
241
+ Modifies outputs of an ONNX model.
242
+
243
+ Args:
244
+ model (Union[onnx.ModelProto, Callable() -> onnx.ModelProto]):
245
+ An ONNX model or a callable that returns one.
246
+
247
+ outputs (Sequence[str]):
248
+ Names of tensors to mark as outputs. If provided, this will override the
249
+ existing model outputs. Supports fnmatch wildcard patterns (e.g. ``"conv_*"``).
250
+ If a value of `constants.MARK_ALL` is used instead of a list, all tensors in the network are marked.
251
+ exclude_outputs (Sequence[str]):
252
+ Names of tensors to exclude as outputs. Supports fnmatch wildcard patterns (e.g. ``"conv_*"``).
253
+ This can be useful in conjunction with ``outputs=constants.MARK_ALL`` to omit outputs.
254
+ copy (bool): Whether to create a copy of the model first. Defaults to False.
255
+ mark_types (Sequence[str]):
256
+ Op type names (case-insensitive) whose output tensors should be marked as outputs.
257
+ For example, ``mark_types=["Conv", "Add"]`` marks the outputs of all Conv and Add nodes.
258
+ If a requested type is not found in the model, a non-critical error is logged with
259
+ suggestions for similar types.
260
+ """
261
+ super().__init__(model, copy)
262
+ self.outputs = outputs
263
+ self.exclude_outputs = exclude_outputs
264
+ self.mark_types = mark_types
265
+
266
+ @util.check_called_by("__call__")
267
+ def call_impl(self):
268
+ """
269
+ Returns:
270
+ onnx.ModelProto: The ONNX model with modified outputs.
271
+ """
272
+ model = self.load()
273
+
274
+ if self.outputs == constants.MARK_ALL:
275
+ G_LOGGER.verbose("Marking all ONNX tensors as outputs")
276
+ model = onnx_util.mark_layerwise(model)
277
+ elif self.outputs is not None:
278
+ model = onnx_util.mark_outputs(model, self.outputs)
279
+
280
+ if self.mark_types is not None:
281
+ model = onnx_util.mark_by_op_type(model, self.mark_types)
282
+
283
+ if self.exclude_outputs is not None:
284
+ model = onnx_util.unmark_outputs(model, self.exclude_outputs)
285
+
286
+ return model
287
+
288
+
289
+ @mod.export(funcify=True)
290
+ class ConvertToFp16(BaseLoadOnnxCopy):
291
+ """
292
+ Functor that converts all floating point tensors in the model to 16-bit precision.
293
+ This is *not* needed in order to use TensorRT's fp16 precision, but may be useful for other backends.
294
+ """
295
+
296
+ def __init__(self, model, copy=None):
297
+ """
298
+ Converts all floating point tensors in the model to 16-bit precision.
299
+
300
+ Args:
301
+ model (Union[onnx.ModelProto, Callable() -> onnx.ModelProto]):
302
+ An ONNX model or a callable that returns one.
303
+ copy (bool): Whether to create a copy of the model first. Defaults to False.
304
+ """
305
+ super().__init__(model, copy)
306
+
307
+ @util.check_called_by("__call__")
308
+ def call_impl(self):
309
+ """
310
+ Returns:
311
+ onnx.ModelProto: The modified ONNX model.
312
+ """
313
+ model = self.load()
314
+
315
+ G_LOGGER.info("Converting float tensors to float16")
316
+ model = onnxconverter_common.float16.convert_float_to_float16(
317
+ model, keep_io_types=True, disable_shape_infer=True
318
+ )
319
+ return model
320
+
321
+
322
+ @mod.export(funcify=True)
323
+ class FoldConstants(BaseLoadOnnxCopy):
324
+ """
325
+ Functor that folds constants in an ONNX model.
326
+ """
327
+
328
+ def __init__(
329
+ self,
330
+ model,
331
+ num_passes=None,
332
+ do_shape_inference=None,
333
+ partitioning=None,
334
+ fold_shapes=None,
335
+ copy=None,
336
+ error_ok=None,
337
+ size_threshold=None,
338
+ allow_onnxruntime_shape_inference=None,
339
+ ):
340
+ """
341
+ Fold constants in an ONNX model.
342
+
343
+ Args:
344
+ model (Union[onnx.ModelProto, Callable() -> onnx.ModelProto]):
345
+ An ONNX model or a callable that returns one.
346
+
347
+ num_passes (int):
348
+ The number of constant folding passes to run.
349
+ Sometimes, subgraphs that compute tensor shapes may not be foldable in a single pass.
350
+ By default, Polygraphy will automatically determine the number of passes required.
351
+ do_shape_inference (bool):
352
+ Whether to run shape inference in the model between passes.
353
+ This enables the loader to fold `Shape` nodes.
354
+ Only effective if `fold_shapes` is True.
355
+ Defaults to True.
356
+ partitioning (Union[str, None]):
357
+ Whether/How to partition the graph so that errors in folding one
358
+ part of a model do not affect other parts. Available modes are:
359
+
360
+ - None: Do not partition the graph. If inference fails, no constants are folded.
361
+ - 'basic': Partition the graph. If inference fails in one partition, other partitions will remain unaffected.
362
+ - 'recursive': Parition the graph recursively. If inference fails in a partition, the partition will be further partitioned.
363
+
364
+ Defaults to None.
365
+ fold_shapes (bool):
366
+ Whether to fold `Shape` nodes in the graph.
367
+ This requires shapes to be inferred in the graph, and can only fold
368
+ static shapes.
369
+ Defaults to True.
370
+ copy (bool):
371
+ Whether to create a copy of the model first.
372
+ Defaults to False.
373
+ error_ok (bool):
374
+ Whether to suppress errors during constant folding.
375
+ If this is set to ``False``, errors will be re-raised.
376
+ Defaults to True.
377
+ size_threshold (int):
378
+ The maximum size threshold, in bytes, for which to fold constants.
379
+ Any tensors larger than this value will not be folded.
380
+ Set to ``None`` to disable the size threshold and always fold constants.
381
+ For example, some models may apply ops like `Tile` or `Expand` to constants, which can
382
+ result in very large tensors. Rather than pre-computing those constants and bloating
383
+ the model size, it may be desirable to skip folding them and allow them to be computed
384
+ at runtime.
385
+ Defaults to None.
386
+ allow_onnxruntime_shape_inference (bool):
387
+ Allow ONNX-Runtime's shape inference to be used if available instead of ONNX's
388
+ shape inference utilities. The former may provide performance or memory usage benefits.
389
+ Has no effect if ``do_shape_inference`` is False.
390
+ Defaults to True.
391
+ """
392
+ super().__init__(model, copy)
393
+ self.num_passes = num_passes
394
+ self.do_shape_inference = util.default(do_shape_inference, True)
395
+ self.partitioning = partitioning
396
+ self.fold_shapes = util.default(fold_shapes, True)
397
+ self.error_ok = util.default(error_ok, True)
398
+ self.size_threshold = size_threshold
399
+ self.allow_onnxruntime_shape_inference = allow_onnxruntime_shape_inference
400
+
401
+ @util.check_called_by("__call__")
402
+ def call_impl(self):
403
+ """
404
+ Returns:
405
+ onnx.ModelProto: The new ONNX model with constants folded.
406
+ """
407
+
408
+ def run_const_fold_pass(model):
409
+ graph = gs_from_onnx(model)
410
+ del model
411
+
412
+ graph.fold_constants(
413
+ fold_shapes=self.fold_shapes,
414
+ partitioning=self.partitioning,
415
+ size_threshold=self.size_threshold,
416
+ )
417
+
418
+ model = gs.export_onnx(graph.cleanup(), do_type_check=False)
419
+ del graph
420
+
421
+ if self.fold_shapes and self.do_shape_inference:
422
+ model = infer_shapes(
423
+ model, allow_onnxruntime=self.allow_onnxruntime_shape_inference
424
+ )
425
+ return model
426
+
427
+ # Need to manually trigger the autoinstall this since it's used by ONNX-GS, which does not have an autoinstall mechanism.
428
+ mod.autoinstall(onnxrt)
429
+ if not onnxrt.is_installed() or not onnxrt.is_importable():
430
+ G_LOGGER.error(
431
+ f"ONNX-Runtime is not installed, so constant folding may be suboptimal or not work at all.\n"
432
+ f"Consider installing ONNX-Runtime: {sys.executable} -m pip install onnxruntime"
433
+ )
434
+
435
+ model = self.load()
436
+
437
+ prefold_num_nodes = len(model.graph.node)
438
+ postfold_num_nodes = -1
439
+ index = 0
440
+
441
+ while (prefold_num_nodes != postfold_num_nodes) and (
442
+ self.num_passes is None or index < self.num_passes
443
+ ):
444
+ prefold_num_nodes = onnx_util.get_num_nodes(model)
445
+
446
+ G_LOGGER.start(f"Folding Constants | Pass {index + 1}")
447
+ try:
448
+ model = run_const_fold_pass(model)
449
+ except Exception as err:
450
+ if not self.error_ok:
451
+ raise
452
+ G_LOGGER.warning(
453
+ f"Constant folding pass failed. Skipping subsequent passes.\nNote: Error was:\n{err}"
454
+ )
455
+ break
456
+ else:
457
+ postfold_num_nodes = onnx_util.get_num_nodes(model)
458
+ index += 1
459
+
460
+ G_LOGGER.finish(
461
+ f"{constants.TAB}Total Nodes | Original: {prefold_num_nodes:5}, "
462
+ f"After Folding: {postfold_num_nodes:5} | {prefold_num_nodes - postfold_num_nodes:5} Nodes Folded"
463
+ )
464
+
465
+ return model
466
+
467
+
468
+ @mod.export(funcify=True)
469
+ class SetUpperBound(BaseLoadOnnxCopy):
470
+ """
471
+ Functor that sets upper bounds for tensors with unbounded DDS in an ONNX model.
472
+
473
+ Requires that the model has been constant folded and has shapes inferred.
474
+ """
475
+
476
+ def __init__(
477
+ self,
478
+ model,
479
+ upper_bounds,
480
+ copy=None,
481
+ ):
482
+ """
483
+ Set upper bounds for tensors with unbounded DDS in an ONNX model.
484
+
485
+ Args:
486
+ model (Union[onnx.ModelProto, Callable() -> onnx.ModelProto]):
487
+ An ONNX model or a callable that returns one.
488
+
489
+ upper_bounds (Union[int, Dict[str, int]]):
490
+ The upper bounds for tensors with unbounded DDS.
491
+ If a single integer is provided, it will be used as the default upper bound for all tensors with unbounded DDS.
492
+ This can also be provided on a per-tensor basis using a dictionary. In that case, use an empty string ("") as the
493
+ key to specify default upper bound for tensors not explicitly listed.
494
+ copy (bool):
495
+ Whether to create a copy of the model first.
496
+ Defaults to False.
497
+ """
498
+ super().__init__(model, copy)
499
+ self.upper_bounds = upper_bounds
500
+
501
+ def call_impl(self):
502
+ """
503
+ Returns:
504
+ onnx.ModelProto: The new ONNX model.
505
+ """
506
+
507
+ # Set upper bounds for tensors with unbounded DDS in the onnx model.
508
+ def set_upper_bound(graph, target_tensor_list):
509
+ applied_bounds = {}
510
+ for tensor in target_tensor_list:
511
+ upper_bound = util.value_or_from_dict(self.upper_bounds, tensor.name)
512
+ if upper_bound is None:
513
+ continue
514
+ # Insert a min operator to set the upper bound for the target tensor.
515
+ # A target tensor should always be produced from a single node.
516
+ assert len(tensor.inputs) == 1
517
+ producer = tensor.inputs[0]
518
+ producer_idx = producer.outputs.index(tensor)
519
+ tensor_copy = gs.Variable(
520
+ tensor.name + "_copy", dtype=tensor.dtype, shape=tensor.shape
521
+ )
522
+ upper_bound_values = np.array(upper_bound)
523
+ if tensor.shape is not None and len(tensor.shape) > 0:
524
+ upper_bound_values = np.array([upper_bound] * len(tensor.shape))
525
+ tensor_upper_bound = gs.Constant(
526
+ tensor.name + "_upper_bound", values=upper_bound_values
527
+ )
528
+ min_node = gs.Node(
529
+ op="Min", inputs=[tensor_copy, tensor_upper_bound], outputs=[tensor]
530
+ )
531
+ producer.outputs[producer_idx] = tensor_copy
532
+ tensor.inputs = [min_node]
533
+ graph.nodes.append(min_node)
534
+ applied_bounds[tensor.name] = upper_bound
535
+ G_LOGGER.info(f"Set tensor upper bounds: {applied_bounds}")
536
+ return graph
537
+
538
+ model = self.load()
539
+ graph = gs_from_onnx(model)
540
+
541
+ target_tensor_list = onnx_util.get_unbounded_dds_tensors(graph)
542
+
543
+ tensor_map = graph.tensors()
544
+ target_names = {tensor.name for tensor in target_tensor_list}
545
+ if isinstance(self.upper_bounds, dict):
546
+ input_names = set(self.upper_bounds.keys()) - {""}
547
+ # Report error when input tensor name is not in the graph.
548
+ util.check_sequence_contains(
549
+ set(tensor_map.keys()),
550
+ input_names,
551
+ name="the upper bounds dictionary",
552
+ items_name="tensors",
553
+ check_extra=False,
554
+ )
555
+ # Report warning when input tensor is not a unbounded DDS tensor.
556
+ util.check_sequence_contains(
557
+ set(target_names),
558
+ input_names,
559
+ name="the upper bounds dictionary",
560
+ items_name="tensors",
561
+ log_func=G_LOGGER.warning,
562
+ check_extra=False,
563
+ )
564
+ # Still set upper bound for input tensors with bounded shapes.
565
+ target_names.update(input_names)
566
+ graph = set_upper_bound(graph, [tensor_map[name] for name in target_names])
567
+ model = gs.export_onnx(graph.cleanup(), do_type_check=False)
568
+
569
+ return model
570
+
571
+
572
+ @mod.export(funcify=True)
573
+ class InferShapes(BaseLoader):
574
+ """
575
+ Functor that runs shape inference on an ONNX model.
576
+ """
577
+
578
+ def __init__(
579
+ self,
580
+ model,
581
+ error_ok=None,
582
+ external_data_dir=None,
583
+ save_to_disk_threshold_bytes=None,
584
+ allow_onnxruntime=None,
585
+ ):
586
+ """
587
+ Run shape inference on an ONNX model.
588
+
589
+ Args:
590
+ model (Union[onnx.ModelProto, Callable() -> onnx.ModelProto, str, Callable() -> str]):
591
+ An ONNX model or a callable that returns one, or a path to a model.
592
+ Supports models larger than the 2 GB protobuf limit.
593
+
594
+ error_ok (bool):
595
+ Whether errors during shape inference should be suppressed.
596
+ Defaults to True.
597
+ external_data_dir (str):
598
+ The directory where external data for the model is stored.
599
+ Only used if the model is provided via a path rather than a loader.
600
+ save_to_disk_threshold_bytes (int):
601
+ The size in bytes above which a ModelProto will be serialized to the disk
602
+ before running shape inference.
603
+ This can be used to work around the 2 GB protobuf limitation.
604
+ Defaults to 2 GB.
605
+ allow_onnxruntime (bool):
606
+ Allow ONNX-Runtime's shape inference to be used if available instead of ONNX's
607
+ shape inference utilities. The former may provide performance or memory usage benefits.
608
+ Defaults to True.
609
+ """
610
+ self._model = model
611
+ self.error_ok = util.default(error_ok, True)
612
+ self.external_data_dir = external_data_dir
613
+ # Subtract a little so we're below the real threshold
614
+ self.save_to_disk_threshold_bytes = util.default(
615
+ save_to_disk_threshold_bytes, PROTOBUF_THRESHOLD
616
+ )
617
+ self.allow_onnxruntime = util.default(allow_onnxruntime, True)
618
+
619
+ def _run_onnx_shape_inference(self, model, external_data_dir):
620
+ if isinstance(model, onnx.ModelProto):
621
+ MODEL_SIZE = model.ByteSize()
622
+ if MODEL_SIZE > LARGE_MODEL_THRESHOLD:
623
+ G_LOGGER.warning(
624
+ f"Attempting to run shape inference on a large model ({MODEL_SIZE // 1024.0 ** 2} MiB). "
625
+ "This may require a large amount of memory.\nIf memory consumption becomes too high, "
626
+ "the process may be killed. You may want to try disabling shape inference in that case. ",
627
+ mode=LogMode.ONCE,
628
+ )
629
+
630
+ if MODEL_SIZE >= self.save_to_disk_threshold_bytes:
631
+ G_LOGGER.warning(
632
+ f"Model size ({MODEL_SIZE / 1024.0 ** 2} MiB) exceeds the in-memory size threshold: "
633
+ f"{self.save_to_disk_threshold_bytes / 1024.0 ** 2} MiB.\n"
634
+ f"The model will be saved to a temporary file before shape inference is run.",
635
+ mode=LogMode.ONCE,
636
+ )
637
+ outdir = tempfile.TemporaryDirectory()
638
+ outpath = os.path.join(outdir.name, "tmp_model.onnx")
639
+ save_onnx(model, outpath, external_data_path="ext.data")
640
+ model = outpath
641
+ external_data_dir = outdir.name
642
+
643
+ if isinstance(model, onnx.ModelProto):
644
+ model = onnx.shape_inference.infer_shapes(model)
645
+ else:
646
+ tmp_path = util.NamedTemporaryFile(
647
+ prefix="tmp_polygraphy_", suffix=".onnx"
648
+ ).name
649
+ G_LOGGER.verbose(f"Writing shape-inferred model to: {tmp_path}")
650
+ onnx.shape_inference.infer_shapes_path(model, tmp_path)
651
+ # In cases where the original model had external data stored in the same directory,
652
+ # the external data directory may not be explicitly specified.
653
+ # In such cases, we need to use the model's directory as the external data path
654
+ # for the newly generated model.
655
+ model = onnx_from_path(
656
+ tmp_path,
657
+ external_data_dir=util.default(
658
+ external_data_dir, os.path.dirname(model) or None
659
+ ),
660
+ )
661
+ return model
662
+
663
+ def _run_onnxruntime_shape_inference(self, model, external_data_dir):
664
+ if not isinstance(model, onnx.ModelProto):
665
+ model = onnx_from_path(model, external_data_dir=external_data_dir)
666
+ return onnxrt_symbolic_shape_inference.SymbolicShapeInference.infer_shapes(
667
+ model, auto_merge=True
668
+ )
669
+
670
+ @util.check_called_by("__call__")
671
+ def call_impl(self):
672
+ """
673
+ Returns:
674
+ onnx.ModelProto: The new ONNX model with shapes inferred.
675
+ """
676
+ model, _ = util.invoke_if_callable(self._model)
677
+ external_data_dir = self.external_data_dir
678
+
679
+ G_LOGGER.verbose("Starting shape inference")
680
+
681
+ try:
682
+ use_onnx_shape_inference = not self.allow_onnxruntime
683
+ if self.allow_onnxruntime:
684
+ try:
685
+ model = self._run_onnxruntime_shape_inference(
686
+ model, external_data_dir
687
+ )
688
+ G_LOGGER.verbose(
689
+ "Inferred shapes in the model with `onnxruntime.tools.symbolic_shape_infer`.\n"
690
+ "Note: To force Polygraphy to use `onnx.shape_inference` instead, set `allow_onnxruntime=False` or "
691
+ "use the `--no-onnxruntime-shape-inference` command-line option.",
692
+ mode=LogMode.ONCE,
693
+ )
694
+ except Exception as err:
695
+ use_onnx_shape_inference = True
696
+ G_LOGGER.extra_verbose(
697
+ f"Error while running `onnxruntime.tools.symbolic_shape_infer`:\n{err}"
698
+ )
699
+ G_LOGGER.warning(
700
+ "Falling back to `onnx.shape_inference` because `onnxruntime.tools.symbolic_shape_infer` either could not be loaded "
701
+ "or did not run successfully.\n"
702
+ "Note that using ONNX-Runtime for shape inference may be faster and require less memory.\n"
703
+ "Consider installing ONNX-Runtime or setting POLYGRAPHY_AUTOINSTALL_DEPS=1 in your environment "
704
+ "variables to allow Polygraphy to do so automatically.",
705
+ mode=LogMode.ONCE,
706
+ )
707
+
708
+ if use_onnx_shape_inference:
709
+ model = self._run_onnx_shape_inference(model, external_data_dir)
710
+ except Exception as err:
711
+ if not self.error_ok:
712
+ raise
713
+ G_LOGGER.warning(f"ONNX shape inference exited with an error:\n{err}")
714
+ G_LOGGER.internal_error(
715
+ f"ONNX shape inference exited with an error:\n{err}"
716
+ )
717
+
718
+ if not isinstance(model, onnx.ModelProto):
719
+ model = onnx_from_path(model, external_data_dir=external_data_dir)
720
+ else:
721
+ G_LOGGER.verbose("Shape inference completed successfully")
722
+
723
+ return model
724
+
725
+
726
+ @mod.export(funcify=True)
727
+ class ExtractSubgraph(BaseLoader):
728
+ """
729
+ Functor that extracts a subgraph from an ONNX model.
730
+ """
731
+
732
+ def __init__(
733
+ self, model, input_metadata=None, output_metadata=None, check_meta=None
734
+ ):
735
+ """
736
+ Extracts a subgraph from an ONNX model.
737
+
738
+ Args:
739
+ model (Union[Union[onnx.ModelProto, onnx_graphsurgeon.Graph], Callable() -> Union[onnx.ModelProto, onnx_graphsurgeon.Graph]]):
740
+ An ONNX model or ONNX-GraphSurgeon Graph or a callable that returns one.
741
+
742
+ input_metadata (TensorMetadata):
743
+ Metadata for the inputs of the subgraph.
744
+ Name, shape, and data type are required.
745
+ If not provided, the graph outputs are not modified.
746
+ output_metadata (TensorMetadata):
747
+ Metadata for the outputs of the subgraph.
748
+ Name and data type are required.
749
+ If not provided, the graph outputs are not modified.
750
+ check_meta (bool):
751
+ Whether to check that the provided input and output metadata include
752
+ all the expected fields.
753
+ Defaults to True.
754
+ """
755
+ self._model = model
756
+ self.input_metadata = input_metadata
757
+ self.output_metadata = output_metadata
758
+ self.check_meta = util.default(check_meta, True)
759
+
760
+ @util.check_called_by("__call__")
761
+ def call_impl(self):
762
+ """
763
+ Returns:
764
+ Union[onnx.ModelProto, onnx_graphsurgeon.Graph]:
765
+ The new ONNX model or ONNX-GraphSurgeon Graph.
766
+ """
767
+ with _GSGraphManager(self._model) as manager:
768
+ graph = manager.graph
769
+ TENSOR_MAP = graph.tensors()
770
+
771
+ def get_tensor(name):
772
+ if name not in TENSOR_MAP:
773
+ G_LOGGER.critical(f"Tensor: {name} does not exist in the model.")
774
+ return TENSOR_MAP[name]
775
+
776
+ def update_tensor(name, dtype, shape):
777
+ tensor = get_tensor(name)
778
+ # No need to update constants
779
+ if isinstance(tensor, gs.Variable):
780
+ tensor.dtype, tensor.shape = (
781
+ DataType.to_dtype(DataType.from_dtype(dtype), "onnx")
782
+ if dtype is not None
783
+ else None
784
+ ) or tensor.dtype, shape or tensor.shape
785
+ return tensor
786
+
787
+ def check_meta(name, dtype, shape, meta_type, needs_shape=True):
788
+ if not self.check_meta:
789
+ return
790
+ if needs_shape and shape is None:
791
+ G_LOGGER.warning(
792
+ f"{meta_type} metadata should include shape, but no shape was provided for tensor: {name}"
793
+ )
794
+ if dtype is None:
795
+ G_LOGGER.warning(
796
+ f"{meta_type} metadata should include data type, but no data type was provided for tensor: {name}"
797
+ )
798
+
799
+ if self.input_metadata is not None:
800
+ graph.inputs.clear()
801
+ for name, (dtype, shape) in self.input_metadata.items():
802
+ tensor = update_tensor(name, dtype, shape)
803
+ check_meta(name, tensor.dtype, tensor.shape, "Input")
804
+ tensor.inputs.clear()
805
+ graph.inputs.append(tensor)
806
+
807
+ if self.output_metadata is not None:
808
+ graph.outputs.clear()
809
+ for name, (dtype, shape) in self.output_metadata.items():
810
+ tensor = update_tensor(name, dtype, shape)
811
+ check_meta(
812
+ name, tensor.dtype, tensor.shape, "Output", needs_shape=False
813
+ )
814
+ graph.outputs.append(tensor)
815
+
816
+ graph.cleanup()
817
+
818
+ tensor_map = graph.tensors()
819
+ for tensor in tensor_map.values():
820
+ if (
821
+ isinstance(tensor, gs.Variable)
822
+ and not tensor.inputs
823
+ and tensor not in graph.inputs
824
+ ):
825
+ consumer_nodes = [
826
+ f"Node: '{node.name}' (Op: {node.op})"
827
+ for node in tensor.outputs
828
+ ]
829
+ G_LOGGER.error(
830
+ f"Tensor: '{tensor.name}' is a variable tensor consumed by: {consumer_nodes}, "
831
+ "but is not produced by a node or marked as a graph input."
832
+ f"\nDid you forget to mark a tensor as a graph input? Hint: Try inspecting the resulting model. "
833
+ f"\nNote: The resulting model will not be valid!"
834
+ )
835
+
836
+ return manager.retval
837
+
838
+
839
+ @mod.export(funcify=True)
840
+ class SaveOnnx(BaseLoader):
841
+ """
842
+ Functor that saves an ONNX model to the specified path.
843
+ """
844
+
845
+ def __init__(
846
+ self,
847
+ model,
848
+ path,
849
+ external_data_path=None,
850
+ size_threshold=None,
851
+ all_tensors_to_one_file=None,
852
+ ):
853
+ """
854
+ Saves an ONNX model to the specified path.
855
+
856
+ Args:
857
+ model (Union[onnx.ModelProto, Callable() -> onnx.ModelProto]):
858
+ An ONNX model or a callable that returns one.
859
+ path (str): Path at which to write the ONNX model.
860
+ external_data_path (str):
861
+ Path to save external data.
862
+ This is always a relative path; external data is always written to the same
863
+ directory as the model.
864
+ Set to an empty string to use the default path.
865
+ Set to None to disable.
866
+ Defaults to None if the model is within the protobuf size threshold and an empty string otherwise.
867
+ size_threshold (int):
868
+ Tensor size threshold, in bytes, above which tensor data will be
869
+ stored in the external file.
870
+ Tensors smaller that this threshold will remain in the ONNX file.
871
+ Has no effect if external_data_path is not set.
872
+ Defaults to 1024.
873
+ all_tensors_to_one_file (bool):
874
+ Whether to write all tensors to one file when saving external data.
875
+ Has no effect if external_data_path is not set.
876
+ Defaults to True.
877
+ """
878
+ self._model = model
879
+ self.path = path
880
+ self.external_data_path = external_data_path
881
+ self.size_threshold = size_threshold
882
+ self.all_tensors_to_one_file = all_tensors_to_one_file
883
+
884
+ @util.check_called_by("__call__")
885
+ def call_impl(self):
886
+ """
887
+ Returns:
888
+ onnx.ModelProto: The model, after saving it.
889
+ """
890
+ model, _ = util.invoke_if_callable(self._model)
891
+ G_LOGGER.info(f"Saving ONNX model to: {self.path}")
892
+
893
+ model_size = model.ByteSize()
894
+ if self.external_data_path is None and model_size >= PROTOBUF_THRESHOLD:
895
+ external_data_path = ""
896
+ G_LOGGER.warning(
897
+ f"Model size ({model_size // 1024.0 ** 2} MiB) exceeds protobuf size threshold ({PROTOBUF_THRESHOLD // 1024 ** 2} MiB). "
898
+ f"Will save weight data to an external file.\n"
899
+ f"To control the location of this file, use the `external_data_path` parameter or the `--external-data-path` command-line option. "
900
+ )
901
+ else:
902
+ external_data_path = self.external_data_path
903
+
904
+ if external_data_path is not None:
905
+ G_LOGGER.verbose(
906
+ f"Saving external data for ONNX model to: {external_data_path}"
907
+ )
908
+ try:
909
+ onnx.external_data_helper.convert_model_to_external_data(
910
+ model,
911
+ location=external_data_path,
912
+ all_tensors_to_one_file=util.default(
913
+ self.all_tensors_to_one_file, True
914
+ ),
915
+ size_threshold=util.default(self.size_threshold, 1024),
916
+ )
917
+ except TypeError:
918
+ if self.size_threshold is not None:
919
+ G_LOGGER.warning(
920
+ "This version of onnx does not support size_threshold in convert_model_to_external_data"
921
+ )
922
+ onnx.external_data_helper.convert_model_to_external_data(
923
+ model,
924
+ location=external_data_path,
925
+ all_tensors_to_one_file=util.default(
926
+ self.all_tensors_to_one_file, True
927
+ ),
928
+ )
929
+ else:
930
+ if self.size_threshold is not None:
931
+ G_LOGGER.warning(
932
+ "size_threshold is set, but external data path has not been set. "
933
+ "No external data will be written."
934
+ )
935
+ if self.all_tensors_to_one_file is not None:
936
+ G_LOGGER.warning(
937
+ "all_tensors_to_one_file is set, but external data path has not been set. "
938
+ "No external data will be written."
939
+ )
940
+
941
+ util.makedirs(self.path)
942
+ onnx.save(model, self.path)
943
+ return model
944
+
945
+
946
+ @mod.export(funcify=True)
947
+ class BytesFromOnnx(BaseLoader):
948
+ """
949
+ Functor that serializes an ONNX model.
950
+ """
951
+
952
+ def __init__(self, model):
953
+ """
954
+ Serializes an ONNX model.
955
+
956
+ Args:
957
+ model (Union[onnx.ModelProto, Callable() -> onnx.ModelProto]):
958
+ An ONNX model or a callable that returns one.
959
+ """
960
+ self._model = model
961
+
962
+ @util.check_called_by("__call__")
963
+ def call_impl(self):
964
+ """
965
+ Returns:
966
+ bytes: The serialized model.
967
+ """
968
+ model, _ = util.invoke_if_callable(self._model)
969
+ return model.SerializeToString()
970
+
971
+
972
+ @mod.export(funcify=True)
973
+ class OnnxFromBytes(BaseLoader):
974
+ """
975
+ Functor that deserializes an ONNX model.
976
+ """
977
+
978
+ def __init__(self, serialized_onnx):
979
+ """
980
+ Deserializes an ONNX model.
981
+
982
+ Args:
983
+ serialized_onnx (Union[bytes, Callable() -> bytes]):
984
+ A serialized ONNX model or a callable that returns one.
985
+ """
986
+ self._serialized_onnx = serialized_onnx
987
+
988
+ @util.check_called_by("__call__")
989
+ def call_impl(self):
990
+ """
991
+ Returns:
992
+ onnx.ModelProto: The ONNX model.
993
+ """
994
+ serialized_onnx, _ = util.invoke_if_callable(self._serialized_onnx)
995
+ model = onnx.ModelProto()
996
+ model.ParseFromString(serialized_onnx)
997
+ return model