Skip to content

[WIP] Add INT2_ASYM weight compression mode - #4168

Draft
Cyberpunk1210 wants to merge 3 commits into
openvinotoolkit:developfrom
Cyberpunk1210:int2-asym
Draft

[WIP] Add INT2_ASYM weight compression mode#4168
Cyberpunk1210 wants to merge 3 commits into
openvinotoolkit:developfrom
Cyberpunk1210:int2-asym

Conversation

@Cyberpunk1210

@Cyberpunk1210 Cyberpunk1210 commented Aug 10, 2026

Copy link
Copy Markdown

Changes

Adds CompressWeightsMode.INT2_ASYM, reusing the existing generic
asymmetric path (is_asym_mode): level_low=0 / level_high=3 with scale and
zero point derived from min/max. compression_dtype maps to
TensorDataType.int2 (OpenVINO has no i2 type; int2 weights are physically
unsigned u2).

Also picks up two mode dispatches that predate the new mode (found via
review feedback on this PR):

  • lora_correction.py listed the integer modes explicitly, so INT2_ASYM
    raised InternalError despite do_integer_dequantization being generic
    over them.
  • gptq.py collected zero points only for [INT8_ASYM, INT4_ASYM], so
    INT2_ASYM silently got zero_points = None -- an asymmetric mode
    dequantized as if symmetric, with no error. Replaced with is_asym_mode
    so the list cannot go stale again.

Reason for changes

CompressWeightsMode stopped at INT2_SYM, so 2-bit asymmetric compression
was not expressible. At 2 bits there are only four levels, and a symmetric
grid spends one of them on a sign that one-sided weight groups never use.

Related tickets

N/A

Tests

Added to tests/openvino/native/quantization/test_weights_compression.py:

  • test_int_asym_compressed_weights_range -- codes fill [0, 2**num_bits-1]
    and the zero point is used (zp == 0 for all-positive data, zp ==
    level_high for all-negative). Parametrized over INT2/INT4/INT8_ASYM so the
    new mode is checked against its siblings.
  • test_int2_asym_group_wise_zero_point_shape -- per-group zero point
    exists and matches the scale's shape.
  • test_int2_asym_config -- num_bits, is_asym_mode, int2 compression dtype.
  • INT2_ASYM added to test_int_quantization_with_precomputed_parameters,
    including the cases where supplying only one of scale/zero point raises.

Also validated end-to-end via optimum-intel's OpenVINO export path
(companion PR: huggingface/optimum-intel#1922) on a
2-bit MoE model.

Caveat on my own verification: both machines I have access to are cut off
from PyPI/conda-forge, so I could not execute the suite locally. The new
assertions were derived by transcribing the exact formulas from
calculate_integer_quantization_params, calculate_scale_zero_point and
_calculate_integer_quantized_weight and checking them numerically
(including that no value lands on a rounding tie, which would make the test
non-deterministic under banker's rounding). CI is the real check here.

CompressWeightsMode stopped at INT2_SYM, so 2-bit asymmetric compression was not
expressible. At 2 bits there are only four levels, and a symmetric grid spends
one of them on a sign that one-sided weight groups never use.

INT2_ASYM reuses the existing generic asymmetric path: is_asym_mode selects
level_low=0 / level_high=2**num_bits-1 with both scale and zero point derived
from min/max, which is already generic in num_bits. compression_dtype maps to
TensorDataType.int2 because OpenVINO has no i2 type -- int2 weights are
physically unsigned, which is also why the symmetric path has to shift by
+2**(num_bits-1). Asymmetric codes land in [0, 3] natively, so the weight
constant keeps its dtype and the zero point is emitted as u2 as well.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a new weight-compression mode, CompressWeightsMode.INT2_ASYM, to enable 2-bit asymmetric weight quantization in NNCF’s weight compression pipeline (notably for OpenVINO export paths).

Changes:

  • Add INT2_ASYM to the public CompressWeightsMode enum (with docstring description).
  • Extend WeightCompressionConfig to recognize INT2_ASYM for bit-width (num_bits) and asymmetric-path selection (is_asym_mode).
  • Map INT2_ASYM to TensorDataType.int2 in compression_dtype (leveraging OpenVINO’s physical u2 storage for int2).

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
src/nncf/quantization/algorithms/weight_compression/config.py Adds INT2_ASYM handling for bit-width, asym-mode selection, and compression dtype mapping.
src/nncf/parameters.py Exposes INT2_ASYM in the public API enum and documents its behavior and motivation.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines 87 to +92
def is_asym_mode(self) -> bool:
return self.mode in [CompressWeightsMode.INT4_ASYM, CompressWeightsMode.INT8_ASYM]
return self.mode in [
CompressWeightsMode.INT2_ASYM,
CompressWeightsMode.INT4_ASYM,
CompressWeightsMode.INT8_ASYM,
]
Comment on lines 72 to 78
CompressWeightsMode.NVFP4: 4,
CompressWeightsMode.FP8_E4M3: 8,
CompressWeightsMode.MXFP8_E4M3: 8,
CompressWeightsMode.INT3_SYM: 3,
CompressWeightsMode.INT2_SYM: 2,
CompressWeightsMode.INT2_ASYM: 2,
}
@Cyberpunk1210 Cyberpunk1210 changed the title Add INT2_ASYM weight compression mode [WIP] Add INT2_ASYM weight compression mode Aug 10, 2026
@Cyberpunk1210
Cyberpunk1210 marked this pull request as draft August 10, 2026 07:47
Cyberpunk1210 and others added 2 commits August 10, 2026 16:20
Two mode allow-lists predate INT2_ASYM and did not pick it up:

- lora_correction.py listed the integer modes explicitly, so INT2_ASYM fell
  through to `raise InternalError` even though do_integer_dequantization is
  generic over them (INT4_ASYM already used it). The error message had also
  gone stale and omitted INT3_SYM/INT2_SYM; it now lists what the branch
  above it actually accepts.

- gptq.py collected zero points only for [INT8_ASYM, INT4_ASYM], so
  INT2_ASYM took the `else` branch and had its zero points silently set to
  None -- an asymmetric mode dequantized as if symmetric, with no error.
  Replaced with `is_asym_mode`, which is the property that defines "has a
  zero point" and is the idiom used elsewhere (weight_lowering.py,
  optimized_functions/models.py), so the list cannot go stale again.

Reachable in both cases: quantize_model.py only blocks the float modes for
the AWQ/scale-estimation/GPTQ/LoRA group on the OpenVINO backend, so
`mode=INT2_ASYM` with either algorithm reaches these dispatches.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Covers what was previously untested for the new mode:

- test_int_asym_compressed_weights_range: codes fill [0, 2**num_bits - 1]
  and the zero point is actually used. Parametrized over all three
  asymmetric modes so INT2_ASYM is checked to behave like its siblings
  rather than in isolation. The quantization range is clamped to include
  zero, so one-sided data is what exercises the zero point: all-positive
  gives zp == 0, all-negative gives zp == level_high.
- test_int2_asym_group_wise_zero_point_shape: a per-group zero point exists
  and has the same shape as the scale, which INT2_SYM has no equivalent of.
- test_int2_asym_config: num_bits, is_asym_mode and the int2 (physically u2)
  compression dtype.
- test_int_quantization_with_precomputed_parameters: INT2_ASYM added to the
  existing parametrization, including the two cases where supplying only one
  of scale/zero point must raise.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot added the NNCF OpenVINO Pull requests that updates NNCF OpenVINO label Aug 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

NNCF OpenVINO Pull requests that updates NNCF OpenVINO

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants