-
Notifications
You must be signed in to change notification settings - Fork 34.2k
Support quantization in Dynamo, ONNX and ET exporters #47747
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
8dc262e
23f08a0
6278a02
29ebbb6
abe4c2b
bf8ee51
daee34e
a18d2aa
f049a96
57c9d6b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -16,6 +16,7 @@ | |
|
|
||
| from __future__ import annotations | ||
|
|
||
| import dataclasses | ||
| from abc import ABC, abstractmethod | ||
| from collections.abc import MutableMapping | ||
| from typing import TYPE_CHECKING | ||
|
|
@@ -25,7 +26,7 @@ | |
| from ..utils import logging | ||
| from ..utils.import_utils import _is_package_available, is_torch_available | ||
| from .configs import ExportConfigMixin | ||
| from .utils import decompose_for_generation | ||
| from .utils import capture_calibration_inputs, decompose_for_generation | ||
|
|
||
|
|
||
| logger = logging.get_logger(__name__) | ||
|
|
@@ -168,6 +169,14 @@ def export_for_generation( | |
| classic single-token step (see [`~exporters.utils.decompose_for_generation`]). Only | ||
| stays dynamic under a dynamic-shape export (`config.dynamic=True`). | ||
| Quantization calibration: when a single `config` is passed (not a per-component dict) and it | ||
| carries a `quantizer`, its `calibration_dataset` is read as **generate** kwarg dicts (same level | ||
| as `sample_inputs` here) and fanned out — each sample is run through the decomposition to produce | ||
| a per-component calibration set that replaces each component's `config.calibration_dataset` | ||
| (per-graph forward kwargs). Leave it `None` to fall back to a single pass on each component's own | ||
| sample inputs (see [`DynamoConfig.calibration_dataset`]). A per-component `config` dict is left | ||
| untouched — set each component's `calibration_dataset` to its own forward kwargs directly. | ||
| Returns: | ||
| `dict[str, Any]`: `{component_name: backend_specific_artifact}` — same keys as | ||
| [`~exporters.utils.decompose_for_generation`]. Values are whatever | ||
|
|
@@ -189,13 +198,28 @@ def export_for_generation( | |
| f"Expected one entry per component: {sorted(components)}." | ||
| ) | ||
| configs = config | ||
| calibration = {} | ||
| else: | ||
| configs = dict.fromkeys(components, config) | ||
| # a single config's `calibration_dataset` is generate-level here: fan it out into a | ||
| # per-component (forward-level) calibration set via the decomposition capture | ||
| calibration = {} | ||
| if getattr(config, "calibration_dataset", None): | ||
| calibration = capture_calibration_inputs( | ||
| model, | ||
| config.calibration_dataset, | ||
| generation_config=generation_config, | ||
| multi_token_decode=multi_token_decode, | ||
| ) | ||
|
|
||
| exported: dict[str, object] = {} | ||
| for name, (submodel, subinputs) in components.items(): | ||
| component_config = configs[name] | ||
| component_calibration = calibration.get(name) | ||
| if component_calibration is not None: | ||
| component_config = dataclasses.replace(component_config, calibration_dataset=component_calibration) | ||
| try: | ||
| exported[name] = self.export(submodel, subinputs, config=configs[name]) | ||
| exported[name] = self.export(submodel, subinputs, config=component_config) | ||
|
Comment on lines
+217
to
+222
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Each component is calibrated/converted independently here, so prefill and decode end up with independent scales/zero-points. Where the two exchange KV-cache tensors as graph I/O (eg. static cache path) the cache bytes prefill writes are read back by decode under a different scale, which silently corrupts the values. For reference, the QNN LLM flow in ET handles this with a third calibrate-only graph: Might be worth putting an optional hook here, something like a post-pass over the converted components that unifies encodings on shared tensors rather than solving it per backend.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i see what you mean, hmm then maybe we can calibrate the multi-token decode graph on both since it can accept prefill and decode inputs. |
||
| except Exception as e: | ||
| raise RuntimeError( | ||
| f"{type(self).__name__}.export failed on component '{name}' " | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.