You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Replace the coremltools-based 1D k-means used by palettization with a vendored C++ core that is JIT-compiled at runtime via `torch.utils.cpp_extension`. `coremltools` is no longer a runtime dependency (it is now an optional dependency, installable via the `coreml` extra). This requires a C++ compiler to be available on the host at runtime.
Calibration is necessary when activation quantization is enabled. In order to determine proper quantization parameters for activation quantizers, representative data must be passed through the prepared model in the `calibration_mode()` context.
55
55
56
-
Inside `calibration_mode()`, fake quantization is disabled and observers track tensor ranges seen at each activation quantizer.
57
-
Each forward pass updates the activation scales without introducing quantization noise into the output.
58
-
On exit, observers are disabled and fake quantization is re-enabled.
56
+
Inside `calibration_mode()`, activation fake quantization is disabled while weight fake quantization stays on, and observers track tensor ranges seen at each activation quantizer.
57
+
Each forward pass updates the activation scales using activations produced with quantized weights upstream, without injecting activation quantization noise into the observed values.
58
+
On exit, observers are disabled and activation fake quantization is re-enabled.
Several examples below configure specific module types or module names. To determine these for your model, see [How to get names + types](../quantization/config.md#how-to-get-names--types-for-modules-and-ops). Since palettization only supports eager execution mode, only the eager mode guidance in that section is relevant.
119
+
Several examples below configure specific module types or module names. To determine these for your model, use {class}`~coreai_opt.inspection.ModelInspector` with `execution_mode="eager"` — see [Inspecting Model Structure](../utils/model_inspection.md). Palettization supports eager mode only.
120
120
121
121
### Apply 4-bit palettization globally, 8-bit to linear layers
Copy file name to clipboardExpand all lines: docs/src/quantization/advanced.md
+19-8Lines changed: 19 additions & 8 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -166,17 +166,28 @@ The `qscheme` controls how these bins are distributed around zero, by determinin
166
166
167
167
## Quantization Defaults for Known-Range Activations
168
168
169
-
In graph mode, certain ops have known output ranges. For these ops, the user's `qscheme`setting is not respected — the activation is always treated as asymmetric or symmetric depending on the op, regardless of what the user configured. The treatment also differs between `relu` and the `sigmoid` / `tanh` family. For `relu`, only `qscheme` is overridden; `dtype`, scale, and zero point are still derived from the user's spec and calibration data. For `sigmoid` and `tanh`, scale, zero point, **and**`dtype` are pinned to fixed values (always `torch.uint8`, ignoring whatever `dtype`the user configured).
169
+
In graph mode, certain activation ops have analytically known output ranges. For these ops, the quantizer overrides the `qscheme`and `float_range` of the qparams calculator at prepare time, regardless of what the user configured. The user's `dtype` is always preserved — these adjustments do not change the number of bits or the signed/unsigned choice.
170
170
171
-
| Op | Output range | Always treated as | Scale | Zero point |
The scale and zero point values in the table below assume the default `int8` dtype. For other dtypes, the same formulas apply with the appropriate `quant_min` / `quant_max`.
176
172
177
-
**Relu**: Treated as asymmetric. The user's `qscheme` is ignored, but `dtype`, scale, and zero point are still derived from the user's spec and calibration data. The zero point follows `zero_point = quant_min - round(min_val / scale)`. Since `relu`'s observed min is always `0`, the zero point very commonly ends up near `quant_min` (e.g., `-128` for `int8`).
173
+
| Op | Output range |`qscheme`|`float_range`| Scale (int8) | Zero point (int8) |
> **Motivation for asymmetric `relu` and `sigmoid`**: Both ops produce non-negative outputs. With symmetric quantization, the zero point sits at the center of the quantized range, placing half the bins in negative territory that these ops never produce. Those bins are effectively wasted — no floating-point value will ever map to them, reducing quantization resolution by half. Asymmetric treatment shifts the zero point toward the edge of the range so all bins cover values the op actually produces.
182
+
**Relu**: The lower bound of `float_range` is pinned to 0 and `qscheme` is set to asymmetric. Because the observed minimum is always 0, the zero point is fixed at `quant_min` (−128 for int8) and stays there regardless of calibration data. The upper bound remains `None` (data-driven), so the scale continues to update during calibration.
183
+
184
+
**Sigmoid and hardsigmoid**: Both `qscheme` and `float_range` are fully pinned. Scale and zero point are entirely determined by the dtype and the fixed output range — calibration data has no effect on them.
185
+
186
+
**Tanh**: `qscheme` (symmetric) and `float_range` (−1, 1) are fully pinned. Scale and zero point are entirely determined by the dtype and range.
187
+
188
+
**Hardtanh**: Bounds are read from the op's node arguments at prepare time, so the effective range and qscheme depend on how the op was configured. If `min_val == −max_val` the range is symmetric around zero and `qscheme` is set to symmetric; otherwise `qscheme` is set to asymmetric. Both ends of `float_range` are pinned to the configured bounds. `relu6` is a special case of `hardtanh(0, 6)` and is handled identically.
189
+
190
+
> **Motivation for asymmetric treatment**: Symmetric quantization places the zero point at the center of the quantized range. For `relu`, `sigmoid`, and `hardsigmoid`, whose outputs are always non-negative, symmetric quantization places half the bins in negative territory that the op never produces — wasting half the available resolution. Asymmetric quantization shifts the zero point to the edge of the range so that all bins cover values the op actually generates. For `tanh` and symmetric `hardtanh`, the output is centered at zero so both halves of the range are used equally, and symmetric quantization is appropriate.
180
191
181
192
Eager mode does not perform these adjustments — all activations are quantized uniformly using the user-configured spec.
Copy file name to clipboardExpand all lines: docs/src/quantization/config.md
+7-35Lines changed: 7 additions & 35 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -184,7 +184,7 @@ The defaults are:
184
184
185
185
In [Quantization Overview](overview.md) we saw how to use the default `W_INT8_A_INT8` config. [Config classes and their defaults](#config-classes-and-their-defaults) described the default settings in `QuantizerConfig()`, `ModuleQuantizerConfig()`, and `OpQuantizerConfig()`. Let us now see how to configure quantization when non-default settings are desired.
186
186
187
-
Several examples below configure specific module names, module types, op names, or op types. To determine these for your model, see [How to get names + types for modules and ops](#how-to-get-names-types-for-modules-and-ops) (eager mode) or [Inspecting Model Structure](../utils/model_inspection.md) (graph mode).
187
+
Several examples below configure specific module names, module types, op names, or op types. To determine these for your model, see [Inspecting Model Structure](../utils/model_inspection.md).
188
188
189
189
### Example: `W_MXFP4_A_FP8` applied to all supported ops
190
190
@@ -857,7 +857,7 @@ classDiagram
857
857
858
858
## How to get names + types for modules and ops
859
859
860
-
**Graph mode** (for `module_name_configs`, `module_type_configs`, `op_name_config`, `op_type_config`): use {class}`~coreai_opt.inspection.ModelInspector` to discover module names, module types, op names, and op types.
860
+
Use {class}`~coreai_opt.inspection.ModelInspector` to discover module names, module types, op names, and op types for both graph and eager execution modes.
861
861
862
862
```python
863
863
import torch
@@ -866,40 +866,12 @@ from coreai_opt.inspection import ModelInspector
866
866
867
867
model = nn.Sequential(nn.Linear(10, 20), nn.ReLU(), nn.Linear(20, 5))
# Use execution_mode="eager" for eager mode inspection.
870
+
model,
871
+
example_inputs=(torch.randn(1, 10),),
872
+
execution_mode="graph",
870
873
)
871
874
print(inspector.format_summary())
872
875
```
873
876
874
-
See [Inspecting Model Structure](../utils/model_inspection.md) for full usage and examples.
875
-
876
-
**Eager mode**: module names (for `module_name_configs`) can be obtained by inspecting `model.named_modules()`. This includes all modules in the model (nested and leaf). The names align with the structure of modules defined in code.
877
-
878
-
Op names can be constructed by referring to the parent module and the op in it. Example:
879
-
880
-
```python
881
-
class TwoAddModule(torch.nn.Module):
882
-
def forward(self, x):
883
-
a = x + x
884
-
b = a + a
885
-
return b
886
-
887
-
888
-
class Model(torch.nn.Module):
889
-
def __init__(self):
890
-
super().__init__()
891
-
self.submodule_a = TwoAddModule()
892
-
self.submodule_b = TwoAddModule()
893
-
894
-
def forward(self, x):
895
-
x = self.submodule_a(x)
896
-
x = self.submodule_b(x)
897
-
return x
898
-
899
-
900
-
# the names of ops in the model will be :
901
-
# - submodule_a.add
902
-
# - submodule_a.add_1
903
-
# - submodule_b.add
904
-
# - submodule_b.add_1
905
-
```
877
+
See [Inspecting Model Structure](../utils/model_inspection.md) for full usage, examples, and a comparison of graph and eager mode op naming.
Copy file name to clipboardExpand all lines: docs/src/quantization/overview.md
+4-3Lines changed: 4 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -111,9 +111,10 @@ Right after `prepare()`, the activation scales come only from `example_inputs`,
111
111
This is what the context manager handles:
112
112
113
113
- Inside the context:
114
-
- fake-quantization is turned **off**: forward pass gives the same output as the unquantized model. Hence without distorting the outputs, the quantization params can be computed.
115
-
- range observers are turned **on**: this means that each forward pass updates the observed activation ranges, and hence the activation quantization scales.
116
-
- After exiting the context manager, observers are turned back off and fake-quantization back on, leaving the model ready for evaluation.
114
+
- activation fake-quantization is turned **off**: activation observers see undistorted activation values, so the observed ranges (and resulting scales) reflect the true distribution rather than already-quantized values.
115
+
- weight fake-quantization stays **on**: activations flowing into each observer are produced with quantized weights upstream, matching what the deployed model will actually see.
116
+
- range observers are turned **on**: each forward pass updates the observed activation ranges, and hence the activation quantization scales.
117
+
- After exiting the context manager, observers are turned back off and activation fake-quantization back on, leaving the model ready for evaluation.
117
118
118
119
A small amount of representative data is typically enough.
0 commit comments