hyperonnx 1.0.2__tar.gz → 1.0.4__tar.gz
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.
- hyperonnx-1.0.4/.claude/CLAUDE.md +27 -0
- hyperonnx-1.0.4/.claude/skills/torch-dynamo-onnx-export/SKILL.md +422 -0
- hyperonnx-1.0.4/.claude/skills/torch-legacy-onnx-export/SKILL.md +407 -0
- {hyperonnx-1.0.2 → hyperonnx-1.0.4}/.github/workflows/lint.yml +8 -1
- {hyperonnx-1.0.2 → hyperonnx-1.0.4}/.github/workflows/unittest.yml +9 -2
- {hyperonnx-1.0.2 → hyperonnx-1.0.4}/.gitignore +7 -1
- {hyperonnx-1.0.2 → hyperonnx-1.0.4}/.pre-commit-config.yaml +2 -2
- {hyperonnx-1.0.2 → hyperonnx-1.0.4}/PKG-INFO +3 -3
- {hyperonnx-1.0.2 → hyperonnx-1.0.4}/hyperonnx/__init__.py +1 -1
- {hyperonnx-1.0.2 → hyperonnx-1.0.4}/hyperonnx/hyper_export.py +16 -1
- {hyperonnx-1.0.2 → hyperonnx-1.0.4}/hyperonnx/torch_export.py +3 -0
- {hyperonnx-1.0.2 → hyperonnx-1.0.4}/hyperonnx/transformers/__init__.py +6 -0
- {hyperonnx-1.0.2 → hyperonnx-1.0.4}/hyperonnx/transformers/cache.py +59 -6
- hyperonnx-1.0.4/hyperonnx/transformers/mamba.py +179 -0
- {hyperonnx-1.0.2 → hyperonnx-1.0.4}/hyperonnx/transformers/patch.py +30 -1
- hyperonnx-1.0.4/hyperonnx/transformers/recurrent.py +367 -0
- hyperonnx-1.0.4/hyperonnx/utils.py +58 -0
- {hyperonnx-1.0.2 → hyperonnx-1.0.4}/pyproject.toml +6 -6
- {hyperonnx-1.0.2 → hyperonnx-1.0.4}/tests/test_export_hyper_onnx.py +7 -3
- hyperonnx-1.0.4/tests/transformers/test_mamba.py +206 -0
- hyperonnx-1.0.4/tests/transformers/test_recurrent.py +69 -0
- hyperonnx-1.0.2/hyperonnx/utils.py +0 -27
- {hyperonnx-1.0.2 → hyperonnx-1.0.4}/.github/words_bag.txt +0 -0
- {hyperonnx-1.0.2 → hyperonnx-1.0.4}/LICENSE +0 -0
- {hyperonnx-1.0.2 → hyperonnx-1.0.4}/README.md +0 -0
- {hyperonnx-1.0.2 → hyperonnx-1.0.4}/README_CN.md +0 -0
- {hyperonnx-1.0.2 → hyperonnx-1.0.4}/docs/assets/qwen2_omni_vision.gif +0 -0
- {hyperonnx-1.0.2 → hyperonnx-1.0.4}/docs/assets/r18-sample.gif +0 -0
- {hyperonnx-1.0.2 → hyperonnx-1.0.4}/hyperonnx/auto.py +0 -0
- {hyperonnx-1.0.2 → hyperonnx-1.0.4}/hyperonnx/exporter/__init__.py +0 -0
- {hyperonnx-1.0.2 → hyperonnx-1.0.4}/hyperonnx/exporter/dynamo.py +0 -0
- {hyperonnx-1.0.2 → hyperonnx-1.0.4}/hyperonnx/exporter/torchscript.py +0 -0
- {hyperonnx-1.0.2 → hyperonnx-1.0.4}/hyperonnx/exporter/utils.py +0 -0
- {hyperonnx-1.0.2 → hyperonnx-1.0.4}/hyperonnx/function_rewriter.py +0 -0
- {hyperonnx-1.0.2 → hyperonnx-1.0.4}/hyperonnx/transformers/attention.py +0 -0
- {hyperonnx-1.0.2 → hyperonnx-1.0.4}/hyperonnx/typing.py +0 -0
- {hyperonnx-1.0.2 → hyperonnx-1.0.4}/tests/expoter/test_detach_module_outputs.py +0 -0
- {hyperonnx-1.0.2 → hyperonnx-1.0.4}/tests/expoter/test_dynamo_build_onnxscript.py +0 -0
- {hyperonnx-1.0.2 → hyperonnx-1.0.4}/tests/expoter/test_dynamo_make_custom_op.py +0 -0
- {hyperonnx-1.0.2 → hyperonnx-1.0.4}/tests/expoter/test_dynamo_replace_custom_op.py +0 -0
- {hyperonnx-1.0.2 → hyperonnx-1.0.4}/tests/expoter/test_non_pod_export.py +0 -0
- {hyperonnx-1.0.2 → hyperonnx-1.0.4}/tests/expoter/test_plain_output_tensors.py +0 -0
- {hyperonnx-1.0.2 → hyperonnx-1.0.4}/tests/test_auto_trace_method.py +0 -0
- {hyperonnx-1.0.2 → hyperonnx-1.0.4}/tests/test_compose_nodes_to_functions.py +0 -0
- {hyperonnx-1.0.2 → hyperonnx-1.0.4}/tests/test_export_functions.py +0 -0
- {hyperonnx-1.0.2 → hyperonnx-1.0.4}/tests/test_fuse_constants_to_function.py +0 -0
- {hyperonnx-1.0.2 → hyperonnx-1.0.4}/tests/transformers/test_attention.py +0 -0
- {hyperonnx-1.0.2 → hyperonnx-1.0.4}/tests/transformers/test_static_cache.py +0 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# HyperONNX Instructions
|
|
2
|
+
|
|
3
|
+
## Workflow
|
|
4
|
+
|
|
5
|
+
- Check relevant skills in `.claude/skills/` before handling specialized tasks.
|
|
6
|
+
- Prefer minimal, targeted changes that preserve existing project structure and style.
|
|
7
|
+
- Validate the files you change with the smallest relevant command before finishing.
|
|
8
|
+
|
|
9
|
+
## Python Changes
|
|
10
|
+
|
|
11
|
+
- Follow the existing Python style already used in `hyperonnx/`.
|
|
12
|
+
- When Python files are modified, use the lint skill workflow and prefer `uv run ruff check hyperonnx`.
|
|
13
|
+
- Format Python changes with `uv run ruff format hyperonnx` when formatting is needed.
|
|
14
|
+
|
|
15
|
+
## Version Compatibility (Hard Constraints)
|
|
16
|
+
|
|
17
|
+
⚠️ **Supported Versions**: This project is currently tested and working on:
|
|
18
|
+
- **torch < 2.11** (torch 2.5–2.10 verified)
|
|
19
|
+
- **onnxscript < 0.6.0**
|
|
20
|
+
|
|
21
|
+
Both torch >= 2.11 and onnxscript >= 0.6.0 introduce breaking changes to module structure, dtype handling, and ONNX IR that are not yet resolved. **Do not use these newer versions** without explicit future work to support them.
|
|
22
|
+
|
|
23
|
+
**When users ask about newer versions**: Acknowledge the constraint, suggest using compatible versions from `pyproject.toml` optional dependencies, and note that torch >= 2.11 support is a known gap requiring future implementation.
|
|
24
|
+
|
|
25
|
+
## Commits
|
|
26
|
+
|
|
27
|
+
- If asked to create a commit, use a clear and concise commit message that describes the actual change.
|
|
@@ -0,0 +1,422 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: torch-dynamo-onnx-export
|
|
3
|
+
description: Use this skill when exporting PyTorch models to ONNX using the modern torch.export (dynamo) path in HyperONNX. Best for models with dynamic control flow, complex tensor operations, and hierarchical custom operators defined via onnxscript.
|
|
4
|
+
argument-hint: Describe the model, custom operations (if any), target opset, and whether you need hierarchical export or simple custom operator definitions.
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Torch Dynamo ONNX Export
|
|
8
|
+
|
|
9
|
+
Use this skill when the task is about the modern Torch ONNX export path in HyperONNX, meaning the `torch.export`-based path with `dynamo=True`.
|
|
10
|
+
|
|
11
|
+
This skill is specific to this repository. It captures the actual workflow used by HyperONNX code and tests, rather than generic PyTorch ONNX advice.
|
|
12
|
+
|
|
13
|
+
## Torch Behaviour
|
|
14
|
+
|
|
15
|
+
- PyTorch 2.6+ introduced `torch.export` for AOT (Ahead-of-Time) tracing, enabling more dynamic models than TorchScript.
|
|
16
|
+
- PyTorch 2.9+ made `dynamo=True` the default in `torch.onnx.export()`.
|
|
17
|
+
- The dynamo path is based on FX graphs and bytecode analysis, not static tracing.
|
|
18
|
+
- On PyTorch 2.5 and below, `dynamo` parameter may not exist; always check `torch.onnx.export` signature and use the wrapper `torch_export_handle_lower_version` from HyperONNX.
|
|
19
|
+
- In this repository, prefer being explicit: always pass `dynamo=True` to clarify intent when using the modern exporter.
|
|
20
|
+
|
|
21
|
+
⚠️ **Version Constraint**: This skill is tested and supported on **torch < 2.11** with **onnxscript < 0.6.0**. These versions have breaking changes in ONNX/module structure that require future work to resolve.
|
|
22
|
+
|
|
23
|
+
## What This Skill Produces
|
|
24
|
+
|
|
25
|
+
Produce one of these outcomes:
|
|
26
|
+
|
|
27
|
+
- A correct export snippet using the modern `torch.export` path with `dynamo=True`.
|
|
28
|
+
- A HyperONNX code change that leverages `torch.export` for better dynamic model support.
|
|
29
|
+
- A custom operator definition using onnxscript and custom_translation_table for the dynamo path.
|
|
30
|
+
- A debugging checklist for why a dynamo export is failing or behaving unexpectedly.
|
|
31
|
+
- A migration guide from legacy (dynamo=False) to modern (dynamo=True) export.
|
|
32
|
+
|
|
33
|
+
## When To Use It
|
|
34
|
+
|
|
35
|
+
Use this skill when the user asks for any of the following:
|
|
36
|
+
|
|
37
|
+
- Use `torch.onnx.export` with the modern or dynamo path.
|
|
38
|
+
- Force torch.export instead of TorchScript tracing.
|
|
39
|
+
- Export a model with `dynamo=True`.
|
|
40
|
+
- Create custom ONNX operators using onnxscript for the dynamo path.
|
|
41
|
+
- Build a custom_translation_table for hierarchical export.
|
|
42
|
+
- Understand why dynamo export behaves differently from legacy export.
|
|
43
|
+
- Export models with dynamic shapes, loops, or data-dependent control flow.
|
|
44
|
+
|
|
45
|
+
Do not use this skill for generic ONNX export or torch.export advice unrelated to HyperONNX.
|
|
46
|
+
|
|
47
|
+
## Repository Facts To Ground Decisions
|
|
48
|
+
|
|
49
|
+
- HyperONNX exposes `torch_export_handle_lower_version` in [hyperonnx/torch_export.py](../../../hyperonnx/torch_export.py) to bridge version differences.
|
|
50
|
+
- The main hierarchical export entrypoint is [hyperonnx/hyper_export.py](../../../hyperonnx/hyper_export.py).
|
|
51
|
+
- The dynamo-specific branch for hierarchical replacement lives in [hyperonnx/exporter/dynamo.py](../../../hyperonnx/exporter/dynamo.py).
|
|
52
|
+
- Custom operator definition using onnxscript is built dynamically in [hyperonnx/exporter/dynamo.py](../../../hyperonnx/exporter/dynamo.py) via `build_onnxscript` and IRBuilder.
|
|
53
|
+
- Transformer attention for the dynamo path is handled in [hyperonnx/transformers/attention.py](../../../hyperonnx/transformers/attention.py) via `attention_translation_table()`.
|
|
54
|
+
- Reference tests: [tests/expoter/test_dynamo_replace_custom_op.py](../../../tests/expoter/test_dynamo_replace_custom_op.py), [tests/expoter/test_dynamo_build_onnxscript.py](../../../tests/expoter/test_dynamo_build_onnxscript.py), [tests/expoter/test_dynamo_make_custom_op.py](../../../tests/expoter/test_dynamo_make_custom_op.py).
|
|
55
|
+
|
|
56
|
+
## Dynamo Export Fundamentals
|
|
57
|
+
|
|
58
|
+
### Core Concepts
|
|
59
|
+
|
|
60
|
+
**torch.export**: Produces an ExportedProgram (AOT traced graph) that preserves more semantics than TorchScript, including data-dependent control flow and dynamic shapes.
|
|
61
|
+
|
|
62
|
+
**custom_translation_table**: A dictionary mapping PyTorch callables (e.g., custom ops, modules) to their ONNX counterparts (via onnxscript). The dynamo exporter uses this table to translate FX nodes to ONNX operators.
|
|
63
|
+
|
|
64
|
+
**onnxscript**: A framework for defining ONNX functions programmatically. HyperONNX uses `onnxscript.OnnxFunction` and `irbuilder.IRFunction` to build custom operators dynamically.
|
|
65
|
+
|
|
66
|
+
**replace_with_custom_op**: Context manager in [hyperonnx/exporter/dynamo.py](../../../hyperonnx/exporter/dynamo.py) that builds a custom_translation_table from module_spec, yielding it for use in export.
|
|
67
|
+
|
|
68
|
+
**build_onnxscript**: Dynamically constructs an ONNX function from a module's spec (inputs, outputs, dtype info). Used to create custom operator definitions without writing onnxscript manually.
|
|
69
|
+
|
|
70
|
+
### Key Differences from Legacy Export (TorchScript)
|
|
71
|
+
|
|
72
|
+
| Aspect | Legacy (dynamo=False) | Dynamo (dynamo=True) |
|
|
73
|
+
|--------|----------------------|---------------------|
|
|
74
|
+
| **Tracer** | TorchScript (static tracing) | torch.export (FX bytecode) |
|
|
75
|
+
| **Dynamic Shapes** | Limited | Better supported |
|
|
76
|
+
| **Control Flow** | Loops unrolled, if/else lost | Can be preserved (with torch.cond) |
|
|
77
|
+
| **Custom Op Registration** | torch.onnx.register_custom_op_symbolic | custom_translation_table + onnxscript |
|
|
78
|
+
| **Module Replacement** | DuckForward (torch.autograd.Function) | Direct FX node replacement |
|
|
79
|
+
| **Symbolic Gen** | Automatic from DuckForward.symbolic | Explicit via onnxscript |
|
|
80
|
+
| **Attention Registration** | register_attention_opsets() | attention_translation_table() |
|
|
81
|
+
| **Performance** | Often faster export | Slower export (bytecode analysis) |
|
|
82
|
+
|
|
83
|
+
## Defining Custom Operators for Dynamo Export
|
|
84
|
+
|
|
85
|
+
### Pattern 1: Direct onnxscript function (simple custom op)
|
|
86
|
+
|
|
87
|
+
For single custom operations without hierarchical wrapping:
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
import onnxscript
|
|
91
|
+
import torch
|
|
92
|
+
|
|
93
|
+
# Step 1: Define an onnxscript function
|
|
94
|
+
_MY_OPSET = onnxscript.values.Opset("custom_domain", 1)
|
|
95
|
+
|
|
96
|
+
@onnxscript.script(_MY_OPSET)
|
|
97
|
+
def my_custom_op(x: onnxscript.FLOAT) -> onnxscript.FLOAT:
|
|
98
|
+
return x * 2.0 + 1.0
|
|
99
|
+
|
|
100
|
+
# Step 2: Build custom_translation_table
|
|
101
|
+
custom_translation_table = {
|
|
102
|
+
torch.ops.custom_lib.my_op: my_custom_op,
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
# Step 3: Export with dynamo=True and the table
|
|
106
|
+
torch.onnx.export(
|
|
107
|
+
model,
|
|
108
|
+
example_inputs,
|
|
109
|
+
'model.onnx',
|
|
110
|
+
dynamo=True,
|
|
111
|
+
custom_translation_table=custom_translation_table,
|
|
112
|
+
opset_version=18,
|
|
113
|
+
)
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Key rules:
|
|
117
|
+
- The onnxscript function signature must match the PyTorch operation signature.
|
|
118
|
+
- The dictionary key must be a callable (e.g., torch.ops, a custom op).
|
|
119
|
+
- Always pass opset_version to align with onnxscript opset expectations.
|
|
120
|
+
|
|
121
|
+
### Pattern 2: HyperONNX hierarchical export with dynamic onnxscript
|
|
122
|
+
|
|
123
|
+
For complex modules that need hierarchical export, use `replace_with_custom_op` and `build_onnxscript`:
|
|
124
|
+
|
|
125
|
+
```python
|
|
126
|
+
import torch
|
|
127
|
+
from hyperonnx import export_hyper_onnx
|
|
128
|
+
|
|
129
|
+
class MyCustomModule(torch.nn.Module):
|
|
130
|
+
"""A complex module with loops or dynamic shapes."""
|
|
131
|
+
def forward(self, x):
|
|
132
|
+
for i in range(x.shape[0]):
|
|
133
|
+
x = x * 2
|
|
134
|
+
return x
|
|
135
|
+
|
|
136
|
+
model = torch.nn.Sequential(
|
|
137
|
+
torch.nn.Linear(10, 20),
|
|
138
|
+
MyCustomModule(),
|
|
139
|
+
torch.nn.Linear(20, 5),
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
export_hyper_onnx(
|
|
143
|
+
model,
|
|
144
|
+
(torch.randn(1, 10),),
|
|
145
|
+
'model.onnx',
|
|
146
|
+
hiera=[MyCustomModule], # Export as hierarchical function
|
|
147
|
+
dynamo=True, # Use torch.export, not TorchScript
|
|
148
|
+
opset_version=18,
|
|
149
|
+
)
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
How it works:
|
|
153
|
+
1. HyperONNX traces the model and caches module specs (inputs, outputs, dtypes).
|
|
154
|
+
2. `build_onnxscript` dynamically creates an ONNX function from the module spec.
|
|
155
|
+
3. `replace_with_custom_op` builds the custom_translation_table.
|
|
156
|
+
4. torch.onnx.export (with dynamo=True) translates the FX graph using the table.
|
|
157
|
+
|
|
158
|
+
Reference: [tests/expoter/test_dynamo_replace_custom_op.py](../../../tests/expoter/test_dynamo_replace_custom_op.py).
|
|
159
|
+
|
|
160
|
+
### Pattern 3: Attention-specific translation for dynamo
|
|
161
|
+
|
|
162
|
+
For transformer attention layers:
|
|
163
|
+
|
|
164
|
+
```python
|
|
165
|
+
import torch
|
|
166
|
+
from hyperonnx.transformers.attention import attention_translation_table
|
|
167
|
+
|
|
168
|
+
# Step 1: Get the translation table for dynamo path
|
|
169
|
+
translation_table = attention_translation_table()
|
|
170
|
+
|
|
171
|
+
# Step 2: Export with dynamo=True and the table
|
|
172
|
+
torch.onnx.export(
|
|
173
|
+
model,
|
|
174
|
+
example_inputs,
|
|
175
|
+
'model.onnx',
|
|
176
|
+
opset_version=24,
|
|
177
|
+
dynamo=True,
|
|
178
|
+
custom_translation_table=translation_table,
|
|
179
|
+
)
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
This uses the modern onnxscript-based Attention definition. See [hyperonnx/transformers/attention.py](../../../hyperonnx/transformers/attention.py) for implementation.
|
|
183
|
+
|
|
184
|
+
## Building Custom Operators with onnxscript
|
|
185
|
+
|
|
186
|
+
### Overview
|
|
187
|
+
|
|
188
|
+
HyperONNX's `build_onnxscript` function constructs ONNX operators automatically from module specs, eliminating the need to write onnxscript manually. However, you can also write custom onnxscript functions for fine-grained control.
|
|
189
|
+
|
|
190
|
+
### Auto-generation via build_onnxscript
|
|
191
|
+
|
|
192
|
+
```python
|
|
193
|
+
from hyperonnx.exporter.dynamo import build_onnxscript
|
|
194
|
+
from hyperonnx.typing import default_module_spec, ModuleSpec
|
|
195
|
+
|
|
196
|
+
# Assume module_spec is populated via trace_module_spec
|
|
197
|
+
spec: ModuleSpec = {...} # Contains: args, output, signature, etc.
|
|
198
|
+
|
|
199
|
+
# Auto-build onnxscript function
|
|
200
|
+
onnx_func = build_onnxscript(spec)
|
|
201
|
+
|
|
202
|
+
# Use in custom_translation_table
|
|
203
|
+
custom_translation_table = {
|
|
204
|
+
MyModule: onnx_func,
|
|
205
|
+
}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### Manual onnxscript Definition
|
|
209
|
+
|
|
210
|
+
For more control, write onnxscript directly:
|
|
211
|
+
|
|
212
|
+
```python
|
|
213
|
+
import onnxscript
|
|
214
|
+
|
|
215
|
+
# Define a custom opset
|
|
216
|
+
CUSTOM_OPSET = onnxscript.values.Opset(domain="my_domain", version=1)
|
|
217
|
+
|
|
218
|
+
@onnxscript.script(CUSTOM_OPSET)
|
|
219
|
+
def my_complex_op(x: onnxscript.FLOAT, mask: onnxscript.BOOL) -> onnxscript.FLOAT:
|
|
220
|
+
"""Custom operator that applies masking."""
|
|
221
|
+
return x * mask.cast(onnxscript.FLOAT)
|
|
222
|
+
|
|
223
|
+
# Use in translation table
|
|
224
|
+
custom_translation_table = {
|
|
225
|
+
my_pytorch_op: my_complex_op,
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
Key considerations:
|
|
230
|
+
- onnxscript functions use ONNX IR types (FLOAT, INT32, BOOL, etc.), not PyTorch types.
|
|
231
|
+
- The function signature must match the number of inputs/outputs of the PyTorch operation.
|
|
232
|
+
- Use `.cast()` for type conversions when needed.
|
|
233
|
+
|
|
234
|
+
## Workflow
|
|
235
|
+
|
|
236
|
+
### 0. Decide between direct onnxscript and hierarchical export
|
|
237
|
+
|
|
238
|
+
Before writing code, ask:
|
|
239
|
+
- **Is this a single atomic custom operation?** → Use direct onnxscript (Pattern 1).
|
|
240
|
+
- **Is this a module hierarchy that needs reusable ONNX functions?** → Use hierarchical export (Pattern 2).
|
|
241
|
+
- **Is this transformer attention?** → Use the built-in pattern (Pattern 3).
|
|
242
|
+
|
|
243
|
+
If unsure, check:
|
|
244
|
+
- [tests/expoter/test_dynamo_make_custom_op.py](../../../tests/expoter/test_dynamo_make_custom_op.py) for simple custom ops.
|
|
245
|
+
- [tests/expoter/test_dynamo_replace_custom_op.py](../../../tests/expoter/test_dynamo_replace_custom_op.py) for hierarchical examples.
|
|
246
|
+
- [tests/expoter/test_dynamo_build_onnxscript.py](../../../tests/expoter/test_dynamo_build_onnxscript.py) for build_onnxscript usage.
|
|
247
|
+
|
|
248
|
+
### 1. Check torch version and dynamo availability
|
|
249
|
+
|
|
250
|
+
```python
|
|
251
|
+
import torch
|
|
252
|
+
from inspect import signature
|
|
253
|
+
|
|
254
|
+
# Verify dynamo parameter exists
|
|
255
|
+
sig = signature(torch.onnx.export)
|
|
256
|
+
if 'dynamo' not in sig.parameters:
|
|
257
|
+
# Use torch_export_handle_lower_version from HyperONNX
|
|
258
|
+
from hyperonnx.torch_export import torch_export_handle_lower_version
|
|
259
|
+
torch_export_handle_lower_version(model, args, f, dynamo=True, ...)
|
|
260
|
+
else:
|
|
261
|
+
torch.onnx.export(model, args, f, dynamo=True, ...)
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
### 2. Identify and define custom operators
|
|
265
|
+
|
|
266
|
+
For each custom operator:
|
|
267
|
+
- Determine its input/output signatures.
|
|
268
|
+
- Write an onnxscript function or use build_onnxscript.
|
|
269
|
+
- Add it to custom_translation_table.
|
|
270
|
+
|
|
271
|
+
Example:
|
|
272
|
+
```python
|
|
273
|
+
custom_translation_table = {}
|
|
274
|
+
|
|
275
|
+
# Add manually-written onnxscript functions
|
|
276
|
+
custom_translation_table[torch.ops.custom_lib.op1] = onnxscript_op1
|
|
277
|
+
|
|
278
|
+
# Or add auto-generated ones from module specs
|
|
279
|
+
custom_translation_table[MyModule] = build_onnxscript(my_module_spec)
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
### 3. Build custom_translation_table
|
|
283
|
+
|
|
284
|
+
Use the context manager `replace_with_custom_op` for hierarchical export:
|
|
285
|
+
|
|
286
|
+
```python
|
|
287
|
+
from hyperonnx.exporter.dynamo import replace_with_custom_op
|
|
288
|
+
|
|
289
|
+
with replace_with_custom_op(model, module_spec) as custom_translation_table:
|
|
290
|
+
torch.onnx.export(
|
|
291
|
+
model,
|
|
292
|
+
example_inputs,
|
|
293
|
+
'model.onnx',
|
|
294
|
+
dynamo=True,
|
|
295
|
+
custom_translation_table=custom_translation_table,
|
|
296
|
+
opset_version=18,
|
|
297
|
+
)
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
### 4. Call torch.onnx.export with dynamo=True
|
|
301
|
+
|
|
302
|
+
```python
|
|
303
|
+
torch.onnx.export(
|
|
304
|
+
model,
|
|
305
|
+
example_inputs,
|
|
306
|
+
'model.onnx',
|
|
307
|
+
input_names=['input'],
|
|
308
|
+
output_names=['output'],
|
|
309
|
+
opset_version=18,
|
|
310
|
+
dynamo=True,
|
|
311
|
+
custom_translation_table=custom_translation_table,
|
|
312
|
+
dynamic_shapes={'input': {0: 'batch_size'}}, # Optional: specify dynamic shapes
|
|
313
|
+
)
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
Key parameters:
|
|
317
|
+
- `dynamo=True`: Use torch.export instead of TorchScript.
|
|
318
|
+
- `custom_translation_table`: Dict mapping callables to onnxscript functions.
|
|
319
|
+
- `dynamic_shapes`: Preferred over dynamic_axes for dynamo path.
|
|
320
|
+
- `opset_version`: Must align with onnxscript opset.
|
|
321
|
+
|
|
322
|
+
### 5. Handle export failures and fallback
|
|
323
|
+
|
|
324
|
+
The dynamo path may fail on complex models. HyperONNX provides fallback:
|
|
325
|
+
|
|
326
|
+
```python
|
|
327
|
+
from hyperonnx.hyper_export import export_hyper_onnx
|
|
328
|
+
|
|
329
|
+
# export_hyper_onnx tries fallback automatically
|
|
330
|
+
export_hyper_onnx(
|
|
331
|
+
model,
|
|
332
|
+
args,
|
|
333
|
+
'model.onnx',
|
|
334
|
+
dynamo=True,
|
|
335
|
+
# If dynamo export fails, HyperONNX may retry with dynamo=False
|
|
336
|
+
)
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
To disable fallback and fail fast:
|
|
340
|
+
```python
|
|
341
|
+
# Call torch_export_handle_lower_version directly without fallback
|
|
342
|
+
torch.onnx.export(
|
|
343
|
+
model,
|
|
344
|
+
args,
|
|
345
|
+
f,
|
|
346
|
+
dynamo=True,
|
|
347
|
+
# No fallback logic here
|
|
348
|
+
)
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
### 6. Validate the exported ONNX model
|
|
352
|
+
|
|
353
|
+
```python
|
|
354
|
+
import onnx
|
|
355
|
+
|
|
356
|
+
# Load and check
|
|
357
|
+
onnx_model = onnx.load('model.onnx')
|
|
358
|
+
onnx.checker.check_model(onnx_model, full_check=True)
|
|
359
|
+
|
|
360
|
+
# Verify custom operators appear
|
|
361
|
+
for node in onnx_model.graph.node:
|
|
362
|
+
print(f"{node.op_type} in domain {node.domain}")
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
## Common Errors & Recovery
|
|
366
|
+
|
|
367
|
+
| Error | Cause | Fix |
|
|
368
|
+
|-------|-------|-----|
|
|
369
|
+
| `KeyError` in custom_translation_table | torch.ops key not in table | Verify the key exactly matches the PyTorch op reference |
|
|
370
|
+
| `TypeError: ... got an unexpected keyword argument 'dynamo'` | torch.onnx.export too old | Use `torch_export_handle_lower_version` wrapper |
|
|
371
|
+
| onnxscript function signature mismatch | ONNX function has wrong number of inputs/outputs | Verify spec['args'] and spec['output'] match onnxscript function |
|
|
372
|
+
| `UnflatteningError` in build_onnxscript | Output structure not flattened | Use `plain_tensor_container` to extract only tensors |
|
|
373
|
+
| Export succeeds but ONNX model is wrong | dynamic_shapes not specified | Add `dynamic_shapes` parameter for dynamic-shape models |
|
|
374
|
+
| torch.export fails on data-dependent branches | Model has unhandled control flow | Wrap loops/conditionals with `torch.cond` or fall back to dynamo=False |
|
|
375
|
+
|
|
376
|
+
## Branching Guidance
|
|
377
|
+
|
|
378
|
+
If the request is about library implementation:
|
|
379
|
+
- Prefer HyperONNX abstractions (export_hyper_onnx, replace_with_custom_op).
|
|
380
|
+
- Preserve backward compatibility with older torch versions.
|
|
381
|
+
- Use torch_export_handle_lower_version wrapper for version bridging.
|
|
382
|
+
|
|
383
|
+
If the request is about custom operators:
|
|
384
|
+
- Start with simple onnxscript functions (Pattern 1) for atomic ops.
|
|
385
|
+
- Use build_onnxscript for auto-generation from module specs.
|
|
386
|
+
- Write manual onnxscript only when fine-grained control is needed.
|
|
387
|
+
|
|
388
|
+
If the request is about a failing export:
|
|
389
|
+
- Check if the model uses data-dependent control flow; consider torch.cond.
|
|
390
|
+
- Verify custom_translation_table keys match torch.ops exactly.
|
|
391
|
+
- Check onnxscript function signatures match specs.
|
|
392
|
+
- Try lowering torch version requirement (dynamo may not exist in older PyTorch).
|
|
393
|
+
- Consider falling back to dynamo=False if dynamo export is unreliable for this model.
|
|
394
|
+
|
|
395
|
+
## Quality Bar
|
|
396
|
+
|
|
397
|
+
A good answer or code change using this skill should:
|
|
398
|
+
- Clearly distinguish between dynamo=True (modern) and dynamo=False (legacy).
|
|
399
|
+
- Use repository-native abstractions (export_hyper_onnx, replace_with_custom_op, build_onnxscript).
|
|
400
|
+
- Specify dynamic_shapes when exporting models with variable batch size or sequence length.
|
|
401
|
+
- Include custom_translation_table definitions with clear onnxscript signatures.
|
|
402
|
+
- Provide validation (onnx.checker.check_model) and explain expected ONNX structure.
|
|
403
|
+
- Mention torch version requirements and fallback behavior.
|
|
404
|
+
|
|
405
|
+
## Example Prompts
|
|
406
|
+
|
|
407
|
+
- Export this model using dynamo=True and custom attention operators.
|
|
408
|
+
- Add dynamic shape support to this ONNX export.
|
|
409
|
+
- Create a custom_translation_table for my hierarchical model.
|
|
410
|
+
- Why does dynamo export fail on this model with torch.cond?
|
|
411
|
+
- Migrate this export from dynamo=False to dynamo=True.
|
|
412
|
+
- Build an onnxscript function for this custom module.
|
|
413
|
+
|
|
414
|
+
## Weak Spots To Clarify Before Extending This Skill
|
|
415
|
+
|
|
416
|
+
If you evolve this skill later, clarify these points first:
|
|
417
|
+
|
|
418
|
+
- Which torch versions (2.5, 2.6, 2.8, 2.9+) should be officially supported for dynamo export examples?
|
|
419
|
+
- Should we recommend torch.cond for all data-dependent branches, or only certain patterns?
|
|
420
|
+
- Is build_onnxscript reliable enough to recommend for user-defined modules, or should we prefer manual onnxscript?
|
|
421
|
+
- How to handle dtype inference in build_onnxscript for models with mixed precision (float16, bfloat16)?
|
|
422
|
+
- Should fallback behavior be exposed as a user-facing option, or remain internal to HyperONNX?
|