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.
- onnxscript/__init__.py +131 -0
- onnxscript/_framework_apis/__init__.py +3 -0
- onnxscript/_framework_apis/torch_2_5.py +117 -0
- onnxscript/_framework_apis/torch_2_6.py +45 -0
- onnxscript/_internal/__init__.py +0 -0
- onnxscript/_internal/analysis.py +229 -0
- onnxscript/_internal/ast_utils.py +64 -0
- onnxscript/_internal/autocast.py +250 -0
- onnxscript/_internal/deprecation.py +78 -0
- onnxscript/_internal/param_manipulation.py +148 -0
- onnxscript/_internal/runtime_typing.py +43 -0
- onnxscript/_internal/utils.py +99 -0
- onnxscript/_internal/version_utils.py +118 -0
- onnxscript/_legacy_ir/__init__.py +341 -0
- onnxscript/_legacy_ir/visitor.py +937 -0
- onnxscript/_thirdparty/asciichartpy.py +313 -0
- onnxscript/backend/__init__.py +2 -0
- onnxscript/backend/onnx_backend.py +303 -0
- onnxscript/backend/onnx_export.py +885 -0
- onnxscript/converter.py +1470 -0
- onnxscript/evaluator.py +619 -0
- onnxscript/function_libs/tools/torch_lib/deduce_type_constraints.py +403 -0
- onnxscript/function_libs/tools/torch_lib/generate_aten_signatures.py +333 -0
- onnxscript/function_libs/tools/torch_lib/generate_prims_signatures.py +331 -0
- onnxscript/function_libs/torch_lib/__init__.py +12 -0
- onnxscript/function_libs/torch_lib/_constants.py +5 -0
- onnxscript/function_libs/torch_lib/_flags.py +58 -0
- onnxscript/function_libs/torch_lib/graph_building/__init__.py +56 -0
- onnxscript/function_libs/torch_lib/graph_building/_graph_building_ir.py +723 -0
- onnxscript/function_libs/torch_lib/graph_building/_graph_building_torch.py +1125 -0
- onnxscript/function_libs/torch_lib/ops/__init__.py +27 -0
- onnxscript/function_libs/torch_lib/ops/common.py +80 -0
- onnxscript/function_libs/torch_lib/ops/core.py +8935 -0
- onnxscript/function_libs/torch_lib/ops/fft.py +385 -0
- onnxscript/function_libs/torch_lib/ops/linalg.py +399 -0
- onnxscript/function_libs/torch_lib/ops/nested.py +25 -0
- onnxscript/function_libs/torch_lib/ops/nn.py +2713 -0
- onnxscript/function_libs/torch_lib/ops/prims.py +850 -0
- onnxscript/function_libs/torch_lib/ops/quantized_decomposed.py +63 -0
- onnxscript/function_libs/torch_lib/ops/sparse.py +23 -0
- onnxscript/function_libs/torch_lib/ops/special.py +387 -0
- onnxscript/function_libs/torch_lib/ops/vision.py +25 -0
- onnxscript/function_libs/torch_lib/registration.py +151 -0
- onnxscript/function_libs/torch_lib/tensor_typing.py +74 -0
- onnxscript/ir/__init__.py +153 -0
- onnxscript/ir/_convenience.py +447 -0
- onnxscript/ir/_core.py +3119 -0
- onnxscript/ir/_display.py +49 -0
- onnxscript/ir/_enums.py +163 -0
- onnxscript/ir/_graph_comparison.py +23 -0
- onnxscript/ir/_io.py +97 -0
- onnxscript/ir/_linked_list.py +276 -0
- onnxscript/ir/_metadata.py +44 -0
- onnxscript/ir/_name_authority.py +72 -0
- onnxscript/ir/_polyfill.py +25 -0
- onnxscript/ir/_protocols.py +598 -0
- onnxscript/ir/_schemas.py +548 -0
- onnxscript/ir/_tape.py +120 -0
- onnxscript/ir/_type_casting.py +106 -0
- onnxscript/ir/convenience.py +32 -0
- onnxscript/ir/external_data.py +396 -0
- onnxscript/ir/passes/__init__.py +33 -0
- onnxscript/ir/passes/_pass_infra.py +172 -0
- onnxscript/ir/serde.py +1620 -0
- onnxscript/ir/tensor_adapters.py +122 -0
- onnxscript/ir/traversal.py +82 -0
- onnxscript/irbuilder.py +542 -0
- onnxscript/main.py +167 -0
- onnxscript/onnx_opset/__init__.py +232 -0
- onnxscript/onnx_opset/_impl/opset1.py +4100 -0
- onnxscript/onnx_opset/_impl/opset10.py +1227 -0
- onnxscript/onnx_opset/_impl/opset11.py +4013 -0
- onnxscript/onnx_opset/_impl/opset12.py +1078 -0
- onnxscript/onnx_opset/_impl/opset13.py +3924 -0
- onnxscript/onnx_opset/_impl/opset14.py +999 -0
- onnxscript/onnx_opset/_impl/opset15.py +604 -0
- onnxscript/onnx_opset/_impl/opset16.py +1255 -0
- onnxscript/onnx_opset/_impl/opset17.py +561 -0
- onnxscript/onnx_opset/_impl/opset18.py +1803 -0
- onnxscript/onnx_opset/_impl/opset19.py +1942 -0
- onnxscript/onnx_opset/_impl/opset2.py +218 -0
- onnxscript/onnx_opset/_impl/opset20.py +675 -0
- onnxscript/onnx_opset/_impl/opset21.py +1976 -0
- onnxscript/onnx_opset/_impl/opset22.py +2588 -0
- onnxscript/onnx_opset/_impl/opset3.py +199 -0
- onnxscript/onnx_opset/_impl/opset4.py +77 -0
- onnxscript/onnx_opset/_impl/opset5.py +84 -0
- onnxscript/onnx_opset/_impl/opset6.py +944 -0
- onnxscript/onnx_opset/_impl/opset7.py +1243 -0
- onnxscript/onnx_opset/_impl/opset8.py +444 -0
- onnxscript/onnx_opset/_impl/opset9.py +1485 -0
- onnxscript/onnx_opset/_impl/opset_ai_onnx_ml1.py +974 -0
- onnxscript/onnx_opset/_impl/opset_ai_onnx_ml2.py +112 -0
- onnxscript/onnx_opset/_impl/opset_ai_onnx_ml3.py +308 -0
- onnxscript/onnx_opset/_impl/opset_ai_onnx_ml4.py +129 -0
- onnxscript/onnx_opset/_impl/opset_ai_onnx_ml5.py +158 -0
- onnxscript/onnx_opset/_impl/opset_ai_onnx_preview_training1.py +577 -0
- onnxscript/onnx_types.py +229 -0
- onnxscript/optimizer/__init__.py +39 -0
- onnxscript/optimizer/_constant_folding.py +1083 -0
- onnxscript/optimizer/_inliner.py +312 -0
- onnxscript/optimizer/_legacy/_optimizer.py +98 -0
- onnxscript/optimizer/_legacy/_remove_unused_proto.py +144 -0
- onnxscript/optimizer/_legacy/_simple_function_folding.py +243 -0
- onnxscript/optimizer/_legacy/constant_folding.py +293 -0
- onnxscript/optimizer/_legacy/evaluator.py +439 -0
- onnxscript/optimizer/_optimizer.py +61 -0
- onnxscript/optimizer/_remove_unused.py +106 -0
- onnxscript/optimizer/_remove_unused_function.py +72 -0
- onnxscript/py.typed +1 -0
- onnxscript/rewriter/__init__.py +56 -0
- onnxscript/rewriter/_ir_utils.py +111 -0
- onnxscript/rewriter/broadcast_to_matmul.py +180 -0
- onnxscript/rewriter/cast_constant_of_shape.py +50 -0
- onnxscript/rewriter/collapse_slices.py +141 -0
- onnxscript/rewriter/erfgelu.py +27 -0
- onnxscript/rewriter/function_rule.py +232 -0
- onnxscript/rewriter/gemm_to_matmul_add.py +21 -0
- onnxscript/rewriter/generic_pattern.py +700 -0
- onnxscript/rewriter/llama_rule_sets.py +287 -0
- onnxscript/rewriter/no_op.py +56 -0
- onnxscript/rewriter/onnxruntime/__init__.py +50 -0
- onnxscript/rewriter/onnxruntime/bfloat16_utils/bfloat16_converter.py +99 -0
- onnxscript/rewriter/onnxruntime/fused_matmul_rule_sets.py +177 -0
- onnxscript/rewriter/onnxruntime/group_normalization_merge_silu.py +64 -0
- onnxscript/rewriter/onnxruntime/instance_to_group_normalization.py +155 -0
- onnxscript/rewriter/onnxruntime/softmax.py +63 -0
- onnxscript/rewriter/onnxruntime/transformers/__init__.py +21 -0
- onnxscript/rewriter/onnxruntime/transformers/biassplitgelu.py +31 -0
- onnxscript/rewriter/onnxruntime/transformers/fastgelu.py +29 -0
- onnxscript/rewriter/onnxruntime/transformers/layernorm.py +47 -0
- onnxscript/rewriter/onnxruntime/transformers/multihead_attention.py +715 -0
- onnxscript/rewriter/ort_fusions/__init__.py +9 -0
- onnxscript/rewriter/ort_fusions/_core.py +28 -0
- onnxscript/rewriter/ort_fusions/_smollm_1.py +253 -0
- onnxscript/rewriter/ort_fusions/_smollm_2.py +467 -0
- onnxscript/rewriter/ort_fusions/_test_models.py +122 -0
- onnxscript/rewriter/ort_fusions/_test_utils.py +42 -0
- onnxscript/rewriter/ort_fusions/cos_sin_cache.py +154 -0
- onnxscript/rewriter/ort_fusions/gqa.py +156 -0
- onnxscript/rewriter/ort_fusions/mha.py +198 -0
- onnxscript/rewriter/ort_fusions/rms_normalization.py +95 -0
- onnxscript/rewriter/ort_fusions/rotary_embedding.py +64 -0
- onnxscript/rewriter/ort_fusions/sdpa.py +75 -0
- onnxscript/rewriter/ort_fusions/skip_normalization.py +46 -0
- onnxscript/rewriter/pattern.py +1714 -0
- onnxscript/rewriter/testing.py +77 -0
- onnxscript/sourceinfo.py +59 -0
- onnxscript/tensor.py +227 -0
- onnxscript/testing/__init__.py +482 -0
- onnxscript/tools/__init__.py +4 -0
- onnxscript/tools/benchmark/__init__.py +23 -0
- onnxscript/tools/benchmark/benchmark_helpers.py +783 -0
- onnxscript/tools/benchmark/benchmark_run.py +140 -0
- onnxscript/tools/benchmark/export_model.py +207 -0
- onnxscript/tools/benchmark/export_model_batch.py +146 -0
- onnxscript/tools/memory_peak.py +244 -0
- onnxscript/tools/training_helper.py +50 -0
- onnxscript/tools/transformers_models/__init__.py +190 -0
- onnxscript/tools/transformers_models/llama.py +168 -0
- onnxscript/tools/transformers_models/mistral.py +238 -0
- onnxscript/tools/transformers_models/phi.py +248 -0
- onnxscript/tools/transformers_models/phi3.py +259 -0
- onnxscript/type_annotation.py +281 -0
- onnxscript/utils/__init__.py +0 -0
- onnxscript/utils/evaluation_utils.py +56 -0
- onnxscript/utils/timing_utils.py +33 -0
- onnxscript/utils/utils.py +84 -0
- onnxscript/values.py +790 -0
- onnxscript/version_converter/__init__.py +21 -0
- onnxscript/version_converter/_version_converter.py +314 -0
- onnxscript-0.1.0.dist-info/LICENSE +21 -0
- onnxscript-0.1.0.dist-info/METADATA +370 -0
- onnxscript-0.1.0.dist-info/RECORD +176 -0
- onnxscript-0.1.0.dist-info/WHEEL +5 -0
- 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]
|