nathansut1 commited on
Commit
f00cc10
Β·
verified Β·
1 Parent(s): d8cc6e6

Upload fix_onnx.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. fix_onnx.py +215 -0
fix_onnx.py ADDED
@@ -0,0 +1,215 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """Fix ONNX model for TensorRT compatibility.
3
+
4
+ Takes the raw tf2onnx output (model.onnx) and produces a TRT-optimized
5
+ model (model_convtranspose.onnx) in three steps:
6
+
7
+ Step 2A: Fix Reshape node with -2048 batch dim -> -1
8
+ Step 2B: Fix Resize nodes with TF-specific attributes
9
+ Step 2C: Validate model
10
+ Step 3: Replace Resize (nearest 2x upsample) -> ConvTranspose
11
+
12
+ Usage:
13
+ python3 fix_onnx.py <input.onnx> <output.onnx>
14
+
15
+ Requires: pip install onnx numpy
16
+ """
17
+
18
+ import sys
19
+ import onnx
20
+ from onnx import numpy_helper, shape_inference, helper
21
+ import numpy as np
22
+
23
+ if len(sys.argv) < 3:
24
+ print(f"Usage: {sys.argv[0]} <input.onnx> <output.onnx>")
25
+ sys.exit(1)
26
+
27
+ INPUT_MODEL = sys.argv[1]
28
+ OUTPUT_MODEL = sys.argv[2]
29
+
30
+ print(f"Loading model from {INPUT_MODEL}...")
31
+ model = onnx.load(INPUT_MODEL)
32
+ print(f" opset: {[o.version for o in model.opset_import]}")
33
+ print(f" nodes: {len(model.graph.node)}")
34
+
35
+ fixes_applied = []
36
+
37
+ # ── Step 2A: Fix Reshape with -2048 ──────────────────────────────────────────
38
+ print("\n=== Step 2A: Checking Reshape nodes for -2048 ===")
39
+ for node in model.graph.node:
40
+ if node.op_type == "Reshape":
41
+ for init in model.graph.initializer:
42
+ if init.name == node.input[1]:
43
+ shape = numpy_helper.to_array(init).copy()
44
+ if -2048 in shape:
45
+ print(f" FOUND -2048 in initializer '{init.name}': {shape}")
46
+ shape[shape == -2048] = -1
47
+ new_init = numpy_helper.from_array(shape, init.name)
48
+ idx = list(model.graph.initializer).index(init)
49
+ model.graph.initializer.remove(init)
50
+ model.graph.initializer.insert(idx, new_init)
51
+ fixes_applied.append(f"Reshape: fixed -2048 -> -1 in '{init.name}'")
52
+ print(f" FIXED -> {numpy_helper.to_array(new_init)}")
53
+
54
+ # ── Step 2B: Fix Resize node attributes ──────────────────────────────────────
55
+ print("\n=== Step 2B: Checking Resize node attributes ===")
56
+ resize_count = 0
57
+ for node in model.graph.node:
58
+ if node.op_type == "Resize":
59
+ resize_count += 1
60
+ node_fixes = []
61
+ for attr in node.attribute:
62
+ if attr.name == "nearest_mode" and attr.s == b"floor":
63
+ old_val = attr.s.decode()
64
+ attr.s = b"round_prefer_floor"
65
+ node_fixes.append(f"nearest_mode: {old_val} -> round_prefer_floor")
66
+ if attr.name == "coordinate_transformation_mode" and attr.s == b"tf_half_pixel_for_nn":
67
+ old_val = attr.s.decode()
68
+ attr.s = b"half_pixel"
69
+ node_fixes.append(f"coordinate_transformation_mode: {old_val} -> half_pixel")
70
+ if node_fixes:
71
+ print(f" Resize '{node.name}': {', '.join(node_fixes)}")
72
+ fixes_applied.extend([f"Resize '{node.name}': {f}" for f in node_fixes])
73
+ else:
74
+ attrs = {a.name: a.s.decode() if a.type == 3 else a for a in node.attribute}
75
+ print(f" Resize '{node.name}': OK (attrs: {attrs})")
76
+ print(f" Total Resize nodes: {resize_count}")
77
+
78
+ # ── Step 2C: Scan for other issues ───────────────────────────────────────────
79
+ print("\n=== Step 2C: Scanning for other potential TRT issues ===")
80
+
81
+ for node in model.graph.node:
82
+ if node.op_type == "Reshape":
83
+ for init in model.graph.initializer:
84
+ if init.name == node.input[1]:
85
+ shape = numpy_helper.to_array(init)
86
+ negatives = shape[shape < -1]
87
+ if len(negatives) > 0:
88
+ print(f" WARNING: Reshape '{init.name}' still has negative values: {shape}")
89
+
90
+ for node in model.graph.node:
91
+ if node.op_type == "LayerNormalization":
92
+ for attr in node.attribute:
93
+ if attr.name == "stash_type" and attr.i != 1:
94
+ print(f" WARNING: LayerNormalization '{node.name}' has stash_type={attr.i}")
95
+
96
+ op_counts = {}
97
+ for node in model.graph.node:
98
+ op_counts[node.op_type] = op_counts.get(node.op_type, 0) + 1
99
+ print(f" Op type distribution:")
100
+ for op, count in sorted(op_counts.items()):
101
+ print(f" {op}: {count}")
102
+
103
+ # ── Step 3: Replace Resize (nearest 2x) -> ConvTranspose ────────────────────
104
+ #
105
+ # Resize nodes doing nearest-neighbor 2x upsampling cause TRT to split the
106
+ # model into 8+ subgraphs, with GPU<->CPU copies at each boundary.
107
+ # Replacing them with depthwise ConvTranspose (group=channels, 2x2 kernel
108
+ # of all ones, stride 2) produces identical output but TRT compiles it as
109
+ # a single subgraph.
110
+ print("\n=== Step 3: Replacing Resize nodes with ConvTranspose ===")
111
+
112
+ # Run shape inference so we know tensor dimensions
113
+ print(" Running shape inference...")
114
+ try:
115
+ model = shape_inference.infer_shapes(model)
116
+ print(" Shape inference: OK")
117
+ except Exception as e:
118
+ print(f" Shape inference WARNING: {e}")
119
+
120
+ # Build tensor shape lookup
121
+ tensor_shapes = {}
122
+ for vi in list(model.graph.value_info) + list(model.graph.input) + list(model.graph.output):
123
+ if vi.type.tensor_type.HasField('shape'):
124
+ dims = [d.dim_value if d.dim_value > 0 else -1
125
+ for d in vi.type.tensor_type.shape.dim]
126
+ tensor_shapes[vi.name] = dims
127
+
128
+ resize_nodes = [n for n in model.graph.node if n.op_type == "Resize"]
129
+ replaced = 0
130
+
131
+ for resize_node in resize_nodes:
132
+ # Only replace nearest-mode upsampling
133
+ mode = None
134
+ for attr in resize_node.attribute:
135
+ if attr.name == "mode":
136
+ mode = attr.s.decode()
137
+ if mode != "nearest":
138
+ print(f" Skipping '{resize_node.name}' (mode={mode}, not nearest)")
139
+ continue
140
+
141
+ # Get channel count from input shape
142
+ # After shape inference, tensor may be NHWC [B,H,W,C] or NCHW [B,C,H,W].
143
+ # Detect format: if dim[2]==dim[3] it's NCHW (square spatial), channels=dim[1].
144
+ input_name = resize_node.input[0]
145
+ input_shape = tensor_shapes.get(input_name)
146
+ if not input_shape or len(input_shape) != 4:
147
+ print(f" Skipping '{resize_node.name}' (can't determine input shape)")
148
+ continue
149
+
150
+ if input_shape[2] == input_shape[3]:
151
+ channels = input_shape[1] # NCHW
152
+ else:
153
+ channels = input_shape[3] # NHWC
154
+ if channels <= 0:
155
+ print(f" Skipping '{resize_node.name}' (dynamic channels)")
156
+ continue
157
+
158
+ # Create all-ones kernel: [channels, 1, 2, 2] for depthwise ConvTranspose
159
+ kernel_name = f"{resize_node.name}_kernel"
160
+ kernel_data = np.ones((channels, 1, 2, 2), dtype=np.float32)
161
+ model.graph.initializer.append(numpy_helper.from_array(kernel_data, kernel_name))
162
+
163
+ # Create ConvTranspose node (same input/output tensor names)
164
+ ct_node = helper.make_node(
165
+ "ConvTranspose",
166
+ inputs=[input_name, kernel_name],
167
+ outputs=list(resize_node.output),
168
+ name=f"{resize_node.name}_ConvTranspose",
169
+ kernel_shape=[2, 2],
170
+ strides=[2, 2],
171
+ pads=[0, 0, 0, 0],
172
+ group=channels,
173
+ )
174
+
175
+ # Swap in-place
176
+ idx = list(model.graph.node).index(resize_node)
177
+ model.graph.node.remove(resize_node)
178
+ model.graph.node.insert(idx, ct_node)
179
+
180
+ replaced += 1
181
+ fixes_applied.append(f"Resize '{resize_node.name}' -> ConvTranspose (group={channels})")
182
+ print(f" Replaced '{resize_node.name}' -> ConvTranspose (channels={channels})")
183
+
184
+ print(f" Replaced {replaced} Resize nodes")
185
+
186
+ # ── Final validation ─────────────────────────────────────────────────────────
187
+ print("\n=== Final validation ===")
188
+ try:
189
+ model = shape_inference.infer_shapes(model)
190
+ print(" Shape inference: OK")
191
+ except Exception as e:
192
+ print(f" Shape inference WARNING: {e}")
193
+
194
+ try:
195
+ onnx.checker.check_model(model)
196
+ print(" Model validation: PASSED")
197
+ except Exception as e:
198
+ print(f" Model validation WARNING: {e}")
199
+ try:
200
+ onnx.checker.check_model(model, full_check=False)
201
+ print(" Model validation (relaxed): PASSED")
202
+ except Exception as e2:
203
+ print(f" Model validation (relaxed) FAILED: {e2}")
204
+
205
+ # ── Save ─────────────────────────────────────────────────────────────────────
206
+ print(f"\n=== Saving to {OUTPUT_MODEL} ===")
207
+ onnx.save(model, OUTPUT_MODEL)
208
+ print(f" Saved. Total fixes applied: {len(fixes_applied)}")
209
+ for f in fixes_applied:
210
+ print(f" - {f}")
211
+
212
+ remaining_resize = sum(1 for n in model.graph.node if n.op_type == "Resize")
213
+ total_ct = sum(1 for n in model.graph.node if n.op_type == "ConvTranspose")
214
+ print(f"\n Final: {len(model.graph.node)} nodes, {remaining_resize} Resize, {total_ct} ConvTranspose")
215
+ print("\nDone.")