Skip to content

Commit 68f510a

Browse files
Exporters: restore ONNX Runtime's quantizer after ModelOpt, follow main's module moves
ModelOpt patches ONNX Runtime's calibrators and QDQ quantizer in place for its own calibration and never restores them, so an `OnnxRuntimeQuantizer` run later in the same process failed ("Quantization parameters are not specified for param ..."). The quantizer now puts back every attribute ModelOpt adds or replaces. `onnx_feed` moved to `exporters/utils.py` on main, and the ModelOpt test runs the export through `ExportedModel` like the other ONNX tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent dbe27d7 commit 68f510a

2 files changed

Lines changed: 42 additions & 13 deletions

File tree

‎src/transformers/exporters/quantizers/quantizer_modelopt.py‎

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,15 @@
1818
import os
1919
import tempfile
2020
from collections.abc import Iterable
21+
from contextlib import contextmanager
2122
from dataclasses import dataclass
2223
from typing import Any
2324

2425
from ...utils.import_utils import is_modelopt_available, is_onnxscript_available
2526
from ..configs import ExportFormat
27+
from ..utils import onnx_feed
2628
from .base import CalibrationSet, ExportQuantizer, QuantizationConfigMixin
2729
from .quantizer_onnxruntime import _FeedReader
28-
from .utils import onnx_feed
2930

3031

3132
if is_onnxscript_available():
@@ -35,6 +36,35 @@
3536
from modelopt.onnx.quantization import quantize as modelopt_quantize
3637

3738

39+
@contextmanager
40+
def _restored_onnxruntime_quantization():
41+
"""ModelOpt patches ONNX Runtime's calibrators and QDQ quantizer in place for its own calibration
42+
(`modelopt.onnx.quantization.ort_patches`) and leaves them patched, which breaks a later `OnnxRuntimeQuantizer` in
43+
the same process. Put back every attribute it adds or replaces on them."""
44+
from onnxruntime.quantization import base_quantizer, calibrate, qdq_quantizer
45+
46+
owners = (
47+
calibrate,
48+
qdq_quantizer,
49+
calibrate.CalibraterBase,
50+
calibrate.MinMaxCalibrater,
51+
calibrate.HistogramCalibrater,
52+
calibrate.HistogramCollector,
53+
qdq_quantizer.QDQQuantizer,
54+
base_quantizer.BaseQuantizer,
55+
)
56+
snapshots = [(owner, dict(vars(owner))) for owner in owners]
57+
try:
58+
yield
59+
finally:
60+
for owner, attributes in snapshots:
61+
for name in set(vars(owner)) - set(attributes):
62+
delattr(owner, name)
63+
for name, value in attributes.items():
64+
if vars(owner).get(name) is not value:
65+
setattr(owner, name, value)
66+
67+
3868
@dataclass
3969
class ModelOptOnnxConfig(QuantizationConfigMixin):
4070
"""
@@ -96,13 +126,14 @@ def quantize(
96126
path = os.path.join(tmp, "model.onnx")
97127
quantized_path = os.path.join(tmp, "quantized.onnx")
98128
onnx_ir.save(model, path, external_data="model.onnx.data")
99-
modelopt_quantize(
100-
path,
101-
calibration_data_reader=_FeedReader(calibration),
102-
output_path=quantized_path,
103-
use_external_data_format=True,
104-
**self.config.to_kwargs(),
105-
)
129+
with _restored_onnxruntime_quantization():
130+
modelopt_quantize(
131+
path,
132+
calibration_data_reader=_FeedReader(calibration),
133+
output_path=quantized_path,
134+
use_external_data_format=True,
135+
**self.config.to_kwargs(),
136+
)
106137
# Load the weights into memory before the temporary directory goes away
107138
quantized = onnx_ir.load(quantized_path)
108139
onnx_ir.external_data.load_to_model(quantized)

‎tests/exporters/test_quantization.py‎

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -542,11 +542,9 @@ def test_modelopt_onnx(self, family):
542542
ModelOptOnnxConfig(calibration_eps=["cpu"], high_precision_dtype="fp32"),
543543
calibration_dataset=[copy.deepcopy(inputs)],
544544
)
545-
program = (
546-
OnnxExporter().export(model, copy.deepcopy(inputs), OnnxConfig(dynamic=False, quantizer=quantizer)).program
547-
)
548-
self.assertTrue(_has_onnx_quantize_ops(program))
549-
outputs = _run_onnx_program(program, copy.deepcopy(inputs))
545+
exported = OnnxExporter().export(model, copy.deepcopy(inputs), OnnxConfig(dynamic=False, quantizer=quantizer))
546+
self.assertTrue(any(node.op_type == "QuantizeLinear" for node in exported.program.graph))
547+
outputs = get_leaf_tensors(exported(**copy.deepcopy(inputs)))
550548
self.assertTrue(outputs)
551549
self.assertTrue(all(o.isfinite().all() for o in outputs.values() if o.is_floating_point()))
552550

0 commit comments

Comments
 (0)