hyperonnx 1.0.2__tar.gz → 1.0.3__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.3/.claude/CLAUDE.md +27 -0
- hyperonnx-1.0.3/.claude/skills/torch-dynamo-onnx-export/SKILL.md +422 -0
- hyperonnx-1.0.3/.claude/skills/torch-legacy-onnx-export/SKILL.md +407 -0
- {hyperonnx-1.0.2 → hyperonnx-1.0.3}/.gitignore +7 -1
- {hyperonnx-1.0.2 → hyperonnx-1.0.3}/PKG-INFO +2 -2
- {hyperonnx-1.0.2 → hyperonnx-1.0.3}/hyperonnx/__init__.py +1 -1
- {hyperonnx-1.0.2 → hyperonnx-1.0.3}/hyperonnx/hyper_export.py +16 -1
- {hyperonnx-1.0.2 → hyperonnx-1.0.3}/hyperonnx/transformers/cache.py +59 -6
- {hyperonnx-1.0.2 → hyperonnx-1.0.3}/hyperonnx/transformers/patch.py +30 -1
- {hyperonnx-1.0.2 → hyperonnx-1.0.3}/pyproject.toml +3 -3
- {hyperonnx-1.0.2 → hyperonnx-1.0.3}/tests/test_export_hyper_onnx.py +7 -3
- {hyperonnx-1.0.2 → hyperonnx-1.0.3}/.github/words_bag.txt +0 -0
- {hyperonnx-1.0.2 → hyperonnx-1.0.3}/.github/workflows/lint.yml +0 -0
- {hyperonnx-1.0.2 → hyperonnx-1.0.3}/.github/workflows/unittest.yml +0 -0
- {hyperonnx-1.0.2 → hyperonnx-1.0.3}/.pre-commit-config.yaml +0 -0
- {hyperonnx-1.0.2 → hyperonnx-1.0.3}/LICENSE +0 -0
- {hyperonnx-1.0.2 → hyperonnx-1.0.3}/README.md +0 -0
- {hyperonnx-1.0.2 → hyperonnx-1.0.3}/README_CN.md +0 -0
- {hyperonnx-1.0.2 → hyperonnx-1.0.3}/docs/assets/qwen2_omni_vision.gif +0 -0
- {hyperonnx-1.0.2 → hyperonnx-1.0.3}/docs/assets/r18-sample.gif +0 -0
- {hyperonnx-1.0.2 → hyperonnx-1.0.3}/hyperonnx/auto.py +0 -0
- {hyperonnx-1.0.2 → hyperonnx-1.0.3}/hyperonnx/exporter/__init__.py +0 -0
- {hyperonnx-1.0.2 → hyperonnx-1.0.3}/hyperonnx/exporter/dynamo.py +0 -0
- {hyperonnx-1.0.2 → hyperonnx-1.0.3}/hyperonnx/exporter/torchscript.py +0 -0
- {hyperonnx-1.0.2 → hyperonnx-1.0.3}/hyperonnx/exporter/utils.py +0 -0
- {hyperonnx-1.0.2 → hyperonnx-1.0.3}/hyperonnx/function_rewriter.py +0 -0
- {hyperonnx-1.0.2 → hyperonnx-1.0.3}/hyperonnx/torch_export.py +0 -0
- {hyperonnx-1.0.2 → hyperonnx-1.0.3}/hyperonnx/transformers/__init__.py +0 -0
- {hyperonnx-1.0.2 → hyperonnx-1.0.3}/hyperonnx/transformers/attention.py +0 -0
- {hyperonnx-1.0.2 → hyperonnx-1.0.3}/hyperonnx/typing.py +0 -0
- {hyperonnx-1.0.2 → hyperonnx-1.0.3}/hyperonnx/utils.py +0 -0
- {hyperonnx-1.0.2 → hyperonnx-1.0.3}/tests/expoter/test_detach_module_outputs.py +0 -0
- {hyperonnx-1.0.2 → hyperonnx-1.0.3}/tests/expoter/test_dynamo_build_onnxscript.py +0 -0
- {hyperonnx-1.0.2 → hyperonnx-1.0.3}/tests/expoter/test_dynamo_make_custom_op.py +0 -0
- {hyperonnx-1.0.2 → hyperonnx-1.0.3}/tests/expoter/test_dynamo_replace_custom_op.py +0 -0
- {hyperonnx-1.0.2 → hyperonnx-1.0.3}/tests/expoter/test_non_pod_export.py +0 -0
- {hyperonnx-1.0.2 → hyperonnx-1.0.3}/tests/expoter/test_plain_output_tensors.py +0 -0
- {hyperonnx-1.0.2 → hyperonnx-1.0.3}/tests/test_auto_trace_method.py +0 -0
- {hyperonnx-1.0.2 → hyperonnx-1.0.3}/tests/test_compose_nodes_to_functions.py +0 -0
- {hyperonnx-1.0.2 → hyperonnx-1.0.3}/tests/test_export_functions.py +0 -0
- {hyperonnx-1.0.2 → hyperonnx-1.0.3}/tests/test_fuse_constants_to_function.py +0 -0
- {hyperonnx-1.0.2 → hyperonnx-1.0.3}/tests/transformers/test_attention.py +0 -0
- {hyperonnx-1.0.2 → hyperonnx-1.0.3}/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?
|
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: torch-legacy-onnx-export
|
|
3
|
+
description: Use this skill when working in HyperONNX and you need the legacy Torch ONNX export path with dynamo=False, especially for TorchScript-based export behavior, lower-version compatibility, custom attention registration, or post-export ONNX fixes.
|
|
4
|
+
argument-hint: Describe the model, export entrypoint, torch version, and whether custom attention or custom input structures are involved.
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Torch Legacy ONNX Export
|
|
8
|
+
|
|
9
|
+
Use this skill when the task is about the legacy Torch ONNX export path in HyperONNX, meaning the TorchScript-style path with dynamo=False.
|
|
10
|
+
|
|
11
|
+
This skill is specific to this repository. It captures the actual workflow used by HyperONNX code and tests, rather than generic PyTorch advice.
|
|
12
|
+
|
|
13
|
+
## Torch Behaviour
|
|
14
|
+
|
|
15
|
+
- PyTorch 2.9 changed `torch.onnx.export(..., dynamo=...)` so that `dynamo=True` is the default.
|
|
16
|
+
- On PyTorch 2.8 and earlier, do not assume the default selects the modern exporter. If legacy behavior matters, set `dynamo=False` explicitly. If modern exporter behavior matters, set `dynamo=True` explicitly.
|
|
17
|
+
- In this repository, prefer being explicit either way. Do not rely on the library default because the default changed across PyTorch versions.
|
|
18
|
+
|
|
19
|
+
⚠️ **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.
|
|
20
|
+
|
|
21
|
+
## What This Skill Produces
|
|
22
|
+
|
|
23
|
+
Produce one of these outcomes:
|
|
24
|
+
|
|
25
|
+
- A correct export snippet that uses the legacy path with dynamo=False.
|
|
26
|
+
- A HyperONNX code change that preserves legacy export compatibility.
|
|
27
|
+
- A debugging checklist for why a legacy export is failing in this repository.
|
|
28
|
+
- A validation plan that proves the exported ONNX model is structurally correct.
|
|
29
|
+
|
|
30
|
+
## When To Use It
|
|
31
|
+
|
|
32
|
+
Use this skill when the user asks for any of the following:
|
|
33
|
+
|
|
34
|
+
- Use torch.onnx.export with the old or legacy path.
|
|
35
|
+
- Force TorchScript export instead of dynamo export.
|
|
36
|
+
- Keep HyperONNX compatible with older torch.onnx.export behavior.
|
|
37
|
+
- Export a model with dynamo=False.
|
|
38
|
+
- Make custom attention export work on the TorchScript path.
|
|
39
|
+
- Understand why legacy export behaves differently from dynamo export.
|
|
40
|
+
|
|
41
|
+
Do not use this skill for generic ONNX export advice that is unrelated to HyperONNX.
|
|
42
|
+
|
|
43
|
+
## Repository Facts To Ground Decisions
|
|
44
|
+
|
|
45
|
+
- HyperONNX exposes a version-bridging wrapper in [hyperonnx/torch_export.py](../../../hyperonnx/torch_export.py) named torch_export_handle_lower_version.
|
|
46
|
+
- The main hierarchical export entrypoint is [hyperonnx/hyper_export.py](../../../hyperonnx/hyper_export.py), which accepts dynamo and may fall back between export modes.
|
|
47
|
+
- The TorchScript branch for hierarchical replacement lives in [hyperonnx/exporter/__init__.py](../../../hyperonnx/exporter/__init__.py).
|
|
48
|
+
- Transformer attention registration for the legacy path is implemented in [hyperonnx/transformers/attention.py](../../../hyperonnx/transformers/attention.py).
|
|
49
|
+
- Repository examples already show dynamo=False in [README.md](../../../README.md) and [README_CN.md](../../../README_CN.md).
|
|
50
|
+
- The duck-typing and symbolic operation system for TorchScript export is in [hyperonnx/exporter/torchscript.py](../../../hyperonnx/exporter/torchscript.py).
|
|
51
|
+
|
|
52
|
+
## Registering Custom Operators for Legacy Export
|
|
53
|
+
|
|
54
|
+
The legacy TorchScript export path requires explicit symbolic function registration for custom operators. Use this section when the task is to export custom PyTorch operations to ONNX while using dynamo=False.
|
|
55
|
+
|
|
56
|
+
### Pattern 1: Direct symbolic registration (torch-native style)
|
|
57
|
+
|
|
58
|
+
For simple custom operations that do not need hierarchical module wrapping:
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
import torch
|
|
62
|
+
from torch.onnx import register_custom_op_symbolic
|
|
63
|
+
|
|
64
|
+
# Step 1: Define a symbolic function for ONNX
|
|
65
|
+
def symbolic_my_op(g, input, param):
|
|
66
|
+
"""ONNX symbolic for my_op.
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
g: The ONNX graph context.
|
|
70
|
+
input: The input tensor from PyTorch.
|
|
71
|
+
param: A parameter.
|
|
72
|
+
|
|
73
|
+
Returns:
|
|
74
|
+
ONNX operator node(s).
|
|
75
|
+
"""
|
|
76
|
+
return g.op('custom_domain::MyOp', input, param_f=param)
|
|
77
|
+
|
|
78
|
+
# Step 2: Register the symbolic before export
|
|
79
|
+
register_custom_op_symbolic('custom_domain::my_op', symbolic_my_op, opset_version=18)
|
|
80
|
+
|
|
81
|
+
# Step 3: Export with dynamo=False
|
|
82
|
+
torch.onnx.export(
|
|
83
|
+
model,
|
|
84
|
+
example_inputs,
|
|
85
|
+
'model.onnx',
|
|
86
|
+
opset_version=18,
|
|
87
|
+
dynamo=False,
|
|
88
|
+
)
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Key rules:
|
|
92
|
+
- The symbolic function name must match the registered name in `"<domain>::<op>"` format.
|
|
93
|
+
- Always use opset_version consistently between registration and export.
|
|
94
|
+
- Register before calling export; registration persists in the current process.
|
|
95
|
+
|
|
96
|
+
### Pattern 2: HyperONNX hierarchical module with duck-typed custom op
|
|
97
|
+
|
|
98
|
+
For complex models where the custom operation is itself a module that needs hierarchical export, use the duck-typing system from [hyperonnx/exporter/torchscript.py](../../../hyperonnx/exporter/torchscript.py):
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
import torch
|
|
102
|
+
from hyperonnx import export_hyper_onnx
|
|
103
|
+
|
|
104
|
+
class MyCustomModule(torch.nn.Module):
|
|
105
|
+
"""A module that implements custom logic."""
|
|
106
|
+
def forward(self, x):
|
|
107
|
+
# Custom computation
|
|
108
|
+
return x * 2 + 1
|
|
109
|
+
|
|
110
|
+
model = torch.nn.Sequential(
|
|
111
|
+
torch.nn.Linear(10, 20),
|
|
112
|
+
MyCustomModule(),
|
|
113
|
+
torch.nn.Linear(20, 5),
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
export_hyper_onnx(
|
|
117
|
+
model,
|
|
118
|
+
(torch.randn(1, 10),),
|
|
119
|
+
'model.onnx',
|
|
120
|
+
hiera=[MyCustomModule], # Export MyCustomModule as a function
|
|
121
|
+
dynamo=False,
|
|
122
|
+
opset_version=18,
|
|
123
|
+
)
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
How it works:
|
|
127
|
+
1. When you pass `hiera=[MyCustomModule]`, HyperONNX will trace and export the module separately.
|
|
128
|
+
2. The module is then replaced with a duck-type that acts like a custom operator node.
|
|
129
|
+
3. The DuckForward class (from torchscript.py) handles the ONNX symbolic generation automatically.
|
|
130
|
+
|
|
131
|
+
Reference: [tests/expoter/test_dynamo_replace_custom_op.py](../../../tests/expoter/test_dynamo_replace_custom_op.py) for hierarchical examples.
|
|
132
|
+
|
|
133
|
+
### Pattern 3: Attention-specific registration
|
|
134
|
+
|
|
135
|
+
For transformer attention layers, HyperONNX provides a pre-built pattern:
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
import torch
|
|
139
|
+
from hyperonnx.transformers.attention import register_attention_opsets
|
|
140
|
+
|
|
141
|
+
# Register custom attention ONNX symbolic for the legacy path
|
|
142
|
+
register_attention_opsets()
|
|
143
|
+
|
|
144
|
+
# Then proceed with normal export
|
|
145
|
+
torch.onnx.export(
|
|
146
|
+
model,
|
|
147
|
+
example_inputs,
|
|
148
|
+
'model.onnx',
|
|
149
|
+
opset_version=24,
|
|
150
|
+
dynamo=False,
|
|
151
|
+
)
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
This registration only affects the TorchScript (legacy) path. See "Handle transformer attention correctly" in Workflow step 4 for post-export adjustments.
|
|
155
|
+
|
|
156
|
+
## Understanding torchscript.py: Duck-Typing and Symbolic Export
|
|
157
|
+
|
|
158
|
+
The [hyperonnx/exporter/torchscript.py](../../../hyperonnx/exporter/torchscript.py) module implements a clever pattern for exporting PyTorch modules as ONNX custom operators without requiring explicit symbolic functions. Here is how it works:
|
|
159
|
+
|
|
160
|
+
### Core Concepts
|
|
161
|
+
|
|
162
|
+
**DuckForward**: A torch.autograd.Function subclass that:
|
|
163
|
+
- Stores the precomputed output in module_spec at forward time.
|
|
164
|
+
- Implements a `symbolic()` static method that generates an ONNX node instead of computing.
|
|
165
|
+
- Acts as a "duck type" – it quacks like a normal forward pass during tracing, but produces ONNX during export.
|
|
166
|
+
|
|
167
|
+
**make_duck_forward**: Creates a DuckForward instance with closure over a specific module_spec.
|
|
168
|
+
|
|
169
|
+
**replace_duck_forward**: Context manager that temporarily replaces module.forward with duck-typed versions.
|
|
170
|
+
|
|
171
|
+
### Workflow (Three Phases)
|
|
172
|
+
|
|
173
|
+
**Phase 1: Tracing and Caching**
|
|
174
|
+
|
|
175
|
+
Before export, HyperONNX calls `trace_module_spec()` which:
|
|
176
|
+
1. Registers forward hooks on all modules in the hiera list.
|
|
177
|
+
2. Runs the model with example inputs to capture: input signatures, actual output values, loop count.
|
|
178
|
+
3. Stores all this in module_spec.
|
|
179
|
+
|
|
180
|
+
**Phase 2: Duck-Typing (During torch.onnx.export)**
|
|
181
|
+
|
|
182
|
+
When `torch.onnx.export` traces the model with dynamo=False:
|
|
183
|
+
1. `replace_duck_forward()` context manager replaces each hiera module's forward with a duck-typed version.
|
|
184
|
+
2. Each duck forward is a DuckForward.apply() call that:
|
|
185
|
+
- During normal tracing: calls DuckForward.forward(), returns the precomputed cached output.
|
|
186
|
+
- During ONNX symbolic tracing: calls DuckForward.symbolic(), returns an ONNX op node.
|
|
187
|
+
3. The exporter builds the ONNX graph, inserting custom op nodes where the duck forwards were.
|
|
188
|
+
|
|
189
|
+
**Phase 3: Function Embedding**
|
|
190
|
+
|
|
191
|
+
After export, HyperONNX uses other rewriters to:
|
|
192
|
+
1. Embed the actual ONNX subgraph for each module into a function definition.
|
|
193
|
+
2. Replace the custom op nodes with function calls.
|
|
194
|
+
|
|
195
|
+
### Key Implementation Details
|
|
196
|
+
|
|
197
|
+
**Forward method**: Stores precomputed output in module_spec, indexed by loop counter. Handles multiple calls to the same module and flattens nested output structures.
|
|
198
|
+
|
|
199
|
+
**Symbolic method**: Uses the module's type_name (set during tracing) as the ONNX op name. Filters out None args and specifies the number of outputs.
|
|
200
|
+
|
|
201
|
+
**Forward wrapper (_forward function)**:
|
|
202
|
+
- Runs in inference mode to avoid backprop.
|
|
203
|
+
- Flattens input structures the same way as the real forward.
|
|
204
|
+
- Reorders kwargs to match the original function signature.
|
|
205
|
+
- Calls DuckForward.apply which triggers either forward or symbolic depending on context.
|
|
206
|
+
|
|
207
|
+
### Why This Pattern Matters for Legacy Export
|
|
208
|
+
|
|
209
|
+
1. **No need for explicit symbolic functions**: The duck-typing pattern generates ONNX nodes automatically.
|
|
210
|
+
2. **Handles complex module interactions**: Multiple calls, nested outputs, custom input types are all cached.
|
|
211
|
+
3. **Integrates with TorchScript tracer**: The exporter sees normal forward calls; duck-typing is transparent.
|
|
212
|
+
4. **Preserves correctness**: By caching actual outputs during tracing, the ONNX graph matches PyTorch semantics.
|
|
213
|
+
|
|
214
|
+
### When to Use vs. When Not To Use
|
|
215
|
+
|
|
216
|
+
**Use duck-typing (torchscript.py) when:**
|
|
217
|
+
- You are using HyperONNX hierarchical export (passing hiera list).
|
|
218
|
+
- The module is complex enough that writing a correct symbolic function is error-prone.
|
|
219
|
+
- You want the output to be an embedded ONNX function, not just a custom op node.
|
|
220
|
+
|
|
221
|
+
**Use direct symbolic registration when:**
|
|
222
|
+
- You only have a single custom operator, not a module hierarchy.
|
|
223
|
+
- You already have a working symbolic function.
|
|
224
|
+
- You want the ONNX output to reference an external custom op domain.
|
|
225
|
+
|
|
226
|
+
### Common Gotchas
|
|
227
|
+
|
|
228
|
+
1. **Mismatched signatures**: If the cached output from tracing does not match the actual forward call structure, duck forward will return wrong data. Always verify with trace_module_spec.
|
|
229
|
+
2. **Module called multiple times**: Duck forward increments loops and indexes into loop_outputs. Ensure tracing and export see the same number of calls.
|
|
230
|
+
3. **Non-tensor outputs**: The symbolic method must return an op or tuple of ops. Use plain_tensor_container to extract just tensors.
|
|
231
|
+
4. **Backward incompatibility**: Do not call backward on a DuckForward result. The backward method raises RuntimeError by design.
|
|
232
|
+
|
|
233
|
+
## Workflow
|
|
234
|
+
|
|
235
|
+
### 0. Decide between direct symbolic registration and duck-typing
|
|
236
|
+
|
|
237
|
+
Before writing code, ask:
|
|
238
|
+
- **Is this a single custom op that will appear in the final ONNX?** → Use direct symbolic registration (Pattern 1).
|
|
239
|
+
- **Is this a PyTorch module that should become a reusable ONNX function?** → Use HyperONNX hierarchical export with duck-typing (Pattern 2).
|
|
240
|
+
- **Is this transformer attention?** → Use the built-in pattern (Pattern 3) and refer to step 4 of Workflow.
|
|
241
|
+
|
|
242
|
+
If unsure, check the test files:
|
|
243
|
+
- [tests/expoter/test_dynamo_replace_custom_op.py](../../../tests/expoter/test_dynamo_replace_custom_op.py) for hierarchical examples.
|
|
244
|
+
- [hyperonnx/transformers/attention.py](../../../hyperonnx/transformers/attention.py) for attention-specific patterns.
|
|
245
|
+
|
|
246
|
+
### 1. Identify the export entrypoint
|
|
247
|
+
|
|
248
|
+
First determine which layer of the repository the task belongs to:
|
|
249
|
+
|
|
250
|
+
- If the task is about HyperONNX library internals, prefer torch_export_handle_lower_version instead of calling raw torch.onnx.export directly.
|
|
251
|
+
- If the task is about exporting a full hierarchical model, work through export_hyper_onnx in [hyperonnx/hyper_export.py](../../../hyperonnx/hyper_export.py).
|
|
252
|
+
- If the task is a direct usage example or a focused test, raw torch.onnx.export is acceptable when it matches the repository’s current test style.
|
|
253
|
+
|
|
254
|
+
### 2. Choose the export mode deliberately
|
|
255
|
+
|
|
256
|
+
Use this decision logic:
|
|
257
|
+
|
|
258
|
+
- If the requirement is legacy or TorchScript export, set dynamo=False explicitly.
|
|
259
|
+
- If the user only says export must work and does not require legacy behavior, note that HyperONNX may try fallback modes internally in some paths.
|
|
260
|
+
- If the code is in trace_module_spec, remember the repository intentionally uses dynamo=False there to keep tracing behavior aligned with TorchScript export semantics.
|
|
261
|
+
|
|
262
|
+
### 3. Apply the correct calling pattern
|
|
263
|
+
|
|
264
|
+
For repository code changes:
|
|
265
|
+
|
|
266
|
+
- Preserve the wrapper call shape used in [hyperonnx/torch_export.py](../../../hyperonnx/torch_export.py).
|
|
267
|
+
- Keep kwargs handling compatible with torch versions below 2.5.0, where kwargs are merged into args.
|
|
268
|
+
- Do not remove the OperatorExportTypes.ONNX_ATEN_FALLBACK behavior for older torch versions unless the task explicitly requires it and the change is validated.
|
|
269
|
+
|
|
270
|
+
For direct usage examples:
|
|
271
|
+
|
|
272
|
+
- Pass dynamo=False explicitly.
|
|
273
|
+
- Provide input_names and output_names when the model interface is non-trivial.
|
|
274
|
+
- Keep opset_version explicit when export semantics matter.
|
|
275
|
+
|
|
276
|
+
Minimal example:
|
|
277
|
+
|
|
278
|
+
```python
|
|
279
|
+
import torch
|
|
280
|
+
|
|
281
|
+
model = MyModule().eval()
|
|
282
|
+
example_inputs = (x, y)
|
|
283
|
+
|
|
284
|
+
torch.onnx.export(
|
|
285
|
+
model,
|
|
286
|
+
example_inputs,
|
|
287
|
+
"model.onnx",
|
|
288
|
+
input_names=["x", "y"],
|
|
289
|
+
output_names=["out"],
|
|
290
|
+
opset_version=19,
|
|
291
|
+
dynamo=False,
|
|
292
|
+
)
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
### 4. Handle transformer attention correctly
|
|
296
|
+
|
|
297
|
+
If the task involves HyperONNX transformer attention, the legacy path has a required branch:
|
|
298
|
+
|
|
299
|
+
- Before export on the TorchScript path, call register_attention_opsets() from [hyperonnx/transformers/attention.py](../../../hyperonnx/transformers/attention.py).
|
|
300
|
+
- This registration exists specifically for the torchscript export path with dynamo=False.
|
|
301
|
+
- If the export also relies on the custom Attention op becoming default-domain ONNX Attention opset 24, promote the exported model with promote_onnx_model_to_opset24() after export.
|
|
302
|
+
|
|
303
|
+
Pattern:
|
|
304
|
+
|
|
305
|
+
```python
|
|
306
|
+
import onnx
|
|
307
|
+
import torch
|
|
308
|
+
|
|
309
|
+
from hyperonnx.transformers.attention import (
|
|
310
|
+
promote_onnx_model_to_opset24,
|
|
311
|
+
register_attention_opsets,
|
|
312
|
+
)
|
|
313
|
+
|
|
314
|
+
register_attention_opsets()
|
|
315
|
+
|
|
316
|
+
torch.onnx.export(
|
|
317
|
+
model,
|
|
318
|
+
example_inputs,
|
|
319
|
+
output_path,
|
|
320
|
+
opset_version=24,
|
|
321
|
+
dynamo=False,
|
|
322
|
+
)
|
|
323
|
+
|
|
324
|
+
onnx_model = onnx.load_model(str(output_path))
|
|
325
|
+
onnx_model = promote_onnx_model_to_opset24(onnx_model)
|
|
326
|
+
onnx.checker.check_model(onnx_model, full_check=True)
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
Reference implementation: [tests/transformers/test_attention.py](../../../tests/transformers/test_attention.py)
|
|
330
|
+
|
|
331
|
+
### 5. Watch for input-structure limitations
|
|
332
|
+
|
|
333
|
+
Legacy export does not make arbitrary Python objects exportable.
|
|
334
|
+
|
|
335
|
+
Use these rules from the repository tests:
|
|
336
|
+
|
|
337
|
+
- Plain custom objects fail export.
|
|
338
|
+
- Tuple or list subclasses may pass flattening but still lose class behavior.
|
|
339
|
+
- A torch.Tensor subclass can sometimes preserve the needed behavior well enough for export.
|
|
340
|
+
|
|
341
|
+
Reference: [tests/expoter/test_non_pod_export.py](../../../tests/expoter/test_non_pod_export.py)
|
|
342
|
+
|
|
343
|
+
If the user is trying to export non-POD inputs, explain that the fix is usually to flatten or tensorize inputs, not to keep arbitrary Python objects in the ONNX boundary.
|
|
344
|
+
|
|
345
|
+
### 6. Validate the exported model
|
|
346
|
+
|
|
347
|
+
Minimum completion checks:
|
|
348
|
+
|
|
349
|
+
- The export artifact exists or the in-memory export object is non-null.
|
|
350
|
+
- onnx.checker.check_model passes.
|
|
351
|
+
- The chosen export path really used dynamo=False when legacy behavior was required.
|
|
352
|
+
- If custom attention is involved, verify the expected Attention node and domain/opset normalization.
|
|
353
|
+
- If changing library code, verify at least the smallest relevant test for the touched path.
|
|
354
|
+
|
|
355
|
+
Good repository-aligned validation targets:
|
|
356
|
+
|
|
357
|
+
- [tests/transformers/test_attention.py](../../../tests/transformers/test_attention.py)
|
|
358
|
+
- [tests/transformers/test_static_cache.py](../../../tests/transformers/test_static_cache.py)
|
|
359
|
+
- [tests/expoter/test_dynamo_replace_custom_op.py](../../../tests/expoter/test_dynamo_replace_custom_op.py) for contrast with the dynamo path
|
|
360
|
+
|
|
361
|
+
## Branching Guidance
|
|
362
|
+
|
|
363
|
+
If the request is about library implementation:
|
|
364
|
+
|
|
365
|
+
- Prefer HyperONNX wrappers and existing abstractions.
|
|
366
|
+
- Preserve backward-compatibility branches.
|
|
367
|
+
- Avoid replacing the wrapper with direct raw torch.onnx.export unless there is a strong reason.
|
|
368
|
+
|
|
369
|
+
If the request is about examples or docs:
|
|
370
|
+
|
|
371
|
+
- Show the shortest correct snippet with dynamo=False.
|
|
372
|
+
- Mention any required registration step up front.
|
|
373
|
+
- Link the example back to the repository’s existing test or README pattern.
|
|
374
|
+
|
|
375
|
+
If the request is about a failing export:
|
|
376
|
+
|
|
377
|
+
- Check whether the failure is caused by unsupported input structures.
|
|
378
|
+
- Check whether the user forgot dynamo=False.
|
|
379
|
+
- Check whether custom attention registration was skipped.
|
|
380
|
+
- Check whether post-export opset promotion is missing.
|
|
381
|
+
- Check whether the code path should have used torch_export_handle_lower_version.
|
|
382
|
+
|
|
383
|
+
## Quality Bar
|
|
384
|
+
|
|
385
|
+
A good answer or code change using this skill should:
|
|
386
|
+
|
|
387
|
+
- Distinguish clearly between legacy TorchScript export and dynamo export.
|
|
388
|
+
- Use repository-native entrypoints and helpers.
|
|
389
|
+
- Mention version-compatibility handling when touching core export code.
|
|
390
|
+
- Include concrete validation, not just export code.
|
|
391
|
+
- Avoid generic ONNX advice that ignores HyperONNX’s actual implementation.
|
|
392
|
+
|
|
393
|
+
## Example Prompts
|
|
394
|
+
|
|
395
|
+
- Use the legacy torch.onnx.export path for this HyperONNX test.
|
|
396
|
+
- Add a sample that exports with dynamo=False and validates the ONNX.
|
|
397
|
+
- Debug why this custom attention export fails on the TorchScript path.
|
|
398
|
+
- Update HyperONNX export code without breaking lower torch versions.
|
|
399
|
+
- Show me the correct legacy export flow for a transformer module in this repo.
|
|
400
|
+
|
|
401
|
+
## Weak Spots To Clarify Before Extending This Skill
|
|
402
|
+
|
|
403
|
+
If you evolve this skill later, clarify these points first:
|
|
404
|
+
|
|
405
|
+
- Whether the team wants to prefer legacy export in docs, or only keep it as a compatibility path.
|
|
406
|
+
- Which torch version range should be treated as officially supported for legacy export examples.
|
|
407
|
+
- Whether future transformer examples should standardize on post-export opset promotion or hide that behind a helper.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: hyperonnx
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.3
|
|
4
4
|
Summary: Copyright (C) 2025 The HYPERONNX Authors.
|
|
5
5
|
Author-email: Wenyi Tang <wenyitang@outlook.com>
|
|
6
6
|
Requires-Python: >=3.11
|
|
@@ -9,7 +9,7 @@ Classifier: License :: OSI Approved :: Apache Software License
|
|
|
9
9
|
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
10
10
|
Classifier: Topic :: Software Development
|
|
11
11
|
License-File: LICENSE
|
|
12
|
-
Requires-Dist: onnxifier>=2.0.
|
|
12
|
+
Requires-Dist: onnxifier>=2.0.3
|
|
13
13
|
Requires-Dist: onnxscript<0.6.0
|
|
14
14
|
Requires-Dist: onnx>=1.18.0
|
|
15
15
|
Requires-Dist: torch<2.11 ; extra == "cpu"
|
|
@@ -209,6 +209,8 @@ def _export_hiera(
|
|
|
209
209
|
dynamo: bool,
|
|
210
210
|
external_data: bool,
|
|
211
211
|
do_optimization: bool,
|
|
212
|
+
fold_nodes_to_functions: bool,
|
|
213
|
+
fuse_constants_to_function: bool,
|
|
212
214
|
external_directory: str | PathLike | None,
|
|
213
215
|
module_spec: dict[Module, ModuleSpec],
|
|
214
216
|
hiera: Collection[type[Module]],
|
|
@@ -244,6 +246,8 @@ def _export_hiera(
|
|
|
244
246
|
hiera=hiera,
|
|
245
247
|
module_spec=child_spec,
|
|
246
248
|
do_optimization=do_optimization,
|
|
249
|
+
fold_nodes_to_functions=fold_nodes_to_functions,
|
|
250
|
+
fuse_constants_to_function=fuse_constants_to_function,
|
|
247
251
|
external_directory=external_directory,
|
|
248
252
|
)
|
|
249
253
|
# update since child_spec may be updated
|
|
@@ -318,6 +322,8 @@ def export_hyper_onnx( # noqa: C901
|
|
|
318
322
|
hiera: Collection[type[Module]] | None = None,
|
|
319
323
|
module_spec: dict[Module, ModuleSpec] | None = None,
|
|
320
324
|
do_optimization: bool = True,
|
|
325
|
+
fold_nodes_to_functions: bool = True,
|
|
326
|
+
fuse_constants_to_function: bool = True,
|
|
321
327
|
external_directory: str | PathLike | None = None,
|
|
322
328
|
**_: Any, # ignored options
|
|
323
329
|
) -> Any | None:
|
|
@@ -347,6 +353,10 @@ def export_hyper_onnx( # noqa: C901
|
|
|
347
353
|
module_spec (Optional[Dict[Module, ModuleSpec]]): A dictionary to store the
|
|
348
354
|
detail spec of modules.
|
|
349
355
|
do_optimization: Whether to optimize the exported ONNX model.
|
|
356
|
+
fold_nodes_to_functions: Whether to compose individual nodes into a function.
|
|
357
|
+
Defaults to True.
|
|
358
|
+
fuse_constants_to_function: Whether to fuse constants into function.
|
|
359
|
+
Defaults to True.
|
|
350
360
|
external_directory (Optional[str | PathLike]): The directory to save the onnx
|
|
351
361
|
model exported to be composed. If not specified, the model will be saved
|
|
352
362
|
in the memory. Set to True if functions to be composed are too large.
|
|
@@ -389,7 +399,8 @@ def export_hyper_onnx( # noqa: C901
|
|
|
389
399
|
if _dyn == dynamo:
|
|
390
400
|
logger.warning(
|
|
391
401
|
f" Failed to export {model_typename}, "
|
|
392
|
-
f"try use dynamo={not _dyn}"
|
|
402
|
+
f"try use dynamo={not _dyn}, "
|
|
403
|
+
"set ONNXIFIER_LOG_LEVEL=DEBUG to see error logs."
|
|
393
404
|
)
|
|
394
405
|
logger.debug(f" <<<\n{ex}")
|
|
395
406
|
raise RuntimeError(f" Failed to export {model_typename}.")
|
|
@@ -411,6 +422,8 @@ def export_hyper_onnx( # noqa: C901
|
|
|
411
422
|
dynamo=dynamo,
|
|
412
423
|
external_data=external_data,
|
|
413
424
|
do_optimization=do_optimization,
|
|
425
|
+
fold_nodes_to_functions=fold_nodes_to_functions,
|
|
426
|
+
fuse_constants_to_function=fuse_constants_to_function,
|
|
414
427
|
external_directory=external_directory,
|
|
415
428
|
module_spec=module_spec,
|
|
416
429
|
hiera=hiera,
|
|
@@ -460,7 +473,9 @@ def export_hyper_onnx( # noqa: C901
|
|
|
460
473
|
passes = [ComposeOnnxAsFunctionRewriter(HYPER_DOMAIN, tuple(module_spec.values()))]
|
|
461
474
|
if do_optimization:
|
|
462
475
|
passes.extend(OPTIMIZER_PASSES)
|
|
476
|
+
if fold_nodes_to_functions:
|
|
463
477
|
passes.append(ComposeNodesToFunctionsRewriter(model_typename))
|
|
478
|
+
if fuse_constants_to_function:
|
|
464
479
|
passes.append(FuseConstantsToFunctionRewriter())
|
|
465
480
|
with chdir(external_directory):
|
|
466
481
|
graph = PassManager(
|
|
@@ -33,6 +33,8 @@ class StaticCache(Tensor):
|
|
|
33
33
|
We should treat DynamicCache as static for onnx export.
|
|
34
34
|
"""
|
|
35
35
|
|
|
36
|
+
# pylint: disable=unused-argument
|
|
37
|
+
|
|
36
38
|
def update(
|
|
37
39
|
self,
|
|
38
40
|
key_states: Tensor,
|
|
@@ -61,6 +63,12 @@ class StaticCache(Tensor):
|
|
|
61
63
|
|
|
62
64
|
layer_kv = self[layer_idx]
|
|
63
65
|
keys, values = layer_kv.unbind()
|
|
66
|
+
if torch.onnx.is_in_onnx_export():
|
|
67
|
+
# We don't want to update cache because it's inplace. During exporting,
|
|
68
|
+
# the forward could be called multiple times and the cache will be falsely
|
|
69
|
+
# updated, so we skip cache update in onnx exporting phase.
|
|
70
|
+
keys = keys.clone()
|
|
71
|
+
values = values.clone()
|
|
64
72
|
# Some old models give None for `cache_position` or even omit passing
|
|
65
73
|
# `cache_kwargs` when used as cross-attention, in which case we should copy
|
|
66
74
|
# the whole Layer (key_states.shape[-2] == self.max_cache_len)
|
|
@@ -82,6 +90,40 @@ class StaticCache(Tensor):
|
|
|
82
90
|
|
|
83
91
|
return keys, values
|
|
84
92
|
|
|
93
|
+
def update_conv_state(
|
|
94
|
+
self, conv_states: torch.Tensor, layer_idx: int, **kwargs
|
|
95
|
+
) -> torch.Tensor:
|
|
96
|
+
"""
|
|
97
|
+
Updates the cache with the new `conv_states` for the layer `layer_idx`.
|
|
98
|
+
|
|
99
|
+
Parameters:
|
|
100
|
+
conv_states (`torch.Tensor`):
|
|
101
|
+
The new conv states to cache.
|
|
102
|
+
layer_idx (`int`):
|
|
103
|
+
The index of the layer to cache the states for.
|
|
104
|
+
|
|
105
|
+
Return:
|
|
106
|
+
`torch.Tensor`: The updated conv states.
|
|
107
|
+
"""
|
|
108
|
+
raise NotImplementedError("Qwen GDN conv state is not supported yet.")
|
|
109
|
+
|
|
110
|
+
def update_recurrent_state(
|
|
111
|
+
self, recurrent_states: torch.Tensor, layer_idx: int, **kwargs
|
|
112
|
+
) -> torch.Tensor:
|
|
113
|
+
"""
|
|
114
|
+
Updates the cache with the new `recurrent_states` for the layer `layer_idx`.
|
|
115
|
+
|
|
116
|
+
Parameters:
|
|
117
|
+
smm_states (`torch.Tensor`):
|
|
118
|
+
The new ssm states to cache.
|
|
119
|
+
layer_idx (`int`):
|
|
120
|
+
The index of the layer to cache the states for.
|
|
121
|
+
|
|
122
|
+
Return:
|
|
123
|
+
`torch.Tensor`: The updated ssm states.
|
|
124
|
+
"""
|
|
125
|
+
raise NotImplementedError("Qwen GDN recurrent state is not supported yet.")
|
|
126
|
+
|
|
85
127
|
def early_initialization(
|
|
86
128
|
self,
|
|
87
129
|
batch_size: int,
|
|
@@ -107,11 +149,9 @@ class StaticCache(Tensor):
|
|
|
107
149
|
"""Returns the sequence length of the cache for the given layer."""
|
|
108
150
|
if layer_idx >= self.shape[0]:
|
|
109
151
|
return 0
|
|
110
|
-
return int(self[
|
|
152
|
+
return int(self[layer_idx, 0].any(dim=-1).sum().item())
|
|
111
153
|
|
|
112
|
-
def get_mask_sizes(
|
|
113
|
-
self, cache_position: torch.Tensor, layer_idx: int
|
|
114
|
-
) -> tuple[int, int]:
|
|
154
|
+
def get_mask_sizes(self, query_length: int, layer_idx: int) -> tuple[int, int]:
|
|
115
155
|
"""
|
|
116
156
|
Return a tuple (kv_length, kv_offset) corresponding to the length and offset
|
|
117
157
|
that will be returned for the given layer at `layer_idx`. The masks are then
|
|
@@ -119,8 +159,9 @@ class StaticCache(Tensor):
|
|
|
119
159
|
for each layer.
|
|
120
160
|
"""
|
|
121
161
|
if layer_idx >= self.shape[0]:
|
|
122
|
-
|
|
123
|
-
|
|
162
|
+
if isinstance(query_length, torch.Tensor) and query_length.ndim > 0:
|
|
163
|
+
return query_length.shape[0], 0
|
|
164
|
+
return self.max_cache_len, 0
|
|
124
165
|
|
|
125
166
|
def get_max_cache_shape(self, layer_idx: int = 0) -> int:
|
|
126
167
|
"""
|
|
@@ -140,6 +181,18 @@ class StaticCache(Tensor):
|
|
|
140
181
|
if self.get_seq_length() > 0:
|
|
141
182
|
self.copy_(self.index_select(0, beam_idx.to(self.device)))
|
|
142
183
|
|
|
184
|
+
def crop(self, max_length: int):
|
|
185
|
+
"""Do nothing for static cache."""
|
|
186
|
+
return
|
|
187
|
+
|
|
188
|
+
def batch_repeat_interleave(self, repeats: int):
|
|
189
|
+
"""Do nothing for static cache."""
|
|
190
|
+
return
|
|
191
|
+
|
|
192
|
+
def batch_select_indices(self, indices: torch.Tensor):
|
|
193
|
+
"""Do nothing for static cache."""
|
|
194
|
+
return
|
|
195
|
+
|
|
143
196
|
@property
|
|
144
197
|
def max_batch_size(self) -> int:
|
|
145
198
|
"""Return the maximum batch size of the cache"""
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"""
|
|
2
|
-
Copyright (C)
|
|
2
|
+
Copyright (C) 2026 The HYPERONNX Authors.
|
|
3
3
|
|
|
4
4
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
5
|
you may not use this file except in compliance with the License.
|
|
@@ -18,6 +18,7 @@ import importlib
|
|
|
18
18
|
from contextlib import contextmanager
|
|
19
19
|
from unittest.mock import patch
|
|
20
20
|
|
|
21
|
+
import torch
|
|
21
22
|
from packaging.version import Version
|
|
22
23
|
|
|
23
24
|
|
|
@@ -34,6 +35,7 @@ def patch_transformers():
|
|
|
34
35
|
TRANSFORMERS_HAS_SDPA_ATTENTION = Version("4.48.0")
|
|
35
36
|
TRANSFORMERS_HAS_SDPA_MASK = Version("4.53.0")
|
|
36
37
|
TRANSFORMERS_FIX_VMAP = Version("5.0.0")
|
|
38
|
+
TRANSFORMERS_DEPRECATE_CACHE_POSITION = Version("5.4.0")
|
|
37
39
|
CURR_VER = Version(transformers.__version__)
|
|
38
40
|
|
|
39
41
|
patches = []
|
|
@@ -56,6 +58,33 @@ def patch_transformers():
|
|
|
56
58
|
"transformers.integrations.sdpa_attention.use_gqa_in_sdpa", use_gqa_in_sdpa
|
|
57
59
|
)
|
|
58
60
|
patches.append(p2)
|
|
61
|
+
if CURR_VER >= TRANSFORMERS_DEPRECATE_CACHE_POSITION:
|
|
62
|
+
original_sdpa_mask = transformers.masking_utils.sdpa_mask
|
|
63
|
+
|
|
64
|
+
def patch_sdpa_mask(
|
|
65
|
+
batch_size: int,
|
|
66
|
+
q_length: int,
|
|
67
|
+
kv_length: int,
|
|
68
|
+
*args,
|
|
69
|
+
**kwargs,
|
|
70
|
+
):
|
|
71
|
+
# In onnx exporting (torch.onnx.is_in_onnx_export()==True), the shape of a
|
|
72
|
+
# Tensor is still a tensor:
|
|
73
|
+
# type(tensor([1]).shape[0]) == torch.Tensor
|
|
74
|
+
# So the wrap in sdpa_mask is wrong in this case. We forcely convert the
|
|
75
|
+
# shape to integer.
|
|
76
|
+
def _to_int(x):
|
|
77
|
+
if isinstance(x, torch.Tensor) and x.ndim == 0:
|
|
78
|
+
return int(x.item())
|
|
79
|
+
return x
|
|
80
|
+
|
|
81
|
+
q_length = _to_int(q_length)
|
|
82
|
+
kv_length = _to_int(kv_length)
|
|
83
|
+
|
|
84
|
+
return original_sdpa_mask(batch_size, q_length, kv_length, *args, **kwargs)
|
|
85
|
+
|
|
86
|
+
p3 = patch("transformers.masking_utils.sdpa_mask", patch_sdpa_mask)
|
|
87
|
+
patches.append(p3)
|
|
59
88
|
|
|
60
89
|
try:
|
|
61
90
|
for p in patches:
|
|
@@ -28,7 +28,7 @@ classifiers = [
|
|
|
28
28
|
"Topic :: Software Development",
|
|
29
29
|
]
|
|
30
30
|
dynamic = ["version", "description"]
|
|
31
|
-
dependencies = ["onnxifier>=2.0.
|
|
31
|
+
dependencies = ["onnxifier>=2.0.3", "onnxscript<0.6.0", "onnx>=1.18.0"]
|
|
32
32
|
|
|
33
33
|
[project.urls]
|
|
34
34
|
Home = "https://github.com/LoSealL/hyperonnx"
|
|
@@ -47,7 +47,7 @@ dev = [
|
|
|
47
47
|
"pyright>=1.1.408",
|
|
48
48
|
"pytest",
|
|
49
49
|
"pytest-cov",
|
|
50
|
-
"transformers>=4.48.0
|
|
50
|
+
"transformers>=4.48.0",
|
|
51
51
|
]
|
|
52
52
|
|
|
53
53
|
[tool.setuptools]
|
|
@@ -56,7 +56,7 @@ packages = ["hyperonnx"]
|
|
|
56
56
|
|
|
57
57
|
[tool.pyright]
|
|
58
58
|
include = ["hyperonnx"]
|
|
59
|
-
exclude = ["tests"]
|
|
59
|
+
exclude = ["**/node_modules", "**/__pycache__", "**/.*", ".venv", "tests"]
|
|
60
60
|
|
|
61
61
|
[tool.pytest.ini_options]
|
|
62
62
|
testpaths = ["tests"]
|
|
@@ -140,6 +140,7 @@ def test_export_resnet_with_basicblock_and_bottleneck(dynamo):
|
|
|
140
140
|
input_names=["input.1"],
|
|
141
141
|
hiera=[ResNet, BasicBlock, Bottleneck],
|
|
142
142
|
do_optimization=False,
|
|
143
|
+
fold_nodes_to_functions=False,
|
|
143
144
|
dynamo=dynamo,
|
|
144
145
|
)
|
|
145
146
|
model = onnx.load_from_string(f.getvalue())
|
|
@@ -229,7 +230,6 @@ def test_export_llama_transformers(dynamo, cache, tmp_path):
|
|
|
229
230
|
input_ids=torch.randint(config.vocab_size, [1, 16]).long(),
|
|
230
231
|
attention_mask=torch.ones([1, 16]),
|
|
231
232
|
position_ids=torch.arange(16).long()[None],
|
|
232
|
-
cache_position=torch.arange(16).long(),
|
|
233
233
|
)
|
|
234
234
|
if cache:
|
|
235
235
|
# example_inputs["use_cache"] = True
|
|
@@ -243,10 +243,12 @@ def test_export_llama_transformers(dynamo, cache, tmp_path):
|
|
|
243
243
|
)
|
|
244
244
|
with torch.no_grad():
|
|
245
245
|
ref_output = llama(**example_inputs)
|
|
246
|
+
if cache:
|
|
247
|
+
example_inputs["past_key_values"].fill_(0)
|
|
246
248
|
with BytesIO() as f, patch_transformers(), torch.no_grad():
|
|
247
|
-
input_names = ["input_ids", "attention_mask", "position_ids"
|
|
249
|
+
input_names = ["input_ids", "attention_mask", "position_ids"]
|
|
248
250
|
if cache:
|
|
249
|
-
input_names.
|
|
251
|
+
input_names.append("past_key_values")
|
|
250
252
|
output_names = ["last_hidden_state"]
|
|
251
253
|
if cache:
|
|
252
254
|
output_names.append("present_key_values")
|
|
@@ -311,6 +313,8 @@ def test_export_rnnlike(dynamo):
|
|
|
311
313
|
hiera=[RNNCell],
|
|
312
314
|
input_names=["x"],
|
|
313
315
|
do_optimization=False,
|
|
316
|
+
fold_nodes_to_functions=False,
|
|
317
|
+
fuse_constants_to_function=False,
|
|
314
318
|
dynamo=dynamo,
|
|
315
319
|
)
|
|
316
320
|
onnx_model = onnx.load_from_string(f.getvalue())
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|