forked from openvinotoolkit/nncf
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
6e73710
commit 20c7f65
Showing
4 changed files
with
355 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
53 changes: 53 additions & 0 deletions
53
tests/openvino/native/quantization/test_channel_alignment.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
# Copyright (c) 2023 Intel Corporation | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
|
||
from typing import Type | ||
|
||
import pytest | ||
|
||
from nncf.common.graph.graph import NNCFGraph | ||
from nncf.openvino.graph.layer_attributes import OVConstantLayerAttributesContainer | ||
from nncf.openvino.graph.metatypes.openvino_metatypes import OVAddMetatype | ||
from nncf.openvino.graph.metatypes.openvino_metatypes import OVConvolutionMetatype | ||
from nncf.openvino.graph.model_transformer import OVModelTransformer | ||
from nncf.quantization.algorithms.channel_alignment.openvino_backend import OVChannelAlignmentAlgoBackend | ||
from tests.post_training.test_templates.test_channel_alignment import TemplateTestChannelAlignment | ||
|
||
|
||
def create_nncf_graph_for_ca_algo(): | ||
NNCFGraph() | ||
|
||
|
||
NNCF_GRAPH_FOR_CA = None | ||
|
||
|
||
class TestOVChannelAlignment(TemplateTestChannelAlignment): | ||
def get_backend_cls(self) -> Type[OVChannelAlignmentAlgoBackend]: | ||
return OVChannelAlignmentAlgoBackend | ||
|
||
def mock_nncf_graph_factory(self, mocker, nncf_graph: NNCFGraph) -> None: | ||
mocker.patch("nncf.common.factory.NNCFGraphFactory.create", return_value=nncf_graph) | ||
|
||
def mock_model_transformer_factory(self, mocker) -> None: | ||
mocker.patch("nncf.common.factory.ModelTransformerFactory.create", return_value=OVModelTransformer) | ||
|
||
def convert_conv_layer_attrs(self, layer_attributes): | ||
return OVConstantLayerAttributesContainer({}, {1: layer_attributes}) | ||
|
||
def get_conv_metatype(self): | ||
return OVConvolutionMetatype | ||
|
||
def get_add_metatype(self): | ||
return OVAddMetatype | ||
|
||
@pytest.fixture(scope="session") | ||
def test_params(self): | ||
return {"test_get_node_pairs": {"NNCFGraph": {{"bad": None, "good": None}}}} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
234 changes: 234 additions & 0 deletions
234
tests/post_training/test_templates/test_channel_alignment.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,234 @@ | ||
# Copyright (c) 2023 Intel Corporation | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
|
||
from abc import abstractmethod | ||
from typing import Type | ||
|
||
import numpy as np | ||
import pytest | ||
|
||
from nncf.common.graph.graph import NNCFGraph | ||
from nncf.common.graph.layer_attributes import ConvolutionLayerAttributes | ||
from nncf.common.graph.transformations.commands import TargetType | ||
from nncf.common.tensor_statistics.statistic_point import StatisticPoint | ||
from nncf.common.tensor_statistics.statistic_point import StatisticPointsContainer | ||
from nncf.experimental.common.tensor_statistics.collectors import MedianAggregator | ||
from nncf.experimental.common.tensor_statistics.collectors import QuantileReducer | ||
from nncf.experimental.common.tensor_statistics.collectors import TensorCollector | ||
from nncf.quantization.algorithms.channel_alignment.algorithm import ChannelAlignment | ||
from nncf.quantization.algorithms.channel_alignment.backend import ChannelAlignmentAlgoBackend | ||
from tests.post_training.test_templates.models import NNCFGraphCA | ||
from tests.post_training.test_templates.models import NNCFGraphCAWithBias | ||
|
||
VALID_CONV_LAYER_ATTRS = [ | ||
ConvolutionLayerAttributes( | ||
weight_requires_grad=False, | ||
in_channels=5, | ||
out_channels=5, | ||
kernel_size=(5, 5), | ||
stride=(1, 1), | ||
dilations=(1, 1), | ||
groups=1, | ||
transpose=False, | ||
padding_values=(0, 0, 0, 0), | ||
) | ||
] | ||
|
||
|
||
INVALID_CONV_LAYER_ATTRS = [ | ||
ConvolutionLayerAttributes( | ||
weight_requires_grad=False, | ||
in_channels=5, | ||
out_channels=5, | ||
kernel_size=(5, 5), | ||
stride=(2, 1), | ||
dilations=(1, 1), | ||
groups=1, | ||
transpose=False, | ||
padding_values=(0, 0, 0, 0), | ||
), | ||
ConvolutionLayerAttributes( | ||
weight_requires_grad=False, | ||
in_channels=5, | ||
out_channels=5, | ||
kernel_size=(5, 5), | ||
stride=(1, 1), | ||
dilations=(2, 1), | ||
groups=1, | ||
transpose=False, | ||
padding_values=(0, 0, 0, 0), | ||
), | ||
ConvolutionLayerAttributes( | ||
weight_requires_grad=False, | ||
in_channels=5, | ||
out_channels=5, | ||
kernel_size=(5, 5), | ||
stride=(1, 1), | ||
dilations=(2, 1), | ||
groups=1, | ||
transpose=False, | ||
padding_values=(0, 0, 0, 0), | ||
), | ||
ConvolutionLayerAttributes( | ||
weight_requires_grad=False, | ||
in_channels=5, | ||
out_channels=5, | ||
kernel_size=(5, 5), | ||
stride=(1, 1), | ||
dilations=(1, 1), | ||
groups=5, | ||
transpose=False, | ||
padding_values=(0, 0, 0, 0), | ||
), | ||
ConvolutionLayerAttributes( | ||
weight_requires_grad=False, | ||
in_channels=5, | ||
out_channels=5, | ||
kernel_size=(5, 5), | ||
stride=(1, 1), | ||
dilations=(1, 1), | ||
groups=1, | ||
transpose=False, | ||
padding_values=(1, 0, 0, 0), | ||
), | ||
] | ||
|
||
|
||
class TemplateTestChannelAlignment: | ||
@abstractmethod | ||
def get_backend_cls(self) -> Type[ChannelAlignmentAlgoBackend]: | ||
pass | ||
|
||
@abstractmethod | ||
def mock_nncf_graph_factory(self, mocker, nncf_graph: NNCFGraph) -> None: | ||
pass | ||
|
||
@abstractmethod | ||
def mock_model_transformer_factory(self, mocker, mocked_transformer) -> None: | ||
pass | ||
|
||
@abstractmethod | ||
def convert_conv_layer_attrs(self, layer_attributes): | ||
pass | ||
|
||
@abstractmethod | ||
def get_conv_metatype(self): | ||
pass | ||
|
||
@abstractmethod | ||
def get_add_metatype(self): | ||
pass | ||
|
||
@abstractmethod | ||
@pytest.fixture(scope="session") | ||
def test_params(self): | ||
pass | ||
|
||
def test_align_means_scales(): | ||
pass | ||
|
||
@pytest.mark.parametrize( | ||
"layer_attributes,ref_match", | ||
[(attr, True) for attr in VALID_CONV_LAYER_ATTRS] + [(attr, False) for attr in INVALID_CONV_LAYER_ATTRS], | ||
) | ||
def test_get_node_pairs(self, layer_attributes, ref_match): | ||
algorithm = ChannelAlignment() | ||
algorithm._backend_entity = self.get_backend_cls() | ||
conv_layer_attrs = self.convert_conv_layer_attrs(layer_attributes) | ||
nncf_graph = NNCFGraphCA(self.get_conv_metatype(), conv_layer_attrs) | ||
pairs = algorithm._get_node_pairs(nncf_graph.nncf_graph) | ||
if ref_match: | ||
assert len(pairs) == 1 | ||
conv_in, add_in, conv_out = pairs[0] | ||
assert conv_in.node_name == "/Conv_1_0" | ||
assert add_in is None | ||
assert conv_out.node_name == "/Conv_2_0" | ||
else: | ||
assert len(pairs) == 0 | ||
|
||
def _get_nncf_graph(self, with_bias: bool) -> NNCFGraph: | ||
cla = self.convert_conv_layer_attrs(VALID_CONV_LAYER_ATTRS[0]) | ||
if with_bias: | ||
return NNCFGraphCAWithBias(self.get_conv_metatype(), self.get_add_metatype(), cla).nncf_graph | ||
else: | ||
return NNCFGraphCA(self.get_conv_metatype(), cla).nncf_graph | ||
|
||
@pytest.mark.parametrize("with_bias", [True, False]) | ||
def test_transformation_layout(self, with_bias, mocker): | ||
mocked_transformer = mocker.MagicMocker() | ||
self.mock_model_transformer_factory(mocker, mocked_transformer) | ||
nncf_graph = self._get_nncf_graph(with_bias) | ||
self.mock_nncf_graph_factory(mocker, nncf_graph) | ||
ChannelAlignment._align_means = mocker.MagicMock(return_value=(None, None)) | ||
ChannelAlignment._align_scales = mocker.MagicMock(return_value=(None, None, None)) | ||
|
||
statistic_points = StatisticPointsContainer() | ||
statistic_points.add_statistic_point(StatisticPoint()) | ||
|
||
@pytest.mark.parametrize("with_bias", [True, False]) | ||
def test_get_statistic_points(self, with_bias, mocker): | ||
nncf_graph = self._get_nncf_graph(with_bias) | ||
self.mock_nncf_graph_factory(mocker, nncf_graph) | ||
|
||
ref_subset_size = "ref_subset_size" | ||
ref_inplace = "ref_inplace" | ||
algorithm = ChannelAlignment(ref_subset_size, ref_inplace) | ||
algorithm._set_backend_entity = mocker.MagicMock() | ||
mocked_backed = self.get_backend_cls() | ||
ref_stat_collector = "ref_stat_collector" | ||
mocked_backed.get_statistic_collector = mocker.MagicMock(return_value=ref_stat_collector) | ||
algorithm._backend_entity = mocked_backed | ||
|
||
statistic_container = algorithm.get_statistic_points(None) | ||
|
||
backend_cls = self.get_backend_cls() | ||
target_node_name = "/Add_1_0" if with_bias else "/Conv_1_0" | ||
target_node = nncf_graph.get_node_by_name(target_node_name) | ||
ref_input_port_id, _ = backend_cls.get_activation_port_ids_for_node(target_node) | ||
|
||
assert len(statistic_container) == 1 | ||
assert target_node_name in statistic_container | ||
stat_points = statistic_container[target_node_name] | ||
assert len(stat_points) == 1 | ||
|
||
assert len(stat_points[0].algorithm_to_tensor_collectors.keys()) == 1 | ||
assert ChannelAlignment in stat_points[0].algorithm_to_tensor_collectors | ||
tensor_collectors = stat_points[0].algorithm_to_tensor_collectors[ChannelAlignment] | ||
assert len(tensor_collectors) == 1 | ||
assert tensor_collectors[0] == ref_stat_collector | ||
mocked_backed.get_statistic_collector.assert_called_once_with((0, 2, 3), 1e-4, ref_subset_size, ref_inplace) | ||
|
||
target_point = stat_points[0].target_point | ||
assert target_point.target_node_name == target_node_name | ||
assert target_point.port_id == ref_input_port_id | ||
assert target_point.type == TargetType.POST_LAYER_OPERATION | ||
|
||
@pytest.mark.parametrize("inplace_ref", [False, True]) | ||
@pytest.mark.parametrize("q_ref", [1e-4, 0.3]) | ||
def test_statistic_collectors(self, inplace_ref, q_ref): | ||
backend_cls = self.get_backend_cls() | ||
reduction_shape_ref = (0, 2, 3) | ||
num_samples_ref = 123 | ||
statistic_collector: TensorCollector = backend_cls.get_statistic_collector( | ||
reduction_shape=reduction_shape_ref, q=q_ref, num_samples=num_samples_ref, inplace=inplace_ref | ||
) | ||
|
||
assert len(statistic_collector.reducers) == 1 | ||
reducer = statistic_collector.reducers.pop() | ||
assert isinstance(reducer, QuantileReducer) | ||
assert reducer._reduction_shape == reduction_shape_ref | ||
assert np.allclose(reducer._quantile, (q_ref, 1 - q_ref)) | ||
|
||
assert len(statistic_collector.aggregators) == 2 | ||
for aggr in statistic_collector.aggregators.values(): | ||
assert isinstance(aggr, MedianAggregator) | ||
assert aggr.num_samples == num_samples_ref | ||
assert not aggr._use_per_sample_stats |