Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/nncf/parameters.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@ class CompressWeightsMode(StrEnum):
:param INT4_ASYM: The same as INT4_SYM mode, but weights are quantized to a primary precision asymmetrically
with a typical non-fixed zero point.
https://github.com/openvinotoolkit/nncf/blob/develop/docs/usage/training_time_compression/other_algorithms/LegacyQuantization.md#asymmetric-quantization
:param INT3_SYM: Stands for a mixed-precision weights quantization with 3-bit integer as a primary precision.
Weights are quantized to a primary precision symmetrically without zero point.
:param INT2_SYM: Stands for a mixed-precision weights quantization with 2-bit integer as a primary precision.
Weights are quantized to a primary precision symmetrically without zero point.
:param NF4: The the same as INT4_SYM mode, but primary precision is NF4 data type without zero point.
:param MXFP4: MX-compliant FP4 format with E2M1 values sharing group-level E8M0 scale. The size of group is 32.
:param MXFP8_E4M3: MX-compliant FP8 format with E4M3 values sharing group-level E8M0 scale. The size of group is 32.
Expand All @@ -104,6 +108,8 @@ class CompressWeightsMode(StrEnum):
INT8_ASYM = "int8_asym"
INT4_SYM = "int4_sym"
INT4_ASYM = "int4_asym"
INT3_SYM = "int3_sym"
INT2_SYM = "int2_sym"
NF4 = "nf4"
CB4 = "cb4"
MXFP4 = "mxfp4"
Expand Down
40 changes: 32 additions & 8 deletions src/nncf/quantization/algorithms/weight_compression/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,14 +61,26 @@ def num_bits(self) -> int:
return 8
return 16

if self.mode in [
CompressWeightsMode.INT8_SYM,
CompressWeightsMode.INT8_ASYM,
CompressWeightsMode.FP8_E4M3,
CompressWeightsMode.MXFP8_E4M3,
]:
return 8
return 4
mode_to_bits_map = {
CompressWeightsMode.INT8_ASYM: 8,
CompressWeightsMode.INT8_SYM: 8,
CompressWeightsMode.INT4_SYM: 4,
CompressWeightsMode.INT4_ASYM: 4,
CompressWeightsMode.NF4: 4,
CompressWeightsMode.FP4: 4,
CompressWeightsMode.MXFP4: 4,
CompressWeightsMode.NVFP4: 4,
CompressWeightsMode.FP8_E4M3: 8,
CompressWeightsMode.MXFP8_E4M3: 8,
CompressWeightsMode.INT3_SYM: 3,
CompressWeightsMode.INT2_SYM: 2,
}

try:
return mode_to_bits_map[self.mode]
except KeyError as e:
msg = f"Unsupported compression mode for num_bits: {self.mode}"
raise InternalError(msg) from e

@property
def is_asym_mode(self) -> bool:
Expand Down Expand Up @@ -118,6 +130,8 @@ def compression_dtype(self) -> TensorDataType:
return TensorDataType.uint8
return TensorDataType.uint16
dtype_per_mode = {
CompressWeightsMode.INT2_SYM: TensorDataType.int2,
CompressWeightsMode.INT3_SYM: TensorDataType.int3,
CompressWeightsMode.INT4_SYM: TensorDataType.int4,
CompressWeightsMode.INT4_ASYM: TensorDataType.uint4,
CompressWeightsMode.INT8_ASYM: TensorDataType.uint8,
Expand All @@ -131,6 +145,16 @@ def compression_dtype(self) -> TensorDataType:
}
return dtype_per_mode[self.mode]

@property
def is_symmetric_represented_by_unsigned(self) -> bool:
"""
True if compression type is symmetric and represented by unsigned integer, else False.
"""
return self.mode in [
CompressWeightsMode.INT2_SYM,
CompressWeightsMode.INT3_SYM,
]

def __hash__(self) -> int:
return hash((self.mode.value, self.group_size))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,18 @@ def _create_compression_subgraph(
dtype=compression_dtype,
name=const_node_name,
)
elif compression_config.is_symmetric_represented_by_unsigned:
offset = 2 ** (compression_config.num_bits - 1)
compressed_tensor = compressed_weight.tensor + offset
compressed_const = create_ov_const_from_tensor(compressed_tensor, compression_dtype, name=const_node_name)
converted_const = opset.convert(compressed_const, ov.Type.f16)

zero_point_const = opset.constant(offset, dtype=ov.Type.i8, name=f"{const_node_name}/zero_point")
zero_point_const = opset.convert(zero_point_const, ov.Type.f16)

converted_const = opset.subtract(
converted_const, zero_point_const, name=f"{const_node_name}/zero_point/subtract"
)
else:
compressed_const = create_ov_const_from_tensor(
compressed_weight.tensor, compression_dtype, name=const_node_name
Expand Down
4 changes: 4 additions & 0 deletions src/nncf/tensor/definitions.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ class TensorDataType(StrEnum):
uint8 = auto()
uint4 = auto()
int4 = auto()
int3 = auto()
int2 = auto()

def is_float(self) -> bool:
"""
Expand All @@ -78,6 +80,8 @@ def itemsize(self) -> int:
TensorDataType.nf4: 4,
TensorDataType.uint4: 4,
TensorDataType.int4: 4,
TensorDataType.int3: 3,
TensorDataType.int2: 2,
TensorDataType.f8e4m3: 8,
TensorDataType.f8e5m2: 8,
TensorDataType.int8: 8,
Expand Down
4 changes: 4 additions & 0 deletions src/nncf/tensor/functions/openvino_numeric.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
from nncf.tensor.functions.numpy_numeric import DTYPE_MAP_REV as DTYPE_MAP_REV_NUMPY

DTYPE_MAP: dict[TensorDataType, ov.Type] = {
TensorDataType.int2: ov.Type.u2,
TensorDataType.int3: ov.Type.u3,
TensorDataType.nf4: ov.Type.nf4,
Comment thread
andreyanufr marked this conversation as resolved.
TensorDataType.f4e2m1: ov.Type.f4e2m1,
TensorDataType.f8e8m0: ov.Type.f8e8m0,
Expand All @@ -43,6 +45,8 @@

NATIVE_OV_CAST_DTYPES = [
TensorDataType.bfloat16,
TensorDataType.int2,
TensorDataType.int3,
TensorDataType.int4,
TensorDataType.uint4,
TensorDataType.nf4,
Expand Down
4 changes: 4 additions & 0 deletions tests/cross_fw/test_templates/template_test_nncf_tensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -2155,6 +2155,8 @@ def test_fn_zeros(self):
and dtype == TensorDataType.bfloat16
or dtype
in [
TensorDataType.int2,
TensorDataType.int3,
TensorDataType.int4,
TensorDataType.uint4,
TensorDataType.nf4,
Expand Down Expand Up @@ -2189,6 +2191,8 @@ def test_fn_eye(self, n, m, ref):
and dtype == TensorDataType.bfloat16
or dtype
in [
TensorDataType.int2,
TensorDataType.int3,
TensorDataType.int4,
TensorDataType.uint4,
TensorDataType.uint16,
Expand Down
23 changes: 15 additions & 8 deletions tests/openvino/native/quantization/test_weights_compression.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@
INT8_MODES = (CompressWeightsMode.INT8_SYM, CompressWeightsMode.INT8_ASYM)
INT4_NF4_MODES = (CompressWeightsMode.INT4_SYM, CompressWeightsMode.INT4_ASYM, CompressWeightsMode.NF4)
INT4_MODES = (CompressWeightsMode.INT4_SYM, CompressWeightsMode.INT4_ASYM)
INT2_3_MODES = (CompressWeightsMode.INT2_SYM, CompressWeightsMode.INT3_SYM)


class LMLinearModel(OVReferenceModel):
Expand Down Expand Up @@ -908,7 +909,7 @@ def test_raise_error_with_unsupported_params_for_int8(mode, params):
compress_weights(ov.Model([], []), mode=mode, **params)


@pytest.mark.parametrize("mode", INT4_NF4_MODES)
@pytest.mark.parametrize("mode", INT4_NF4_MODES + INT2_3_MODES)
@pytest.mark.parametrize(
"params",
(
Expand Down Expand Up @@ -1023,23 +1024,23 @@ def test_call_max_var_criterion_with_dataset_by_default(mocker, mode):
scores_spy.assert_called()


@pytest.mark.parametrize("mode", INT4_MODES)
@pytest.mark.parametrize("mode", INT4_MODES + INT2_3_MODES)
def test_call_max_var_criterion_with_dataset_by_default_awq(mode):
model = AWQMatmulModel().ov_model
dataset = Dataset([np.ones([2, 8, 8])])

compress_weights(model, mode=mode, ratio=1.0, group_size=2, dataset=dataset, awq=True)


@pytest.mark.parametrize("mode", INT4_NF4_MODES)
@pytest.mark.parametrize("mode", INT4_NF4_MODES + INT2_3_MODES)
def test_call_max_var_criterion_with_dataset_awq_for_compressed_model(mode):
model = AWQMatmulModel(is_int8=True).ov_model
dataset = Dataset([np.ones([2, 8, 8])])

compress_weights(model, mode=mode, ratio=1.0, group_size=2, dataset=dataset, awq=True)


@pytest.mark.parametrize("mode", INT4_NF4_MODES)
@pytest.mark.parametrize("mode", INT4_NF4_MODES + INT2_3_MODES)
def test_call_max_var_criterion_with_dataset_awq_neg_group_size(mode):
model = AWQMatmulModel().ov_model
dataset = Dataset([np.ones([2, 8, 8])])
Expand Down Expand Up @@ -1250,31 +1251,31 @@ def test_call_max_var_criterion_with_dataset_by_default_scale_estimation(mode, c
assert tzm_spy.call_args_list[0][0][0].dtype == compressed_weight_dtype


@pytest.mark.parametrize("mode", INT4_NF4_MODES)
@pytest.mark.parametrize("mode", INT4_NF4_MODES + INT2_3_MODES)
def test_call_max_var_criterion_with_dataset_scale_estimation_for_compressed_model(mode):
model = AWQMatmulModel(is_int8=True).ov_model
dataset = Dataset([np.ones([1, 8, 8])])

compress_weights(model, mode=mode, ratio=1.0, group_size=2, dataset=dataset, scale_estimation=True)


@pytest.mark.parametrize("mode", INT4_NF4_MODES)
@pytest.mark.parametrize("mode", INT4_NF4_MODES + INT2_3_MODES)
def test_call_max_var_criterion_with_dataset_scale_estimation_neg_group_size(mode):
model = AWQMatmulModel().ov_model
dataset = Dataset([np.ones([1, 8, 8])])

compress_weights(model, mode=mode, ratio=1.0, group_size=-1, dataset=dataset, scale_estimation=True)


@pytest.mark.parametrize("mode", INT4_NF4_MODES)
@pytest.mark.parametrize("mode", INT4_NF4_MODES + INT2_3_MODES)
def test_call_gptq(mode):
model = AWQMatmulModel().ov_model
dataset = Dataset([np.ones([1, 8, 8])])

compress_weights(model, mode=mode, ratio=1.0, group_size=2, dataset=dataset, gptq=True)


@pytest.mark.parametrize("mode", INT4_NF4_MODES)
@pytest.mark.parametrize("mode", INT4_NF4_MODES + INT2_3_MODES)
def test_call_gptq_with_dataset_scale_estimation_neg_group_size(mode):
model = AWQMatmulModel().ov_model
dataset = Dataset([np.ones([1, 8, 8])])
Expand Down Expand Up @@ -1497,6 +1498,8 @@ def test_codebook(codebook, n_layers, dst_type, group_size):
CompressWeightsMode.INT4_SYM,
[-8.0, -7.0, -6.0, -5.0, -4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0],
),
(CompressWeightsMode.INT3_SYM, [-4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0]),
(CompressWeightsMode.INT2_SYM, [-2.0, -1.0, 0.0, 1.0]),
),
)
def test_int_compressed_weighs_range(mode, data):
Expand Down Expand Up @@ -1660,6 +1663,10 @@ def test_codebook_weights_range(data):
(WeightCompressionConfig(CompressWeightsMode.INT8_SYM), False, False, False),
(WeightCompressionConfig(CompressWeightsMode.INT4_SYM), True, False, False),
(WeightCompressionConfig(CompressWeightsMode.INT4_SYM), False, False, False),
(WeightCompressionConfig(CompressWeightsMode.INT3_SYM), True, False, False),
(WeightCompressionConfig(CompressWeightsMode.INT3_SYM), False, False, False),
(WeightCompressionConfig(CompressWeightsMode.INT2_SYM), True, False, False),
(WeightCompressionConfig(CompressWeightsMode.INT2_SYM), False, False, False),
],
)
def test_int_quantization_with_precomputed_parameters(config, precompute_scale, precompute_zero_point, raises):
Expand Down