onnxscript 0.1.0__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 (176) hide show
  1. onnxscript/__init__.py +131 -0
  2. onnxscript/_framework_apis/__init__.py +3 -0
  3. onnxscript/_framework_apis/torch_2_5.py +117 -0
  4. onnxscript/_framework_apis/torch_2_6.py +45 -0
  5. onnxscript/_internal/__init__.py +0 -0
  6. onnxscript/_internal/analysis.py +229 -0
  7. onnxscript/_internal/ast_utils.py +64 -0
  8. onnxscript/_internal/autocast.py +250 -0
  9. onnxscript/_internal/deprecation.py +78 -0
  10. onnxscript/_internal/param_manipulation.py +148 -0
  11. onnxscript/_internal/runtime_typing.py +43 -0
  12. onnxscript/_internal/utils.py +99 -0
  13. onnxscript/_internal/version_utils.py +118 -0
  14. onnxscript/_legacy_ir/__init__.py +341 -0
  15. onnxscript/_legacy_ir/visitor.py +937 -0
  16. onnxscript/_thirdparty/asciichartpy.py +313 -0
  17. onnxscript/backend/__init__.py +2 -0
  18. onnxscript/backend/onnx_backend.py +303 -0
  19. onnxscript/backend/onnx_export.py +885 -0
  20. onnxscript/converter.py +1470 -0
  21. onnxscript/evaluator.py +619 -0
  22. onnxscript/function_libs/tools/torch_lib/deduce_type_constraints.py +403 -0
  23. onnxscript/function_libs/tools/torch_lib/generate_aten_signatures.py +333 -0
  24. onnxscript/function_libs/tools/torch_lib/generate_prims_signatures.py +331 -0
  25. onnxscript/function_libs/torch_lib/__init__.py +12 -0
  26. onnxscript/function_libs/torch_lib/_constants.py +5 -0
  27. onnxscript/function_libs/torch_lib/_flags.py +58 -0
  28. onnxscript/function_libs/torch_lib/graph_building/__init__.py +56 -0
  29. onnxscript/function_libs/torch_lib/graph_building/_graph_building_ir.py +723 -0
  30. onnxscript/function_libs/torch_lib/graph_building/_graph_building_torch.py +1125 -0
  31. onnxscript/function_libs/torch_lib/ops/__init__.py +27 -0
  32. onnxscript/function_libs/torch_lib/ops/common.py +80 -0
  33. onnxscript/function_libs/torch_lib/ops/core.py +8935 -0
  34. onnxscript/function_libs/torch_lib/ops/fft.py +385 -0
  35. onnxscript/function_libs/torch_lib/ops/linalg.py +399 -0
  36. onnxscript/function_libs/torch_lib/ops/nested.py +25 -0
  37. onnxscript/function_libs/torch_lib/ops/nn.py +2713 -0
  38. onnxscript/function_libs/torch_lib/ops/prims.py +850 -0
  39. onnxscript/function_libs/torch_lib/ops/quantized_decomposed.py +63 -0
  40. onnxscript/function_libs/torch_lib/ops/sparse.py +23 -0
  41. onnxscript/function_libs/torch_lib/ops/special.py +387 -0
  42. onnxscript/function_libs/torch_lib/ops/vision.py +25 -0
  43. onnxscript/function_libs/torch_lib/registration.py +151 -0
  44. onnxscript/function_libs/torch_lib/tensor_typing.py +74 -0
  45. onnxscript/ir/__init__.py +153 -0
  46. onnxscript/ir/_convenience.py +447 -0
  47. onnxscript/ir/_core.py +3119 -0
  48. onnxscript/ir/_display.py +49 -0
  49. onnxscript/ir/_enums.py +163 -0
  50. onnxscript/ir/_graph_comparison.py +23 -0
  51. onnxscript/ir/_io.py +97 -0
  52. onnxscript/ir/_linked_list.py +276 -0
  53. onnxscript/ir/_metadata.py +44 -0
  54. onnxscript/ir/_name_authority.py +72 -0
  55. onnxscript/ir/_polyfill.py +25 -0
  56. onnxscript/ir/_protocols.py +598 -0
  57. onnxscript/ir/_schemas.py +548 -0
  58. onnxscript/ir/_tape.py +120 -0
  59. onnxscript/ir/_type_casting.py +106 -0
  60. onnxscript/ir/convenience.py +32 -0
  61. onnxscript/ir/external_data.py +396 -0
  62. onnxscript/ir/passes/__init__.py +33 -0
  63. onnxscript/ir/passes/_pass_infra.py +172 -0
  64. onnxscript/ir/serde.py +1620 -0
  65. onnxscript/ir/tensor_adapters.py +122 -0
  66. onnxscript/ir/traversal.py +82 -0
  67. onnxscript/irbuilder.py +542 -0
  68. onnxscript/main.py +167 -0
  69. onnxscript/onnx_opset/__init__.py +232 -0
  70. onnxscript/onnx_opset/_impl/opset1.py +4100 -0
  71. onnxscript/onnx_opset/_impl/opset10.py +1227 -0
  72. onnxscript/onnx_opset/_impl/opset11.py +4013 -0
  73. onnxscript/onnx_opset/_impl/opset12.py +1078 -0
  74. onnxscript/onnx_opset/_impl/opset13.py +3924 -0
  75. onnxscript/onnx_opset/_impl/opset14.py +999 -0
  76. onnxscript/onnx_opset/_impl/opset15.py +604 -0
  77. onnxscript/onnx_opset/_impl/opset16.py +1255 -0
  78. onnxscript/onnx_opset/_impl/opset17.py +561 -0
  79. onnxscript/onnx_opset/_impl/opset18.py +1803 -0
  80. onnxscript/onnx_opset/_impl/opset19.py +1942 -0
  81. onnxscript/onnx_opset/_impl/opset2.py +218 -0
  82. onnxscript/onnx_opset/_impl/opset20.py +675 -0
  83. onnxscript/onnx_opset/_impl/opset21.py +1976 -0
  84. onnxscript/onnx_opset/_impl/opset22.py +2588 -0
  85. onnxscript/onnx_opset/_impl/opset3.py +199 -0
  86. onnxscript/onnx_opset/_impl/opset4.py +77 -0
  87. onnxscript/onnx_opset/_impl/opset5.py +84 -0
  88. onnxscript/onnx_opset/_impl/opset6.py +944 -0
  89. onnxscript/onnx_opset/_impl/opset7.py +1243 -0
  90. onnxscript/onnx_opset/_impl/opset8.py +444 -0
  91. onnxscript/onnx_opset/_impl/opset9.py +1485 -0
  92. onnxscript/onnx_opset/_impl/opset_ai_onnx_ml1.py +974 -0
  93. onnxscript/onnx_opset/_impl/opset_ai_onnx_ml2.py +112 -0
  94. onnxscript/onnx_opset/_impl/opset_ai_onnx_ml3.py +308 -0
  95. onnxscript/onnx_opset/_impl/opset_ai_onnx_ml4.py +129 -0
  96. onnxscript/onnx_opset/_impl/opset_ai_onnx_ml5.py +158 -0
  97. onnxscript/onnx_opset/_impl/opset_ai_onnx_preview_training1.py +577 -0
  98. onnxscript/onnx_types.py +229 -0
  99. onnxscript/optimizer/__init__.py +39 -0
  100. onnxscript/optimizer/_constant_folding.py +1083 -0
  101. onnxscript/optimizer/_inliner.py +312 -0
  102. onnxscript/optimizer/_legacy/_optimizer.py +98 -0
  103. onnxscript/optimizer/_legacy/_remove_unused_proto.py +144 -0
  104. onnxscript/optimizer/_legacy/_simple_function_folding.py +243 -0
  105. onnxscript/optimizer/_legacy/constant_folding.py +293 -0
  106. onnxscript/optimizer/_legacy/evaluator.py +439 -0
  107. onnxscript/optimizer/_optimizer.py +61 -0
  108. onnxscript/optimizer/_remove_unused.py +106 -0
  109. onnxscript/optimizer/_remove_unused_function.py +72 -0
  110. onnxscript/py.typed +1 -0
  111. onnxscript/rewriter/__init__.py +56 -0
  112. onnxscript/rewriter/_ir_utils.py +111 -0
  113. onnxscript/rewriter/broadcast_to_matmul.py +180 -0
  114. onnxscript/rewriter/cast_constant_of_shape.py +50 -0
  115. onnxscript/rewriter/collapse_slices.py +141 -0
  116. onnxscript/rewriter/erfgelu.py +27 -0
  117. onnxscript/rewriter/function_rule.py +232 -0
  118. onnxscript/rewriter/gemm_to_matmul_add.py +21 -0
  119. onnxscript/rewriter/generic_pattern.py +700 -0
  120. onnxscript/rewriter/llama_rule_sets.py +287 -0
  121. onnxscript/rewriter/no_op.py +56 -0
  122. onnxscript/rewriter/onnxruntime/__init__.py +50 -0
  123. onnxscript/rewriter/onnxruntime/bfloat16_utils/bfloat16_converter.py +99 -0
  124. onnxscript/rewriter/onnxruntime/fused_matmul_rule_sets.py +177 -0
  125. onnxscript/rewriter/onnxruntime/group_normalization_merge_silu.py +64 -0
  126. onnxscript/rewriter/onnxruntime/instance_to_group_normalization.py +155 -0
  127. onnxscript/rewriter/onnxruntime/softmax.py +63 -0
  128. onnxscript/rewriter/onnxruntime/transformers/__init__.py +21 -0
  129. onnxscript/rewriter/onnxruntime/transformers/biassplitgelu.py +31 -0
  130. onnxscript/rewriter/onnxruntime/transformers/fastgelu.py +29 -0
  131. onnxscript/rewriter/onnxruntime/transformers/layernorm.py +47 -0
  132. onnxscript/rewriter/onnxruntime/transformers/multihead_attention.py +715 -0
  133. onnxscript/rewriter/ort_fusions/__init__.py +9 -0
  134. onnxscript/rewriter/ort_fusions/_core.py +28 -0
  135. onnxscript/rewriter/ort_fusions/_smollm_1.py +253 -0
  136. onnxscript/rewriter/ort_fusions/_smollm_2.py +467 -0
  137. onnxscript/rewriter/ort_fusions/_test_models.py +122 -0
  138. onnxscript/rewriter/ort_fusions/_test_utils.py +42 -0
  139. onnxscript/rewriter/ort_fusions/cos_sin_cache.py +154 -0
  140. onnxscript/rewriter/ort_fusions/gqa.py +156 -0
  141. onnxscript/rewriter/ort_fusions/mha.py +198 -0
  142. onnxscript/rewriter/ort_fusions/rms_normalization.py +95 -0
  143. onnxscript/rewriter/ort_fusions/rotary_embedding.py +64 -0
  144. onnxscript/rewriter/ort_fusions/sdpa.py +75 -0
  145. onnxscript/rewriter/ort_fusions/skip_normalization.py +46 -0
  146. onnxscript/rewriter/pattern.py +1714 -0
  147. onnxscript/rewriter/testing.py +77 -0
  148. onnxscript/sourceinfo.py +59 -0
  149. onnxscript/tensor.py +227 -0
  150. onnxscript/testing/__init__.py +482 -0
  151. onnxscript/tools/__init__.py +4 -0
  152. onnxscript/tools/benchmark/__init__.py +23 -0
  153. onnxscript/tools/benchmark/benchmark_helpers.py +783 -0
  154. onnxscript/tools/benchmark/benchmark_run.py +140 -0
  155. onnxscript/tools/benchmark/export_model.py +207 -0
  156. onnxscript/tools/benchmark/export_model_batch.py +146 -0
  157. onnxscript/tools/memory_peak.py +244 -0
  158. onnxscript/tools/training_helper.py +50 -0
  159. onnxscript/tools/transformers_models/__init__.py +190 -0
  160. onnxscript/tools/transformers_models/llama.py +168 -0
  161. onnxscript/tools/transformers_models/mistral.py +238 -0
  162. onnxscript/tools/transformers_models/phi.py +248 -0
  163. onnxscript/tools/transformers_models/phi3.py +259 -0
  164. onnxscript/type_annotation.py +281 -0
  165. onnxscript/utils/__init__.py +0 -0
  166. onnxscript/utils/evaluation_utils.py +56 -0
  167. onnxscript/utils/timing_utils.py +33 -0
  168. onnxscript/utils/utils.py +84 -0
  169. onnxscript/values.py +790 -0
  170. onnxscript/version_converter/__init__.py +21 -0
  171. onnxscript/version_converter/_version_converter.py +314 -0
  172. onnxscript-0.1.0.dist-info/LICENSE +21 -0
  173. onnxscript-0.1.0.dist-info/METADATA +370 -0
  174. onnxscript-0.1.0.dist-info/RECORD +176 -0
  175. onnxscript-0.1.0.dist-info/WHEEL +5 -0
  176. onnxscript-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,244 @@
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
3
+ # pylint: disable=import-outside-toplevel
4
+ from __future__ import annotations
5
+
6
+ import multiprocessing
7
+ import os
8
+
9
+
10
+ def get_memory_rss(pid: int) -> int:
11
+ """
12
+ Returns the physical memory used by a process.
13
+
14
+ Args:
15
+ pid: Process id, current one is `os.getpid()`.
16
+
17
+ Returns:
18
+ Physical memory.
19
+
20
+ It relies on the module *psutil*.
21
+ """
22
+ import psutil
23
+
24
+ process = psutil.Process(pid)
25
+ mem = process.memory_info().rss
26
+ return mem
27
+
28
+
29
+ class Monitor:
30
+ def __init__(self):
31
+ self.max_peak: float = 0
32
+ self.average: float = 0
33
+ self.n_measures: int = 0
34
+ self.begin: float = 0
35
+ self.end: float = 0
36
+
37
+ def to_dict(self, unit: int = 1) -> dict[str, float]:
38
+ funit = float(unit)
39
+ return dict(
40
+ peak=self.max_peak / funit,
41
+ mean=self.average * 1.0 / self.n_measures / funit,
42
+ n=self.n_measures / funit,
43
+ begin=self.begin / funit,
44
+ end=self.end / funit,
45
+ )
46
+
47
+ def __repr__(self) -> str:
48
+ return (
49
+ f"{self.__class__.__name__}(peak={self.max_peak}, "
50
+ f"average={self.average}, n={self.n_measures})"
51
+ )
52
+
53
+ def update(self, mem: float):
54
+ if self.n_measures == 0:
55
+ self.begin = mem
56
+ self.max_peak = max(mem, self.max_peak)
57
+ self.average += mem
58
+ self.end = mem
59
+ self.n_measures += 1
60
+
61
+ def send(self, conn):
62
+ conn.send(self.max_peak)
63
+ conn.send(self.average)
64
+ conn.send(self.n_measures)
65
+ conn.send(self.begin)
66
+ conn.send(self.end)
67
+
68
+ @classmethod
69
+ def recv(cls, conn) -> Monitor:
70
+ m = cls()
71
+ m.max_peak = conn.recv()
72
+ m.average = conn.recv()
73
+ m.n_measures = conn.recv()
74
+ m.begin = conn.recv()
75
+ m.end = conn.recv()
76
+ return m
77
+
78
+
79
+ def _process_memory_spy(conn):
80
+ # Sends the value it started.
81
+ conn.send(-2)
82
+
83
+ # process id to spy on
84
+ pid = conn.recv()
85
+
86
+ # delay between two measures
87
+ timeout = conn.recv()
88
+
89
+ # do CUDA
90
+ cuda = conn.recv()
91
+
92
+ import psutil
93
+
94
+ process = psutil.Process(pid)
95
+
96
+ if cuda:
97
+ from pynvml import ( # type: ignore[import-not-found]
98
+ nvmlDeviceGetCount,
99
+ nvmlDeviceGetHandleByIndex,
100
+ nvmlDeviceGetMemoryInfo,
101
+ nvmlInit,
102
+ nvmlShutdown,
103
+ )
104
+
105
+ nvmlInit()
106
+ n_gpus = nvmlDeviceGetCount()
107
+ handles = [nvmlDeviceGetHandleByIndex(i) for i in range(n_gpus)]
108
+
109
+ def gpu_used():
110
+ return [nvmlDeviceGetMemoryInfo(h).used for h in handles]
111
+
112
+ gpus = [Monitor() for i in range(n_gpus)]
113
+ else:
114
+ gpus = []
115
+
116
+ cpu = Monitor()
117
+
118
+ conn.send(-2)
119
+
120
+ # loop
121
+ while True:
122
+ mem = process.memory_info().rss
123
+ cpu.update(mem)
124
+ if cuda:
125
+ for r, g in zip(gpu_used(), gpus):
126
+ g.update(r)
127
+ if conn.poll(timeout=timeout):
128
+ code = conn.recv()
129
+ if code == -3:
130
+ break
131
+
132
+ # final iteration
133
+ end = process.memory_info().rss
134
+ cpu.update(end)
135
+ if cuda:
136
+ for r, g in zip(gpu_used(), gpus):
137
+ g.update(r)
138
+
139
+ # send
140
+ cpu.send(conn)
141
+ conn.send(len(gpus))
142
+ for g in gpus:
143
+ g.send(conn)
144
+ if cuda:
145
+ nvmlShutdown()
146
+ conn.close()
147
+
148
+
149
+ class MemorySpy:
150
+ """
151
+ Information about the spy. It class method `start`.
152
+ Method `stop` can be called to end the measure.
153
+
154
+ Args:
155
+ pid: process id of the process to spy on
156
+ delay: spy on every delay seconds
157
+ cuda: enable cuda monitoring
158
+ """
159
+
160
+ def __init__(self, pid: int, delay: float = 0.01, cuda: bool = False):
161
+ self.pid = pid
162
+ self.delay = delay
163
+ self.cuda = cuda
164
+ self.start()
165
+
166
+ def start(self) -> MemorySpy:
167
+ """Starts another process and tells it to spy."""
168
+ self.parent_conn, self.child_conn = multiprocessing.Pipe()
169
+ self.child_process = multiprocessing.Process(
170
+ target=_process_memory_spy, args=(self.child_conn,)
171
+ )
172
+ self.child_process.start()
173
+ data = self.parent_conn.recv()
174
+ if data != -2:
175
+ raise RuntimeError(f"The child processing is supposed to send -2 not {data}.")
176
+ self.parent_conn.send(self.pid)
177
+ self.parent_conn.send(self.delay)
178
+ self.parent_conn.send(1 if self.cuda else 0)
179
+ data = self.parent_conn.recv()
180
+ if data != -2:
181
+ raise RuntimeError(
182
+ f"The child processing is supposed to send -2 again not {data}."
183
+ )
184
+ return self
185
+
186
+ def stop(self) -> dict[str, list[Monitor]]:
187
+ """Stops spying on."""
188
+ self.parent_conn.send(-3)
189
+
190
+ cpu = [Monitor.recv(self.parent_conn)]
191
+
192
+ n_gpus = self.parent_conn.recv()
193
+ gpus = []
194
+ for _ in range(n_gpus):
195
+ gpus.append(Monitor.recv(self.parent_conn))
196
+
197
+ self.parent_conn.close()
198
+ self.child_process.join()
199
+ res = dict(cpu=cpu)
200
+ if self.cuda:
201
+ res["gpus"] = gpus
202
+ return res
203
+
204
+
205
+ def start_spying_on(
206
+ pid: int | None = None, delay: float = 0.01, cuda: bool = False
207
+ ) -> MemorySpy:
208
+ """Starts the memory spy. The function starts another
209
+ process spying on the one sent as an argument.
210
+
211
+ Example::
212
+
213
+ .. code-block:: python
214
+
215
+ from onnxscript.tools.memory_peak import start_spying_on, flatten
216
+
217
+ p = start_spying_on()
218
+ # ...
219
+ # code to measure
220
+ # ...
221
+ stat = p.stop()
222
+ print(stat)
223
+ print(flatten(stat))
224
+
225
+ Args:
226
+ pid: process id to spy or the the current one.
227
+ delay: delay between two measures.
228
+ cuda: True or False to get memory for cuda devices
229
+ """
230
+ if pid is None:
231
+ pid = os.getpid()
232
+ return MemorySpy(pid, delay, cuda)
233
+
234
+
235
+ def flatten(ps: dict[str, list[Monitor]], prefix: str = "") -> dict[str, float]:
236
+ """Flattens a dictionary produced by :meth:`MemorySpy.stop`."""
237
+ obs = ps["cpu"][0].to_dict(unit=2**20)
238
+ if "gpus" in ps:
239
+ for i, g in enumerate(ps["gpus"]):
240
+ for k, v in g.to_dict(unit=2**20).items():
241
+ obs[f"gpu{i}_{k}"] = v
242
+ if prefix:
243
+ obs = {f"{prefix}{k}": v for k, v in obs.items()}
244
+ return obs
@@ -0,0 +1,50 @@
1
+ # -------------------------------------------------------------------------
2
+ # Copyright (c) Microsoft Corporation. All rights reserved.
3
+ # Licensed under the MIT License.
4
+ # --------------------------------------------------------------------------
5
+ import torch
6
+ from torch.onnx import ExportOptions
7
+ from torch.onnx import _OrtBackend as OrtBackend
8
+ from torch.onnx import _OrtBackendOptions as OrtBackendOptions
9
+
10
+
11
+ def make_aot_ort(dynamic: bool = False):
12
+ """Implements an autograd backend for torch.compile based on onnxrt backend."""
13
+ export_options = ExportOptions(dynamic_shapes=dynamic)
14
+ options = OrtBackendOptions(export_options=export_options)
15
+ ort_backend = OrtBackend(options=options)
16
+ return ort_backend
17
+
18
+
19
+ def train_loop(model, *args, loss_fn=None, optimizer=None):
20
+ """Implements a training loop to be used in tests."""
21
+
22
+ if loss_fn is None:
23
+ loss_fn = torch.nn.MSELoss()
24
+ if optimizer is None:
25
+ optimizer = torch.optim.SGD(model.parameters(), lr=1e-3)
26
+
27
+ # Set the model to training mode - important for batch normalization and dropout layers
28
+ # Unnecessary in this situation but added for best practices
29
+ model.train()
30
+
31
+ # Compute prediction and loss
32
+ pred = model(*args)
33
+ if isinstance(pred, tuple):
34
+ v = pred[0]
35
+ elif hasattr(pred, "last_hidden_state"):
36
+ v = pred.last_hidden_state
37
+ else:
38
+ v = pred
39
+ loss = loss_fn(v, torch.ones_like(v))
40
+
41
+ # Backpropagation
42
+ loss.backward()
43
+ optimizer.step()
44
+ # skip that part to retrieve the gradients
45
+ # optimizer.zero_grad()
46
+
47
+ # returns the gradients
48
+ res = tuple(p.grad for p in model.parameters() if p.grad is not None)
49
+ assert len(res) > 0, f"No gradient, loss is {loss}"
50
+ return res
@@ -0,0 +1,190 @@
1
+ # -------------------------------------------------------------------------
2
+ # Copyright (c) Microsoft Corporation. All rights reserved.
3
+ # Licensed under the MIT License.
4
+ # --------------------------------------------------------------------------
5
+ # pylint: disable=import-outside-toplevel
6
+ from __future__ import annotations
7
+
8
+ import random
9
+ from typing import Any, Sequence
10
+
11
+ import onnx
12
+ import onnx.inliner
13
+ import torch
14
+
15
+ import onnxscript.optimizer
16
+ import onnxscript.rewriter
17
+
18
+
19
+ def export_to_onnx(
20
+ model: Any,
21
+ *args: Sequence[Any],
22
+ optimize: bool = True,
23
+ export_api: bool = True,
24
+ no_grad: bool = False,
25
+ ) -> onnx.ModelProto:
26
+ """
27
+ Export a model to ONNX.
28
+ If optimize is True, it calls *onnxscript.optimizer.optimize*,
29
+ *onnxscript.rewriter.rewriter*, *onnx.inliner.inline_local_functions*.
30
+ If *export_api* is True, the function uses ``torch.onnx.export``
31
+ and not ``torch.onnx.dynamo_export``.
32
+ """
33
+ if no_grad:
34
+ with torch.no_grad():
35
+ if export_api:
36
+ prog = torch.onnx.export(model, args, dynamo=True) # pylint: disable=no-value-for-parameter
37
+ else:
38
+ prog = torch.onnx.dynamo_export(model, *args)
39
+ else:
40
+ if export_api:
41
+ prog = torch.onnx.export(model, args, dynamo=True) # pylint: disable=no-value-for-parameter
42
+ else:
43
+ prog = torch.onnx.dynamo_export(model, *args)
44
+ assert prog is not None
45
+ model_proto = prog.model_proto
46
+ if optimize:
47
+ model_proto = onnxscript.optimizer.optimize(
48
+ model_proto,
49
+ num_iterations=2,
50
+ onnx_shape_inference=True,
51
+ )
52
+ model_proto = onnxscript.rewriter.rewrite(model_proto)
53
+ model_proto = onnx.inliner.inline_local_functions(model_proto)
54
+ return model_proto
55
+
56
+
57
+ def ids_tensor(
58
+ shape: Sequence[int],
59
+ vocab_size: int,
60
+ rng: random.Random | None = None,
61
+ name: str | None = None,
62
+ ):
63
+ """Creates a random int32 tensor of the shape within the vocab size."""
64
+ del name # unused
65
+
66
+ if rng is None:
67
+ rng = random.Random()
68
+
69
+ total_dims = 1
70
+ for dim in shape:
71
+ total_dims *= dim
72
+
73
+ values = []
74
+ for _ in range(total_dims):
75
+ values.append(rng.randint(0, vocab_size - 1))
76
+
77
+ return torch.tensor(data=values, dtype=torch.long).view(shape).contiguous()
78
+
79
+
80
+ def get_input_dims_for_llm(
81
+ dynamic_shapes: bool, warmup: int, repeat: int
82
+ ) -> list[tuple[int, int]]:
83
+ """Returns input dimensions for model such as llama, phi, ..."""
84
+ if not dynamic_shapes:
85
+ return [(2, 1024)] * (warmup + repeat)
86
+ w = [(2, 1024), (3, 1024), (2, 1096)] * warmup
87
+ w = w[:warmup]
88
+ r = [(2, 1024), (3, 1024), (4, 1024), (2, 1096), (2, 1112)] * repeat
89
+ r = r[:repeat]
90
+ return w + r
91
+
92
+
93
+ def get_model_and_inputs(
94
+ model: str,
95
+ config: str,
96
+ dynamic_shapes: bool,
97
+ device: str = "cpu",
98
+ num_hidden_layers: int = 1,
99
+ with_mask: bool = True,
100
+ implementation: str = "eager",
101
+ dtype: str | None = None,
102
+ warmup: int = 5,
103
+ repeat: int = 10,
104
+ ) -> tuple[Any, list[tuple[torch.Tensor, ...]], dict | None]:
105
+ """
106
+ Returns a model and a couple of dummy inputs.
107
+
108
+ Args:
109
+ model: model name, 'phi', 'llama', 'phi3', ...
110
+ config: 'small', 'medium', 'large', ...
111
+ dynamic_shapes: dynamic or static shapes
112
+ device: 'cpu' or 'cuda'
113
+ num_hidden_layers: Number of hidden layers.
114
+ with_mask: One input or two inputs.
115
+ implementation: eager or sdpa
116
+ warmup: Number of inputs to generate.
117
+ repeat: Number of inputs to generate for repeat.
118
+ dtype: If specified, cast the model and the inputs into this type.
119
+
120
+ Returns:
121
+ model and list of inputs
122
+ """
123
+ if model == "llama":
124
+ import onnxscript.tools.transformers_models.llama as m_llama
125
+
126
+ tmodel, inputs, dynamic_shapes_def = m_llama.get_llama_model_from_config(
127
+ warmup=warmup,
128
+ repeat=repeat,
129
+ implementation=implementation,
130
+ with_mask=with_mask,
131
+ num_hidden_layers=num_hidden_layers,
132
+ dynamic_shapes=dynamic_shapes,
133
+ config=config,
134
+ )
135
+
136
+ elif model == "mistral":
137
+ import onnxscript.tools.transformers_models.mistral as m_mistral
138
+
139
+ tmodel, inputs, dynamic_shapes_def = m_mistral.get_mistral_model_from_config(
140
+ warmup=warmup,
141
+ repeat=repeat,
142
+ implementation=implementation,
143
+ with_mask=with_mask,
144
+ num_hidden_layers=num_hidden_layers,
145
+ dynamic_shapes=dynamic_shapes,
146
+ config=config,
147
+ )
148
+
149
+ elif model == "phi":
150
+ import onnxscript.tools.transformers_models.phi as m_phi
151
+
152
+ tmodel, inputs, dynamic_shapes_def = m_phi.get_phi_model_from_config(
153
+ warmup=warmup,
154
+ repeat=repeat,
155
+ implementation=implementation,
156
+ with_mask=with_mask,
157
+ num_hidden_layers=num_hidden_layers,
158
+ dynamic_shapes=dynamic_shapes,
159
+ config=config,
160
+ )
161
+
162
+ elif model == "phi3":
163
+ import onnxscript.tools.transformers_models.phi3 as m_phi3
164
+
165
+ tmodel, inputs, dynamic_shapes_def = m_phi3.get_phi3_model_from_config(
166
+ warmup=warmup,
167
+ repeat=repeat,
168
+ implementation=implementation,
169
+ with_mask=with_mask,
170
+ num_hidden_layers=num_hidden_layers,
171
+ dynamic_shapes=dynamic_shapes,
172
+ config=config,
173
+ )
174
+
175
+ else:
176
+ raise ValueError(f"Model {model!r} is unknown.")
177
+
178
+ if dtype is not None:
179
+ dt = getattr(torch, dtype)
180
+ tmodel = tmodel.to(dt)
181
+ inputs = [
182
+ tuple((i if i.dtype in {torch.int64, torch.int32} else i.to(dt)) for i in inp)
183
+ for inp in inputs
184
+ ]
185
+
186
+ if device == "cuda":
187
+ tmodel = tmodel.to("cuda")
188
+ inputs = [tuple(i.to("cuda") for i in inp) for inp in inputs]
189
+
190
+ return tmodel, inputs, dynamic_shapes_def
@@ -0,0 +1,168 @@
1
+ # -------------------------------------------------------------------------
2
+ # Copyright (c) Microsoft Corporation. All rights reserved.
3
+ # Licensed under the MIT License.
4
+ # --------------------------------------------------------------------------
5
+ # pylint: disable=import-outside-toplevel
6
+ from __future__ import annotations
7
+
8
+ from typing import Any, Sequence
9
+
10
+ import torch
11
+
12
+ import onnxscript.tools.transformers_models
13
+
14
+
15
+ def get_llama_model(
16
+ input_dims: Sequence[tuple[int, int]] = ((2, 8), (4, 7), (9, 15)),
17
+ hidden_size: int = 16,
18
+ num_hidden_layers: int = 1,
19
+ vocab_size: int = 1024,
20
+ intermediate_size: int = 16,
21
+ max_position_embeddings: int = 1024,
22
+ num_attention_heads: int = 2,
23
+ _attn_implementation: str = "eager", # needed value to remove graph breaks
24
+ with_mask: bool = True,
25
+ ) -> tuple[Any, list[tuple[torch.Tensor, ...]], dict]:
26
+ """
27
+ Returns a model.
28
+ See `LlamaConfig
29
+ <https://huggingface.co/docs/transformers/main/en/model_doc/llama#transformers.LlamaConfig>`_.
30
+ The parameters are chosen for a unit test configuration.
31
+ """
32
+ from transformers import LlamaConfig
33
+ from transformers.models.llama.modeling_llama import LlamaModel
34
+
35
+ dynamic_shapes = {0: {0: "batch", 1: "length"}}
36
+ if with_mask:
37
+ dynamic_shapes.update({1: {0: "batch", 1: "length"}})
38
+
39
+ config = LlamaConfig(
40
+ num_hidden_layers=num_hidden_layers,
41
+ vocab_size=vocab_size,
42
+ hidden_size=hidden_size,
43
+ intermediate_size=intermediate_size,
44
+ max_position_embeddings=max_position_embeddings,
45
+ num_attention_heads=num_attention_heads,
46
+ )
47
+ if _attn_implementation:
48
+ config._attn_implementation = _attn_implementation # pylint: disable=protected-access
49
+
50
+ if with_mask:
51
+
52
+ class LlamaModelWrapperMask(torch.nn.Module):
53
+ def __init__(self, config):
54
+ super().__init__()
55
+ self.model = LlamaModel(config)
56
+
57
+ def forward(self, input_ids, attention_mask):
58
+ model_output = self.model(
59
+ input_ids, attention_mask=attention_mask, use_cache=False
60
+ )
61
+ return model_output.to_tuple()
62
+
63
+ def generate_example_inputs_mask(batch: int, seq: int, vocab_size: int):
64
+ input_ids = onnxscript.tools.transformers_models.ids_tensor(
65
+ [batch, seq], vocab_size
66
+ )
67
+ input_mask = torch.tril(torch.ones(batch, seq, dtype=torch.float32))
68
+ assert input_mask.dtype == torch.float32
69
+ return input_ids, input_mask
70
+
71
+ example_args_collection = []
72
+ for b, s in input_dims:
73
+ example_args_collection.append(generate_example_inputs_mask(b, s, vocab_size))
74
+
75
+ return LlamaModelWrapperMask(config), example_args_collection, dynamic_shapes
76
+
77
+ # no mask
78
+
79
+ class LlamaModelWrapper(torch.nn.Module):
80
+ def __init__(self, config):
81
+ super().__init__()
82
+ self.model = LlamaModel(config)
83
+
84
+ def forward(self, input_ids):
85
+ model_output = self.model(input_ids, use_cache=False)
86
+ return model_output.to_tuple()
87
+
88
+ def generate_example_inputs(batch: int, seq: int, vocab_size: int):
89
+ input_ids = onnxscript.tools.transformers_models.ids_tensor([batch, seq], vocab_size)
90
+ return (input_ids,)
91
+
92
+ example_args_collection = []
93
+ for b, s in input_dims:
94
+ example_args_collection.append(generate_example_inputs(b, s, vocab_size))
95
+
96
+ return LlamaModelWrapper(config), example_args_collection, dynamic_shapes
97
+
98
+
99
+ def get_llama_model_from_config(
100
+ warmup: int = 5,
101
+ repeat: int = 10,
102
+ config: str = "small",
103
+ num_hidden_layers: int = 1,
104
+ implementation: str = "eager",
105
+ dynamic_shapes: bool = False,
106
+ with_mask: bool = True,
107
+ ) -> tuple[Any, list[tuple[torch.Tensor, ...]], dict]:
108
+ """
109
+ Returns a model Phi to test or benchmark.
110
+
111
+ Args:
112
+ warmup: Number of inputs to generate.
113
+ repeat: Number of inputs to generate for repeat.
114
+ config: small, medium or large
115
+ num_hidden_layers: Number of hidden layers.
116
+ implementation: eager or sdpa
117
+ with_mask: One or two inputs.
118
+ dynamic_shapes: dynamic shapes or not
119
+
120
+ Returns:
121
+ Model and list of inputs.
122
+ """
123
+ if config == "small":
124
+ conf_dict = dict(
125
+ input_dims=onnxscript.tools.transformers_models.get_input_dims_for_llm(
126
+ dynamic_shapes, warmup, repeat
127
+ ),
128
+ hidden_size=16,
129
+ num_hidden_layers=num_hidden_layers,
130
+ vocab_size=1024,
131
+ intermediate_size=16,
132
+ max_position_embeddings=1024,
133
+ num_attention_heads=2,
134
+ _attn_implementation=implementation,
135
+ with_mask=with_mask,
136
+ )
137
+ elif config == "medium":
138
+ conf_dict = dict(
139
+ input_dims=onnxscript.tools.transformers_models.get_input_dims_for_llm(
140
+ dynamic_shapes, warmup, repeat
141
+ ),
142
+ hidden_size=1024,
143
+ num_hidden_layers=num_hidden_layers,
144
+ vocab_size=1024,
145
+ intermediate_size=1024,
146
+ max_position_embeddings=1024,
147
+ num_attention_heads=2,
148
+ _attn_implementation=implementation,
149
+ with_mask=with_mask,
150
+ )
151
+ elif config in ("large", "default"):
152
+ conf_dict = dict(
153
+ input_dims=onnxscript.tools.transformers_models.get_input_dims_for_llm(
154
+ dynamic_shapes, warmup, repeat
155
+ ),
156
+ hidden_size=4096,
157
+ num_hidden_layers=num_hidden_layers,
158
+ vocab_size=32000,
159
+ intermediate_size=11008,
160
+ max_position_embeddings=2048,
161
+ num_attention_heads=32,
162
+ _attn_implementation=implementation,
163
+ with_mask=with_mask,
164
+ )
165
+ else:
166
+ raise ValueError(f"Unexpected configuration {config!r}.")
167
+
168
+ return get_llama_model(**conf_dict) # type: ignore[arg-type]