guruansh commited on
Commit
604515d
·
verified ·
1 Parent(s): 4f8df8d

v2: fix ONNX dtype mismatch that blocked TensorRT parsing; add 5070Ti/5090 benchmarks

Browse files
Files changed (1) hide show
  1. README.md +52 -27
README.md CHANGED
@@ -19,8 +19,8 @@ An ONNX export of the **vision-encoder tail** of
19
  built so the model can be compiled with **TensorRT without breaking NaFlex's
20
  native-aspect-ratio handling**.
21
 
22
- Measured **1.34× faster** than PyTorch fp16 eager on an NVIDIA L4 (67.0 → 89.7
23
- crops/s end-to-end) with **no measurable accuracy loss**.
24
 
25
  ---
26
 
@@ -45,21 +45,23 @@ patch grid like `[36, 16]`, a square one gets `[24, 24]`. Crucially, the
45
  `(B, 576, 768)`. What varies is the *value* of `spatial_shapes`, which drives a
46
  per-image interpolation of the learned position-embedding grid.
47
 
48
- That interpolation is **data-dependent control flow**. Tracing it bakes in
 
49
  whichever aspect ratios happened to be in the export batch, so every other
50
- aspect ratio then silently receives the wrong position embeddings the model
51
- still runs, and still returns plausible-looking embeddings.
 
52
 
53
  Keeping the embeddings in PyTorch and exporting only the static tail avoids this
54
  entirely. It costs almost nothing: the embeddings block measured **2.7% of total
55
  runtime**.
56
 
57
- **Verification.** Two checks were run before publishing:
58
 
59
  | check | result |
60
  |---|---|
61
- | split path vs full `get_image_features` | max abs diff **0.000000**, cosine **0.99999982** |
62
- | ONNX vs PyTorch on aspect ratios **not in the export batch** | cosine min **0.999983** |
63
 
64
  The second is the one that matters — a baked-in interpolation passes on traced
65
  shapes and fails on held-out ones.
@@ -72,7 +74,7 @@ shapes and fails on held-out ones.
72
  | input | `attention_mask` | **int32** | `[batch, 576]` |
73
  | output | `pooled` | **float16** | `[batch, 1152]` |
74
 
75
- opset 17 · 2743 nodes · 854 MB · `batch` is dynamic
76
 
77
  `attention_mask` is the processor's `pixel_attention_mask` (1 = real patch,
78
  0 = padding; 551–576 of the 576 slots are typically real).
@@ -80,6 +82,7 @@ opset 17 · 2743 nodes · 854 MB · `batch` is dynamic
80
  **Bind these dtypes exactly.** TensorRT's `set_tensor_address` takes a raw
81
  pointer and performs *no* dtype checking — binding fp32 buffers to this engine
82
  yields normal-looking latencies and non-finite outputs, with no error raised.
 
83
 
84
  ## Usage
85
 
@@ -117,11 +120,11 @@ emb = pooled / np.linalg.norm(pooled, axis=-1, keepdims=True) # L2 normalise
117
 
118
  ```bash
119
  trtexec --onnx=siglip2_tail_fp16.onnx \
120
- --saveEngine=siglip2_tail_fp16_b8.plan \
121
  --shapes=hidden_states:8x576x1152,attention_mask:8x576
122
  ```
123
 
124
- Builds in **~56 s** on an L4.
125
 
126
  > A TensorRT `.plan` is compiled for **one GPU architecture, one TensorRT
127
  > version, and one batch profile**. No engine is shipped here on purpose — this
@@ -134,35 +137,45 @@ Builds in **~56 s** on an L4.
134
  SigLIP2 vision tower alone, CUDA-event timed, inputs resident on GPU,
135
  `max_num_patches=576`. "crops/s" = images encoded per second.
136
 
137
- | GPU | PyTorch fp16 eager | PyTorch fp16 + `torch.compile` | **TensorRT FP16 (this ONNX)** |
138
- |---|---|---|---|
139
- | **NVIDIA L4** (sm_89) | 67.0 | 70.3 | **89.7 end-to-end / 91.1 engine-only** |
140
- | **Tesla T4** (sm_75) | 33.7 | 36.9 | **36.8 end-to-end / 37.3 engine-only** |
 
 
 
 
 
141
 
142
- **The ranking is architecture-dependent.** On L4, TensorRT wins by 34%. On T4 it
143
- merely ties `torch.compile` and an INT8 PyTorch path measured *faster* there
144
- (39.9 crops/s). Benchmark on your own target; do not assume.
 
 
145
 
146
- torch 2.7.0+cu128 · TensorRT 11.2.1.2 · transformers 5.15.1 · driver 580.126.09
147
 
148
  ## Accuracy
149
 
150
  Evaluated on **1016 person crops** (COCO-derived) against **21,450**
151
- image–prompt similarity scores, versus the PyTorch fp16 reference:
152
 
153
  | | value |
154
  |---|---|
155
- | embedding cosine vs PyTorch fp16 | **min 0.99757**, mean 0.99946 |
156
- | mean absolute error on similarity scores | **0.00058** |
157
- | downstream binary decisions unchanged | **99.68%** |
158
  | ranking accuracy (best positive vs best negative prompt) | **unchanged, ±0.00 pp** |
159
 
 
 
 
160
  This is fp16 numerics throughout — not quantisation — so the residual difference
161
  is only kernel selection and accumulation order.
162
 
163
- For calibration: running the *same* fp16 maths on two different GPUs (L4 vs an
164
- RTX 3050) already moves **0.019%** of those decisions. The TensorRT difference
165
- is small but above that floor.
166
 
167
  ## Limitations
168
 
@@ -171,7 +184,19 @@ is small but above that floor.
171
  - Exported from **revision `cc24074f...`** of the base model.
172
  - No text tower — encode prompts with the original model and cache them.
173
  - Not evaluated beyond person crops; the base model's own limitations
174
- (attribute binding, counting, gaze, expression) are unchanged by this export.
 
 
 
 
 
 
 
 
 
 
 
 
175
 
176
  ## License and attribution
177
 
 
19
  built so the model can be compiled with **TensorRT without breaking NaFlex's
20
  native-aspect-ratio handling**.
21
 
22
+ Benchmarked on four GPUs. **Up to 2.08× faster** than PyTorch fp16, with no
23
+ measurable accuracy loss.
24
 
25
  ---
26
 
 
45
  `(B, 576, 768)`. What varies is the *value* of `spatial_shapes`, which drives a
46
  per-image interpolation of the learned position-embedding grid.
47
 
48
+ That interpolation is **data-dependent control flow** a Python loop calling
49
+ `F.interpolate` with a size read out of the tensor. Tracing it bakes in
50
  whichever aspect ratios happened to be in the export batch, so every other
51
+ aspect ratio then silently receives the wrong position embeddings. The model
52
+ still runs, still returns finite, normalised, plausible-looking vectors. Nothing
53
+ errors.
54
 
55
  Keeping the embeddings in PyTorch and exporting only the static tail avoids this
56
  entirely. It costs almost nothing: the embeddings block measured **2.7% of total
57
  runtime**.
58
 
59
+ **Verification.** Two gates, both in `export_tail.py` (`--verify`):
60
 
61
  | check | result |
62
  |---|---|
63
+ | split path vs full `get_image_features` | max abs diff **0.00000000**, cosine **0.99999988** |
64
+ | ONNX vs PyTorch on aspect ratios **not in the export batch** | cosine min **0.999577** |
65
 
66
  The second is the one that matters — a baked-in interpolation passes on traced
67
  shapes and fails on held-out ones.
 
74
  | input | `attention_mask` | **int32** | `[batch, 576]` |
75
  | output | `pooled` | **float16** | `[batch, 1152]` |
76
 
77
+ opset 17 · 854 MB · `batch` is dynamic · all 445 initializers are fp16
78
 
79
  `attention_mask` is the processor's `pixel_attention_mask` (1 = real patch,
80
  0 = padding; 551–576 of the 576 slots are typically real).
 
82
  **Bind these dtypes exactly.** TensorRT's `set_tensor_address` takes a raw
83
  pointer and performs *no* dtype checking — binding fp32 buffers to this engine
84
  yields normal-looking latencies and non-finite outputs, with no error raised.
85
+ Query `engine.get_tensor_dtype()` rather than assuming.
86
 
87
  ## Usage
88
 
 
120
 
121
  ```bash
122
  trtexec --onnx=siglip2_tail_fp16.onnx \
123
+ --saveEngine=engine.plan \
124
  --shapes=hidden_states:8x576x1152,attention_mask:8x576
125
  ```
126
 
127
+ Builds in **51–61 s** across the GPUs tested.
128
 
129
  > A TensorRT `.plan` is compiled for **one GPU architecture, one TensorRT
130
  > version, and one batch profile**. No engine is shipped here on purpose — this
 
137
  SigLIP2 vision tower alone, CUDA-event timed, inputs resident on GPU,
138
  `max_num_patches=576`. "crops/s" = images encoded per second.
139
 
140
+ | GPU | arch | PyTorch fp16 | + `torch.compile` | **TensorRT (end-to-end)** | TensorRT (engine only) | speedup |
141
+ |---|---|---|---|---|---|---|
142
+ | **Tesla T4** | sm_75 | 33.7 | 36.9 | 36.8 | 37.3 | **1.09×** |
143
+ | **NVIDIA L4** | sm_89 | 67.0 | 70.3 | **89.7** | 91.1 | **1.34×** |
144
+ | **RTX 5070 Ti** | sm_120 | 52.9 | 57.2 | **109.8** | 115.0 | **2.08×** |
145
+ | **RTX 5090** | sm_120 | 232.9 | 263.4 | **457.1** | 497.2 | **1.96×** |
146
+
147
+ "end-to-end" includes the PyTorch NaFlex front end; "engine only" is the ONNX
148
+ graph alone.
149
 
150
+ **The benefit is strongly architecture-dependent.** On T4 TensorRT merely ties
151
+ `torch.compile`; on consumer Blackwell it roughly doubles throughput. Benchmark
152
+ on your own target rather than assuming. Note also that the RTX 5070 Ti is
153
+ *slower* than an L4 in plain PyTorch fp16 (52.9 vs 67.0) yet *faster* once
154
+ compiled with TensorRT (109.8 vs 89.7).
155
 
156
+ Stacks: torch 2.7.0–2.7.1 + cu128 · TensorRT 11.2.1.2 · transformers 5.15.1
157
 
158
  ## Accuracy
159
 
160
  Evaluated on **1016 person crops** (COCO-derived) against **21,450**
161
+ image–prompt similarity scores, versus the PyTorch fp16 reference (L4 engine):
162
 
163
  | | value |
164
  |---|---|
165
+ | embedding cosine vs PyTorch fp16 | mean **0.999736**, min 0.997570 |
166
+ | mean absolute change in similarity score | **0.00058** (scores span ~0–0.24) |
167
+ | signed mean change | **+0.000003** — no systematic bias |
168
  | ranking accuracy (best positive vs best negative prompt) | **unchanged, ±0.00 pp** |
169
 
170
+ Per-batch sanity checks on the other GPUs agreed: cosine min **0.999967**
171
+ (RTX 5090) and **0.999974** (RTX 5070 Ti) against PyTorch on identical inputs.
172
+
173
  This is fp16 numerics throughout — not quantisation — so the residual difference
174
  is only kernel selection and accumulation order.
175
 
176
+ For calibration: running the *same* fp16 maths on two different GPUs already
177
+ moves the scores by **0.000088** on average. The TensorRT difference is small
178
+ but above that floor.
179
 
180
  ## Limitations
181
 
 
184
  - Exported from **revision `cc24074f...`** of the base model.
185
  - No text tower — encode prompts with the original model and cache them.
186
  - Not evaluated beyond person crops; the base model's own limitations
187
+ (small objects, attribute binding, counting, gaze, expression) are unchanged
188
+ by this export.
189
+
190
+ ## Changelog
191
+
192
+ - **v2** — re-exported natively in fp16. The first upload was produced by
193
+ converting an fp32 export with `onnxconverter_common.float16`, which left some
194
+ constants as Float; TensorRT ≥ 11 is strict about mixed types and **refused to
195
+ parse it** (`ElementWiseOperation SUB must have same input types`). This
196
+ version traces directly in fp16, so all 445 initializers are fp16 and the
197
+ graph builds cleanly. Added RTX 5070 Ti and RTX 5090 benchmarks.
198
+ - **v1** — initial release (T4 and L4 benchmarks). **Do not use — TensorRT
199
+ cannot parse it.**
200
 
201
  ## License and attribution
202