Repository navigation
Backend-neutral quantizer: core ATen op quantizers and PT2E composition (#24769) - #24769
mcremon-meta wants to merge 2 commits into
Conversation
🔗 Helpful Links🧪 See artifacts and rendered test results at hud.pytorch.org/pr/pytorch/executorch/24769
Note: Links to docs will display an error until the docs builds have been completed. ✅ You can merge normally! (1 Unrelated Failure)As of commit 1ec7bad with merge base e0015e1 ( BROKEN TRUNK - The following job failed but were present on the merge base:👉 Rebase onto the `viable/strict` branch to avoid these failures
This comment was automatically generated by Dr. CI and updates every 15 minutes. |
|
@mcremon-meta has exported this pull request. If you are a Meta employee, you can view the originating Diff in D129194623. |
This PR needs a
|
Summary: This should have been part of #24720, which added `QuantAbsorptionPass` with the other fused_quant graph passes. It was left out of that PR by mistake. By default, `QuantAbsorptionPass` now only runs the rewrite that is never less accurate. The docstring explains why the other two modes are opt-in. - Output side (unchanged, always on): the producer writes its output directly in the downstream quantize's qparams. The value still ends up on that grid, rounded once instead of twice, so each element is at least as close to the float result. It isn't bit-exact with the q/dq graph, though. - Input side (now opt-in, `absorb_into_inputs`): the consumer reads values on the dequantize's grid instead of the quantize's. Dropping the second rounding is more accurate on average, but not for every value. - Forked dequantize (already opt-in, `absorb_with_fork`): its float users see values on the quantize's grid instead of the producer's, which can be coarser. Only the docstring changes. With input-side absorption off, a pair that only the input side would remove is left to `ReplaceDequantQuantWithRequantize`, which turns it into a `fused_quant.requantize`. Differential Revision: D130466673
…on (#24769) Summary: This diff makes the quantizer in `frontends/fused_quant/quantizer/` usable by any backend: - `get_core_aten_quantizers(config, weighted_config)` in `quantizer/quantizer.py`: the op quantizers every backend can share, with the contract below. `arg1_is_scalar` is the node filter its scalar add/sub/mul variants use. - `FusedQuantQuantizer` now composes with other PT2E quantizers on a first-claim-wins basis. Its annotations set `_annotated` (the flag other quantizers check), and a `fused_quant_owned` node flag lets `fuse()` skip nodes that another quantizer claimed. - `FusedQuantQuantizer.fuse` first repairs the `convert_pt2e` output: it canonicalizes torchao `quantize_affine` / `dequantize_affine` packets to `.default` and refreshes `meta["val"]`, both of which fusion depends on. The contract of `get_core_aten_quantizers`: - **Only core ATen ops.** Every op it quantizes is core ATen (`torch.Tag.core`): add, sub, mul, bmm, relu, max_pool2d_with_indices, avg_pool2d, convolution, sigmoid, tanh, hardtanh, gelu and native_layer_norm. It does not cover every core ATen op, and more can be added. - **Each quantizer always sees its op.** Export's decompositions keep core ATen ops, so a model's use of these ops reaches the quantizer without `preserved_ops()` keeping anything whole. - **Portable.** Whatever no backend claims decomposes to quantize/dequantize plus the same core ATen ops, all of which have portable kernels. - **Qparams come from the caller.** The package builds no `QuantizationConfig` of its own: callers build theirs with TorchAO or reuse their backend's. `weighted_config` applies to convolution, `config` to everything else. The only change it makes is moving the per-channel weight axis to 1 for transposed convolution. Grouped transposed convolution uses `config` instead. - **Non-core ops belong to the backend.** A backend that wants an op such as `linear` or `hardswish` kept whole adds its own `OpQuantizer`, and `preserved_ops()` keeps it out of decomposition. Non-core ops that many backends share, such as `linear`, can move into the shared list later. Differential Revision: D129194623
389b986 to
1ec7bad
Compare
Summary:
This diff makes the quantizer in
frontends/fused_quant/quantizer/usable byany backend:
get_core_aten_quantizers(config, weighted_config)inquantizer/quantizer.py: the op quantizers every backend can share, with thecontract below.
arg1_is_scalaris the node filter its scalar add/sub/mulvariants use.
FusedQuantQuantizernow composes with other PT2E quantizers on afirst-claim-wins basis. Its annotations set
_annotated(the flag otherquantizers check), and a
fused_quant_ownednode flag letsfuse()skipnodes that another quantizer claimed.
FusedQuantQuantizer.fusefirst repairs theconvert_pt2eoutput: itcanonicalizes torchao
quantize_affine/dequantize_affinepackets to.defaultand refreshesmeta["val"], both of which fusion depends on.The contract of
get_core_aten_quantizers:(
torch.Tag.core): add, sub, mul, bmm, relu, max_pool2d_with_indices,avg_pool2d, convolution, sigmoid, tanh, hardtanh, gelu and native_layer_norm.
It does not cover every core ATen op, and more can be added.
ATen ops, so a model's use of these ops reaches the quantizer without
preserved_ops()keeping anything whole.plus the same core ATen ops, all of which have portable kernels.
QuantizationConfigof its own: callers build theirs with TorchAO or reuse their backend's.
weighted_configapplies to convolution,configto everything else. Theonly change it makes is moving the per-channel weight axis to 1 for
transposed convolution. Grouped transposed convolution uses
configinstead.
linearorhardswishkept whole adds its ownOpQuantizer, andpreserved_ops()keeps it out of decomposition. Non-core ops that manybackends share, such as
linear, can move into the shared list later.Differential Revision: D129194623