Skip to content

Commit

Permalink
Fixed to dump the ignored_scope in quantization more gracefully (#2578)
Browse files Browse the repository at this point in the history
### Changes
In the case of dumping parameters of `IgnoredScope` in quantization:
- The field `validate` isn't dumped
- If the values corresponding to the rest of the fields are empty (`[]`)
they aren't dumped
- In case the `IgnoredScope` is passed blank, then only `<ignored_scope
value="[]">` is dumped

### Related tickets
Ticket: 129593
Fixes Issue: #2564 

### Tests

The test case,
[`tests/openvino/native/quantization/test_quantization_pipeline.py::test_meta_information`](https://github.com/openvinotoolkit/nncf/blob/86ea8f68636b3fec84339c2cdc1969227f647f6e/tests/openvino/native/quantization/test_quantization_pipeline.py#L97)
has been modified to validate the new changes. To protect code from
regressions.
  • Loading branch information
Viditagarwal7479 authored May 3, 2024
1 parent 7985126 commit 3586e58
Show file tree
Hide file tree
Showing 2 changed files with 102 additions and 4 deletions.
27 changes: 24 additions & 3 deletions nncf/openvino/rt_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,29 @@
# See the License for the specific language governing permissions and
# limitations under the License.

from typing import Dict, List, Optional
from dataclasses import asdict
from typing import Any, Dict, List, Optional

import openvino.runtime as ov

from nncf.common.logging import nncf_logger
from nncf.scopes import IgnoredScope


def exclude_empty_fields(value: Dict[str, Any]) -> Dict[str, Any]:
"""
Remove keys where value is empty and key is `validate`
:param value: IgnoredScope instance, based on the quantization parameters
"""
value.pop("validate")
keys = list(value.keys())
for key in keys:
if not value[key]:
value.pop(key)
return value


def dump_parameters(
model: ov.Model, parameters: Dict, algo_name: Optional[str] = "quantization", path: Optional[List] = None
) -> None:
Expand All @@ -33,8 +48,14 @@ def dump_parameters(
for key, value in parameters.items():
# Special condition for composed fields like IgnoredScope
if isinstance(value, IgnoredScope):
dump_parameters(model, value.__dict__, algo_name, [key])
continue
value = exclude_empty_fields(asdict(value))
if bool(value):
dump_parameters(model, value, algo_name, [key])
continue
else:
# The default value in case empty ignored_scope parameter passed
value = []

rt_path = ["nncf", algo_name] + path + [key]
model.set_rt_info(str(value), rt_path)
except RuntimeError as e:
Expand Down
79 changes: 78 additions & 1 deletion tests/openvino/native/quantization/test_quantization_pipeline.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import openvino.runtime as ov
import pytest

import nncf
from nncf.common.quantization.structs import QuantizationPreset
from nncf.openvino.quantization.quantize_model import quantize_impl
from nncf.parameters import TargetDevice
Expand All @@ -21,6 +22,7 @@
from tests.openvino.native.models import ConvModel
from tests.openvino.native.models import LinearModel
from tests.openvino.native.models import MatMul2DModel
from tests.openvino.native.models import WeightsModel
from tests.openvino.native.test_model_transformer import get_nodes_by_type

REF_FQ_NODES = [
Expand Down Expand Up @@ -101,9 +103,15 @@ def check_parameters(quantized_model, parameters, path):
if isinstance(value, TargetDevice):
value = value.value
if isinstance(value, IgnoredScope):
if value == IgnoredScope():
check_parameters(quantized_model, {"ignored_scope": []}, path)
continue
check_parameters(quantized_model, value.__dict__, rt_path)
continue
assert quantized_model.get_rt_info(rt_path) == str(value)
if "ignored_scope" in path and (not value or key == "validate"):
assert quantized_model.has_rt_info(rt_path) is False
else:
assert quantized_model.get_rt_info(rt_path) == str(value)

model = model_creator_func().ov_model
dataset = get_dataset_for_test(model)
Expand All @@ -120,3 +128,72 @@ def check_parameters(quantized_model, parameters, path):
assert quantized_model.has_rt_info(base_path)

check_parameters(quantized_model, quantize_parameters, base_path)


@pytest.mark.parametrize(
"ignored_options, expected_dump",
[
(
IgnoredScope(names=["conv_weights_0", "conv_weights_1"]),
{
"validate": None,
"types": None,
"subgraphs": None,
"patterns": None,
"names": "['conv_weights_0', 'conv_weights_1']",
},
),
(
IgnoredScope(
subgraphs=[
nncf.Subgraph(
inputs=[
"MatMul_1",
],
outputs=["MatMul"],
)
],
),
{
"validate": None,
"types": None,
"subgraphs": "[{'inputs': ['MatMul_1'], 'outputs': ['MatMul']}]",
"patterns": None,
"names": None,
},
),
(
IgnoredScope(names=["MatMul"], types=["Add"]),
{
"validate": None,
"types": "['Add']",
"subgraphs": None,
"patterns": None,
"names": "['MatMul']",
},
),
(IgnoredScope(), {"": "[]"}),
],
)
def test_ignored_scope_dump(ignored_options, expected_dump, tmp_path):
ignored_scope_path = ["nncf", "quantization", "ignored_scope"]

model = WeightsModel().ov_model
dataset = get_dataset_for_test(model)
quantize_parameters = {
"preset": QuantizationPreset.PERFORMANCE,
"target_device": TargetDevice.CPU,
"subset_size": 1,
"fast_bias_correction": True,
"ignored_scope": ignored_options,
}
quantized_model = quantize_impl(model, dataset, **quantize_parameters)
ov.save_model(quantized_model, tmp_path / "ov_model.xml")
core = ov.Core()
dumped_model = core.read_model(tmp_path / "ov_model.xml")
for key, value in expected_dump.items():
rt_path = ignored_scope_path + [key] if key else ignored_scope_path
if value:
assert dumped_model.get_rt_info(rt_path) == value
else:
assert dumped_model.has_rt_info(rt_path) is False

0 comments on commit 3586e58

Please sign in to comment.