Skip to content

Commit 30adf05

Browse files
ajrasanecodex
andcommitted
Address PETR example review feedback
Co-Authored-By: Codex <codex@openai.com> Signed-off-by: ajrasane <131806219+ajrasane@users.noreply.github.com>
1 parent 110414e commit 30adf05

14 files changed

Lines changed: 480 additions & 518 deletions

.pre-commit-config.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,9 @@ repos:
127127
examples/llm_eval/mmlu.py|
128128
examples/llm_eval/modeling.py|
129129
examples/onnx_ptq/far3d/evaluate.py|
130+
examples/onnx_ptq/petr/evaluate.py|
131+
examples/onnx_ptq/petr/prepare_sweep_metadata.py|
132+
examples/onnx_ptq/trt_runner.py|
130133
examples/llm_qat/train.py|
131134
examples/llm_sparsity/weight_sparsity/finetune.py|
132135
examples/specdec_bench/specdec_bench/models/specbench_medusa.py|

LICENSE

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,7 @@ the following copyright holders, licensed under the Apache License, Version 2.0
224224
Copyright (c) 2024 Heming Xia
225225
Copyright 2025 The Qwen team, Alibaba Group and the HuggingFace Inc. team
226226
Copyright (c) OpenMMLab. All rights reserved.
227+
Copyright (c) 2022 megvii-model. All Rights Reserved.
227228

228229
Licensed under the Apache License, Version 2.0 (the "License"); you may not
229230
use these files except in compliance with the License. You may obtain a copy

examples/onnx_ptq/far3d/evaluate.py

Lines changed: 6 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,10 @@
2121
import argparse
2222
import importlib
2323
import os
24+
import sys
2425
import warnings
26+
from pathlib import Path
2527

26-
import tensorrt as trt
2728
import torch
2829
from mmcv import Config, DictAction
2930
from mmcv.utils import import_modules_from_strings
@@ -33,115 +34,9 @@
3334
from projects.mmdet3d_plugin.datasets.builder import build_dataloader
3435
from tqdm import tqdm
3536

36-
TRT_TO_TORCH = {
37-
trt.DataType.FLOAT: torch.float32,
38-
trt.DataType.HALF: torch.float16,
39-
trt.DataType.INT8: torch.int8,
40-
trt.DataType.INT32: torch.int32,
41-
trt.DataType.BOOL: torch.bool,
42-
trt.DataType.UINT8: torch.uint8,
43-
}
44-
if int(trt.__version__.split(".")[0]) >= 10:
45-
TRT_TO_TORCH[trt.DataType.INT64] = torch.int64
46-
47-
TRT_LOGGER = trt.Logger(trt.Logger.WARNING)
48-
trt.init_libnvinfer_plugins(TRT_LOGGER, "")
49-
50-
51-
def aligned_tensor(shape, dtype, device, alignment=256):
52-
element_size = torch.empty((), dtype=dtype).element_size()
53-
element_count = int(torch.tensor(shape).prod().item())
54-
storage = torch.empty(element_count + alignment // element_size, dtype=dtype, device=device)
55-
offset_bytes = (-storage.data_ptr()) % alignment
56-
offset = offset_bytes // element_size
57-
return storage[offset : offset + element_count].view(shape)
58-
59-
60-
class TensorRTRunner:
61-
def __init__(self, engine_path, state_names=()):
62-
with open(engine_path, "rb") as engine_file:
63-
engine_bytes = engine_file.read()
64-
self.engine = trt.Runtime(TRT_LOGGER).deserialize_cuda_engine(engine_bytes)
65-
if self.engine is None:
66-
raise RuntimeError(f"Failed to deserialize {engine_path}")
67-
self.context = self.engine.create_execution_context()
68-
if self.context is None:
69-
raise RuntimeError(f"Failed to create an execution context for {engine_path}")
70-
self.tensor_names = [
71-
self.engine.get_tensor_name(index) for index in range(self.engine.num_io_tensors)
72-
]
73-
self.input_shapes = {}
74-
self.output_shapes = {}
75-
self.tensor_dtypes = {}
76-
for name in self.tensor_names:
77-
shape = tuple(self.engine.get_tensor_shape(name))
78-
dtype = TRT_TO_TORCH[self.engine.get_tensor_dtype(name)]
79-
self.tensor_dtypes[name] = dtype
80-
if self.engine.get_tensor_mode(name) == trt.TensorIOMode.INPUT:
81-
self.input_shapes[name] = shape
82-
else:
83-
self.output_shapes[name] = shape
84-
85-
self.state = {}
86-
for base_name in state_names:
87-
name = self.resolve_name(base_name)
88-
if name in self.input_shapes:
89-
tensor = aligned_tensor(self.input_shapes[name], self.tensor_dtypes[name], "cuda")
90-
tensor.zero_()
91-
self.state[name] = tensor
92-
self.context.set_tensor_address(name, tensor.data_ptr())
93-
if self.state:
94-
torch.cuda.synchronize()
95-
96-
def resolve_name(self, base_name):
97-
if base_name in self.tensor_names:
98-
return base_name
99-
suffixed_name = f"{base_name}.1"
100-
return suffixed_name if suffixed_name in self.tensor_names else base_name
101-
102-
def reset_state(self):
103-
for tensor in self.state.values():
104-
tensor.zero_()
105-
106-
def prepare_input(self, name, inputs):
107-
shape = self.input_shapes[name]
108-
base_name = name.rsplit(".1", maxsplit=1)[0] if name.endswith(".1") else name
109-
if base_name not in inputs:
110-
raise KeyError(f"Missing TensorRT input {base_name}")
111-
value = inputs[base_name].to(device="cuda", dtype=self.tensor_dtypes[name])
112-
if tuple(value.shape) != shape:
113-
if tuple(value.shape[1:]) == shape:
114-
value = value.squeeze(0)
115-
elif tuple(shape[1:]) == tuple(value.shape):
116-
value = value.unsqueeze(0)
117-
else:
118-
raise ValueError(
119-
f"Input {base_name} has shape {tuple(value.shape)}, expected {shape}"
120-
)
121-
return value
122-
123-
def __call__(self, stream, **inputs):
124-
input_buffers = {}
125-
for name, shape in self.input_shapes.items():
126-
if name in self.state:
127-
continue
128-
value = self.prepare_input(name, inputs)
129-
buffer = aligned_tensor(shape, value.dtype, value.device)
130-
buffer.copy_(value)
131-
input_buffers[name] = buffer
132-
self.context.set_tensor_address(name, buffer.data_ptr())
133-
134-
outputs = {}
135-
for name, shape in self.output_shapes.items():
136-
output = aligned_tensor(shape, self.tensor_dtypes[name], "cuda")
137-
outputs[name] = output
138-
self.context.set_tensor_address(name, output.data_ptr())
139-
140-
if not self.context.execute_async_v3(stream.cuda_stream):
141-
raise RuntimeError("TensorRT execution failed")
142-
stream.synchronize()
143-
return outputs
37+
sys.path.insert(0, str(Path(__file__).resolve().parents[3]))
14438

39+
from examples.onnx_ptq.trt_runner import TensorRTRunner
14540

14641
STATE_NAMES = (
14742
"memory_embedding",
@@ -154,8 +49,7 @@ def __call__(self, stream, **inputs):
15449

15550
class Far3DDecoderRunner(TensorRTRunner):
15651
def __init__(self, engine_path, input_callback=None):
157-
super().__init__(engine_path, STATE_NAMES)
158-
self.input_callback = input_callback
52+
super().__init__(engine_path, STATE_NAMES, input_callback)
15953
self.scene_token = None
16054
self.timestamp_offset = None
16155

@@ -175,16 +69,6 @@ def __call__(self, stream, img_metas, timestamp, **inputs):
17569
device="cuda",
17670
)
17771
inputs["timestamp"] = (timestamp - self.timestamp_offset).float()
178-
if self.input_callback:
179-
calibration_inputs = {}
180-
for name in self.input_shapes:
181-
base_name = name.rsplit(".1", maxsplit=1)[0] if name.endswith(".1") else name
182-
if name in self.state:
183-
value = self.state[name]
184-
else:
185-
value = self.prepare_input(name, inputs)
186-
calibration_inputs[base_name] = value
187-
self.input_callback(calibration_inputs)
18872
outputs = super().__call__(stream, **inputs)
18973
for base_name in STATE_NAMES:
19074
input_name = self.resolve_name(base_name)
@@ -287,7 +171,7 @@ def main():
287171
}
288172
}
289173
)
290-
if args.max_samples is not None and len(outputs) == args.max_samples:
174+
if args.max_samples is not None and len(outputs) >= args.max_samples:
291175
break
292176

293177
if len(outputs) < len(dataset):

examples/onnx_ptq/far3d/quantize.py

Lines changed: 9 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -14,36 +14,19 @@
1414
# limitations under the License.
1515

1616
import argparse
17-
import re
17+
import sys
1818
from pathlib import Path
1919

2020
import numpy as np
21-
import onnx
22-
from onnxruntime.quantization.calibrate import CalibrationDataReader
2321

2422
from modelopt.onnx.quantization import quantize
25-
from modelopt.onnx.utils import topologically_sort_graph_nodes
2623

27-
28-
class FileCalibrationReader(CalibrationDataReader):
29-
def __init__(self, calibration_dir, pattern):
30-
self.batch_paths = sorted(Path(calibration_dir).glob(pattern))
31-
if not self.batch_paths:
32-
raise ValueError(f"No {pattern} calibration batches found in {calibration_dir}")
33-
self.rewind()
34-
35-
def get_next(self):
36-
batch_path = next(self._iterator, None)
37-
return None if batch_path is None else self.load(batch_path)
38-
39-
def get_first(self):
40-
return self.load(self.batch_paths[0])
41-
42-
def rewind(self):
43-
self._iterator = iter(self.batch_paths)
44-
45-
def load(self, batch_path):
46-
raise NotImplementedError
24+
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
25+
from quantization_utils import (
26+
FileCalibrationReader,
27+
NpzCalibrationReader,
28+
find_vovnet_nodes_to_exclude,
29+
)
4730

4831

4932
class EncoderCalibrationReader(FileCalibrationReader):
@@ -54,42 +37,6 @@ def load(self, batch_path):
5437
return {"img": np.load(batch_path)}
5538

5639

57-
class DecoderCalibrationReader(FileCalibrationReader):
58-
def __init__(self, calibration_dir, onnx_path):
59-
graph = onnx.load(onnx_path, load_external_data=False).graph
60-
self.input_dtypes = {
61-
value.name: onnx.helper.tensor_dtype_to_np_dtype(value.type.tensor_type.elem_type)
62-
for value in graph.input
63-
}
64-
super().__init__(calibration_dir, "*.npz")
65-
66-
def load(self, batch_path):
67-
with np.load(batch_path) as batch:
68-
missing = self.input_dtypes.keys() - batch.files
69-
if missing:
70-
raise ValueError(f"{batch_path} is missing decoder inputs: {sorted(missing)}")
71-
return {
72-
name: batch[name].astype(dtype, copy=False)
73-
for name, dtype in self.input_dtypes.items()
74-
}
75-
76-
77-
def find_encoder_nodes_to_exclude(onnx_path):
78-
graph = onnx.load(onnx_path, load_external_data=False).graph
79-
topologically_sort_graph_nodes(graph)
80-
81-
excluded = set()
82-
downstream_tensors = set()
83-
for node in graph.node:
84-
is_osa = "OSA4_5" in node.name
85-
is_downstream = any(name in downstream_tensors for name in node.input)
86-
if is_osa or is_downstream:
87-
excluded.add(node.name)
88-
if "lateral_convs" in node.name or (is_downstream and not is_osa):
89-
downstream_tensors.update(node.output)
90-
return sorted(excluded)
91-
92-
9340
def parse_args():
9441
parser = argparse.ArgumentParser(description="Quantize the FAR3D ONNX models")
9542
parser.add_argument("--encoder-onnx", required=True, help="Path to far3d.encoder.onnx")
@@ -112,9 +59,7 @@ def quantize_encoder(args):
11259
encoder_dir = Path(args.calibration_dir)
11360
if (encoder_dir / "encoder").is_dir():
11461
encoder_dir /= "encoder"
115-
excluded_nodes = [
116-
rf"^{re.escape(name)}$" for name in find_encoder_nodes_to_exclude(args.encoder_onnx)
117-
]
62+
excluded_nodes = find_vovnet_nodes_to_exclude(args.encoder_onnx)
11863
print(f"Excluding {len(excluded_nodes)} accuracy-sensitive nodes from quantization")
11964
quantize(
12065
onnx_path=args.encoder_onnx,
@@ -133,7 +78,7 @@ def quantize_decoder(args):
13378
quantize(
13479
onnx_path=args.decoder_onnx,
13580
quantize_mode=args.quantization_mode,
136-
calibration_data_reader=DecoderCalibrationReader(decoder_dir, args.decoder_onnx),
81+
calibration_data_reader=NpzCalibrationReader(decoder_dir, args.decoder_onnx),
13782
calibration_method="max",
13883
calibration_eps=["cuda:0", "cpu"],
13984
high_precision_dtype="fp16" if args.quantization_mode == "fp8" else "fp32",

examples/onnx_ptq/petr/Dockerfile

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
11
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
22
# SPDX-License-Identifier: Apache-2.0
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
315

416
FROM nvcr.io/nvidia/pytorch:26.07-py3
517

@@ -12,9 +24,9 @@ RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-ins
1224

1325
ENV UV_PYTHON_INSTALL_DIR=/opt/python
1426

15-
RUN python -m pip install --no-cache-dir uv && \
16-
uv python install 3.8 && \
17-
uv venv --seed --python 3.8 /opt/petr
27+
RUN python -m pip install --no-cache-dir uv==0.12.3 && \
28+
uv python install 3.8.20 && \
29+
uv venv --seed --python 3.8.20 /opt/petr
1830

1931
COPY examples/onnx_ptq/petr/requirements*.txt /tmp/petr-requirements/
2032
RUN env -u PIP_CONSTRAINT /opt/petr/bin/python -m pip install --no-cache-dir \

0 commit comments

Comments
 (0)