From 47269e8e7e629df27f2a4fc1e0c66e0a790908b4 Mon Sep 17 00:00:00 2001 From: Aleksei Kashapov Date: Wed, 5 Jun 2024 20:25:09 +0200 Subject: [PATCH 01/30] draft --- nncf/common/graph/graph.py | 2 +- .../openvino/quantization/quantize_ifmodel.py | 13 +- nncf/openvino/quantization/quantize_model.py | 43 ++++- nncf/quantization/quantize_model.py | 6 +- nncf/scopes.py | 155 +++++++++++------- 5 files changed, 143 insertions(+), 76 deletions(-) diff --git a/nncf/common/graph/graph.py b/nncf/common/graph/graph.py index 13ea932d921..d71b5cb9888 100644 --- a/nncf/common/graph/graph.py +++ b/nncf/common/graph/graph.py @@ -238,7 +238,7 @@ def get_nodes_by_types(self, type_list: List[str]) -> List[NNCFNode]: :return: List of nodes with provided types. """ all_nodes_of_type = [] - for nncf_node in self.get_all_nodes(): + for nncf_node in self.nodes.values(): if nncf_node.node_type in type_list: all_nodes_of_type.append(nncf_node) return all_nodes_of_type diff --git a/nncf/openvino/quantization/quantize_ifmodel.py b/nncf/openvino/quantization/quantize_ifmodel.py index 2dc007201d5..9bca7ac80a5 100644 --- a/nncf/openvino/quantization/quantize_ifmodel.py +++ b/nncf/openvino/quantization/quantize_ifmodel.py @@ -10,14 +10,13 @@ # limitations under the License. from itertools import islice -from typing import List, Optional, Tuple +from typing import Dict, List, Optional, Tuple import openvino.runtime as ov from nncf import Dataset from nncf.common import factory from nncf.common.engine import Engine -from nncf.common.factory import NNCFGraphFactory from nncf.common.graph.graph import NNCFGraph from nncf.common.graph.graph import NNCFNode from nncf.common.graph.model_transformer import ModelTransformer @@ -132,10 +131,11 @@ def _add_outputs_before_if_node(model_transformer: ModelTransformer, model: ov.M return model_transformer.transform(transformation_layout) +# MAYBE IMPLEMENT THROUH TREE STRICTUE? def apply_algorithm_if_bodies( algorithm: Algorithm, parent_model: ov.Model, - parent_graph: NNCFGraph, + graphs: Dict[int, NNCFGraph], parent_dataset: Dataset, subset_size: int, current_model_num: int, @@ -146,7 +146,7 @@ def apply_algorithm_if_bodies( Applies an algorithm recursievley to each bodies of If node. :param parent_model: Model to apply algorithm. - :param parent_graph: Graph of a model. + :param parent_graph: Graph of a model. TODO:fix :param parent_dataset: Dataset for algorithm. :param subset_size: Size of a dataset to use for calibration. :param current_model_num: Current model number. @@ -155,6 +155,7 @@ def apply_algorithm_if_bodies( :return: A model for every bodies of If nodes the algorithm was applied and the latest model number. """ nncf_logger.info(f"Iteration [{current_model_num}/{all_models_num}] ...") + parent_graph = graphs[current_model_num] quantized_model = algorithm.apply(parent_model, parent_graph, parent_statistic_points, parent_dataset) if get_number_if_op(parent_model) == 0: return quantized_model, current_model_num @@ -183,7 +184,7 @@ def apply_algorithm_if_bodies( then_quantized_model, current_model_num = apply_algorithm_if_bodies( algorithm, then_model, - NNCFGraphFactory.create(then_model), + graphs, then_dataset, subset_size, current_model_num + 1, @@ -192,7 +193,7 @@ def apply_algorithm_if_bodies( else_quantized_model, current_model_num = apply_algorithm_if_bodies( algorithm, else_model, - NNCFGraphFactory.create(else_model), + graphs, else_dataset, subset_size, current_model_num + 1, diff --git a/nncf/openvino/quantization/quantize_model.py b/nncf/openvino/quantization/quantize_model.py index 8b8f1b8dbdf..c9d39b48aa1 100644 --- a/nncf/openvino/quantization/quantize_model.py +++ b/nncf/openvino/quantization/quantize_model.py @@ -15,11 +15,14 @@ import openvino.runtime as ov from openvino._offline_transformations import compress_quantize_weights_transformation +import nncf from nncf.common.factory import NNCFGraphFactory from nncf.common.logging import nncf_logger from nncf.common.quantization.structs import QuantizationPreset from nncf.data import Dataset from nncf.openvino.graph.metatypes.groups import OPERATIONS_OUTPUT_HAS_NO_BATCH_AXIS +from nncf.openvino.graph.metatypes.openvino_metatypes import OVIfMetatype +from nncf.openvino.graph.metatypes.openvino_metatypes import get_node_metatype from nncf.openvino.graph.model_utils import remove_friendly_name_duplicates from nncf.openvino.graph.nncf_graph_builder import GraphConverter from nncf.openvino.graph.node_utils import get_number_if_op @@ -42,16 +45,30 @@ from nncf.quantization.algorithms.accuracy_control.evaluator import Evaluator from nncf.quantization.algorithms.post_training.algorithm import PostTrainingQuantization from nncf.quantization.algorithms.weight_compression.algorithm import WeightCompression +from nncf.quantization.quantize_model import BATCHWISE_STATISTICS_WARNING +from nncf.quantization.quantize_model import is_model_no_batchwise_support from nncf.quantization.quantize_model import quantize_with_tune_hyperparams -from nncf.quantization.quantize_model import warning_model_no_batchwise_support from nncf.quantization.telemetry_extractors import CompressionStartedWithQuantizeApi from nncf.scopes import IgnoredScope +from nncf.scopes import error_unmatched_ignored_scope +from nncf.scopes import get_matches_from_ignored_scope +from nncf.scopes import get_unmatched_ignored_scope from nncf.telemetry.decorator import tracked_function from nncf.telemetry.events import NNCF_OV_CATEGORY TTensor = TypeVar("TTensor") +# DFS traversal number rule. +def get_all_graphs(model, graphs, current_cnt): + graphs[current_cnt] = NNCFGraphFactory.create(model) + for op in model.get_ops(): + if get_node_metatype(op) == OVIfMetatype: + current_cnt = get_all_graphs(op.get_function(0), graphs, current_cnt + 1) + current_cnt = get_all_graphs(op.get_function(1), graphs, current_cnt + 1) + return current_cnt + + @tracked_function(NNCF_OV_CATEGORY, [CompressionStartedWithQuantizeApi(), "target_device", "preset"]) def native_quantize_if_op_impl( model: ov.Model, @@ -72,6 +89,18 @@ def native_quantize_if_op_impl( raise NotImplementedError( "The BiasCorrection algorithm is not supported for OpenVINO models with If operation." ) + graphs = {} + get_all_graphs(model, graphs, 1) + if ignored_scope and ignored_scope.validate: + matches = {} + for graph in graphs.values(): + matches.update(get_matches_from_ignored_scope(ignored_scope, graph)) + unmatched = get_unmatched_ignored_scope(ignored_scope, matches) + if any(unmatched.values()): + raise nncf.ValidationError(error_unmatched_ignored_scope(unmatched, ignored_scope)) + ignored_scope = IgnoredScope( + ignored_scope.names, ignored_scope.patterns, ignored_scope.types, ignored_scope.subgraphs, validate=False + ) quantization_algorithm = PostTrainingQuantization( mode=mode, preset=preset, @@ -82,9 +111,10 @@ def native_quantize_if_op_impl( ignored_scope=ignored_scope, advanced_parameters=advanced_parameters, ) - - graph = GraphConverter.create_nncf_graph(model) - warning_model_no_batchwise_support(graph, advanced_parameters, model_type, OPERATIONS_OUTPUT_HAS_NO_BATCH_AXIS) + for graph in graphs.values(): + if is_model_no_batchwise_support(graph, advanced_parameters, model_type, OPERATIONS_OUTPUT_HAS_NO_BATCH_AXIS): + nncf_logger.warning(BATCHWISE_STATISTICS_WARNING) + break if_ops_number = get_number_if_op(model) all_models_number = if_ops_number * 2 + 1 nncf_logger.info( @@ -92,7 +122,7 @@ def native_quantize_if_op_impl( Main model and all If bodies will be quantized recursively." ) quantized_model, _ = apply_algorithm_if_bodies( - quantization_algorithm, model, graph, calibration_dataset, subset_size, 1, all_models_number + quantization_algorithm, model, graphs, calibration_dataset, subset_size, 1, all_models_number ) if is_weight_compression_needed(advanced_parameters): @@ -140,7 +170,8 @@ def native_quantize_impl( advanced_parameters=advanced_parameters, ) graph = GraphConverter.create_nncf_graph(model) - warning_model_no_batchwise_support(graph, advanced_parameters, model_type, OPERATIONS_OUTPUT_HAS_NO_BATCH_AXIS) + if is_model_no_batchwise_support(graph, advanced_parameters, model_type, OPERATIONS_OUTPUT_HAS_NO_BATCH_AXIS): + nncf_logger.warning(BATCHWISE_STATISTICS_WARNING) quantized_model = quantization_algorithm.apply(model, graph, dataset=calibration_dataset) if is_weight_compression_needed(advanced_parameters): diff --git a/nncf/quantization/quantize_model.py b/nncf/quantization/quantize_model.py index 6455eea8535..fec5b744e47 100644 --- a/nncf/quantization/quantize_model.py +++ b/nncf/quantization/quantize_model.py @@ -16,7 +16,6 @@ from nncf.common.deprecation import warning_deprecated from nncf.common.graph import NNCFGraph from nncf.common.graph.operator_metatypes import OperatorMetatype -from nncf.common.logging.logger import nncf_logger from nncf.common.quantization.structs import QuantizationPreset from nncf.common.utils.api_marker import api from nncf.common.utils.backend import BackendType @@ -47,7 +46,7 @@ ) -def warning_model_no_batchwise_support( +def is_model_no_batchwise_support( graph: NNCFGraph, advanced_quantization_parameters: Optional[AdvancedQuantizationParameters], model_type: ModelType, @@ -66,7 +65,8 @@ def warning_model_no_batchwise_support( and advanced_quantization_parameters.batchwise_statistics and (graph.get_nodes_by_metatypes(no_batchwise_support_metatypes) or model_type == ModelType.TRANSFORMER) ): - nncf_logger.warning(BATCHWISE_STATISTICS_WARNING) + return True + return False def _update_advanced_quantization_parameters( diff --git a/nncf/scopes.py b/nncf/scopes.py index c7cf631ad57..ad9184d46bb 100644 --- a/nncf/scopes.py +++ b/nncf/scopes.py @@ -10,9 +10,12 @@ # limitations under the License. import re +import typing +from collections import OrderedDict +from collections import defaultdict from dataclasses import dataclass from dataclasses import field -from typing import List, Optional, Set +from typing import Any, Dict, List, Optional, Set import nncf from nncf.common.graph.graph import NNCFGraph @@ -130,9 +133,7 @@ def convert_ignored_scope_to_list(ignored_scope: Optional[IgnoredScope]) -> List return results -def get_ignored_node_names_from_ignored_scope( - ignored_scope: IgnoredScope, nncf_graph: NNCFGraph, strict: bool = True -) -> Set[str]: +def get_matches_from_ignored_scope(ignored_scope: IgnoredScope, nncf_graph: NNCFGraph) -> typing.OrderedDict[str, Dict]: """ Returns ignored names according to ignored scope and NNCFGraph. If strict is True, raises RuntimeError if any ignored name is not found in the NNCFGraph or @@ -144,64 +145,98 @@ def get_ignored_node_names_from_ignored_scope( :param strict: Whether all ignored_scopes must match at least one node or not. :returns: NNCF node names from given NNCFGraph specified in given ignored scope. """ - error_msg = ( + matches = OrderedDict() + node_names = set(node.node_name for node in nncf_graph.nodes.values()) + + matched_by_names = {} + for ignored_node_name in ignored_scope.names: + if ignored_node_name in node_names: + matched_by_names[ignored_node_name] = ignored_node_name + matches["name"] = matched_by_names + + matched_by_patterns = defaultdict(set) + for str_pattern in ignored_scope.patterns: + pattern = re.compile(str_pattern) + matches = list(filter(pattern.match, node_names)) + matched_by_patterns[str_pattern].update(matches) + matches["patterns"] = matched_by_patterns + + matched_by_types = defaultdict(set) + ignored_scope_types = set(ignored_scope.types) + for node in nncf_graph.get_nodes_by_types(ignored_scope_types): + matched_by_types[node.node_type].add(node.node_name) + matches["types"] = matched_by_types + + matched_by_subgraphs = defaultdict(set) + for i, subgraph in enumerate(ignored_scope.subgraphs): + names_from_subgraph = get_ignored_node_names_from_subgraph(nncf_graph, subgraph) + matched_by_subgraphs[i].update(names_from_subgraph) + matches["subgraphs"] = matched_by_subgraphs + return matches + + +def get_unmatched_ignored_scope( + ignored_scope: IgnoredScope, matches: Dict[str, Dict[Any, Set[str]]] +) -> typing.OrderedDict[str, Dict]: + return OrderedDict( + { + "names": [name for name in ignored_scope.names if name not in matches["names"]], + "patterns": [pattern for pattern in ignored_scope.patterns if pattern not in matches["patterns"]], + "types": [type for type in ignored_scope.types if type not in matches["types"]], + "subgraphs": [ + subgraph_i + for subgraph_i in range(len(ignored_scope.subgraphs)) + if subgraph_i not in matches["subgraphs"] + ], + } + ) + + +def info_matched_ignored_scope(matches): + for rule_type, rules in matches.items(): + total = 0 + for matched_nodes in rules.values(): + total += len(matched_nodes) + if total: + nncf_logger.info(f"{total} ignored nodes were found by {rule_type} in the NNCFGraph") + + +def error_unmatched_ignored_scope(unmatched, ignored_scope: IgnoredScope): + err_msg = "" + for rule_type, unmatched_rule in unmatched.items(): + if unmatched_rule: + if rule_type == "names": + err_msg += f"Ignored nodes with name {list(unmatched_rule)} were not found in the NNCFGraph. " + if rule_type == "patterns": + err_msg += f"No matches for ignored patterns {list(unmatched_rule)} in the NNCFGraph. " + if rule_type == "patterns": + err_msg += f"Nodes with ignored types {list(unmatched_rule)} were not found in the NNCFGraph. " + if rule_type == "subgraphs": + for subgraph in unmatched_rule: + inps, outs = ignored_scope.subgraphs[subgraph].inputs, ignored_scope.subgraphs[subgraph].outputs + err_msg += ( + f"Ignored subgraph with input names {inps} and output names {outs} " + "was not found in the NNCFGraph. " + ) + return err_msg + ( "Refer to the original_graph.dot to discover the operations" "in the model currently visible to NNCF and specify the ignored/target" " scopes in terms of the names there." ) - node_names = [node.node_name for node in nncf_graph.get_all_nodes()] - matched_by_names = [] - if ignored_scope.names: - for ignored_node_name in ignored_scope.names: - if ignored_node_name in node_names: - matched_by_names.append(ignored_node_name) - if strict and len(ignored_scope.names) != len(matched_by_names): - skipped_names = set(ignored_scope.names) - set(matched_by_names) - raise nncf.ValidationError( - f"Ignored nodes with name {list(skipped_names)} were not found in the NNCFGraph. " + error_msg - ) - nncf_logger.info(f"{len(matched_by_names)} ignored nodes were found by name in the NNCFGraph") - - matched_by_patterns = [] - if ignored_scope.patterns: - not_matched_patterns = [] - for str_pattern in ignored_scope.patterns: - pattern = re.compile(str_pattern) - matches = list(filter(pattern.match, node_names)) - if not matches: - not_matched_patterns.append(str_pattern) - matched_by_patterns.extend(matches) - if strict and not_matched_patterns: - raise nncf.ValidationError( - f"No matches for ignored patterns {not_matched_patterns} in the NNCFGraph. " + error_msg - ) - nncf_logger.info(f"{len(matched_by_patterns)} ignored nodes were found by patterns in the NNCFGraph") - - matched_by_types = [] - if ignored_scope.types: - types_found = set() - for node in nncf_graph.get_all_nodes(): - if node.node_type in ignored_scope.types: - types_found.add(node.node_type) - matched_by_types.append(node.node_name) - not_matched_types = set(ignored_scope.types) - types_found - if strict and not_matched_types: - raise nncf.ValidationError( - f"Nodes with ignored types {list(not_matched_types)} were not found in the NNCFGraph. " + error_msg - ) - nncf_logger.info(f"{len(matched_by_types)} ignored nodes were found by types in the NNCFGraph") - - matched_by_subgraphs = [] - if ignored_scope.subgraphs: - for subgraph in ignored_scope.subgraphs: - names_from_subgraph = get_ignored_node_names_from_subgraph(nncf_graph, subgraph) - if strict and not names_from_subgraph: - raise nncf.ValidationError( - f"Ignored subgraph with input names {subgraph.inputs} and output names {subgraph.outputs} " - "was not found in the NNCFGraph. " + error_msg - ) - - matched_by_subgraphs.extend(names_from_subgraph) - - return set(matched_by_names + matched_by_types + matched_by_patterns + matched_by_subgraphs) + +def get_ignored_node_names(matches: Dict[str, Dict[Any, Set[str]]]): + output = set() + for rules in matches.values(): + for matched_nodes in rules.values(): + output.update(matched_nodes) + return output + + +def get_ignored_node_names_from_ignored_scope(ignored_scope: IgnoredScope, nncf_graph: NNCFGraph, strict): + matches = get_matches_from_ignored_scope(ignored_scope, nncf_graph) + unmatched = get_unmatched_ignored_scope(ignored_scope, matches) + if strict and any(unmatched.values()): + raise nncf.ValidationError(error_unmatched_ignored_scope(unmatched, ignored_scope)) + info_matched_ignored_scope(matches) + return get_ignored_node_names(matches) From 5d6edacaacfdaa9080d8ec5317ddbfb1d093d053 Mon Sep 17 00:00:00 2001 From: Aleksei Kashapov Date: Thu, 6 Jun 2024 11:44:18 +0200 Subject: [PATCH 02/30] introduce IgnoredScopeMatch --- .../openvino/quantization/quantize_ifmodel.py | 5 +- nncf/openvino/quantization/quantize_model.py | 22 ++- nncf/scopes.py | 144 +++++++++--------- 3 files changed, 86 insertions(+), 85 deletions(-) diff --git a/nncf/openvino/quantization/quantize_ifmodel.py b/nncf/openvino/quantization/quantize_ifmodel.py index 9bca7ac80a5..f7939dd31b0 100644 --- a/nncf/openvino/quantization/quantize_ifmodel.py +++ b/nncf/openvino/quantization/quantize_ifmodel.py @@ -139,7 +139,6 @@ def apply_algorithm_if_bodies( parent_dataset: Dataset, subset_size: int, current_model_num: int, - all_models_num: int, parent_statistic_points: Optional[StatisticPointsContainer] = None, ) -> Tuple[ov.Model, int]: """ @@ -154,7 +153,7 @@ def apply_algorithm_if_bodies( :param parent_statistic_points: Statistics points for algorithm. :return: A model for every bodies of If nodes the algorithm was applied and the latest model number. """ - nncf_logger.info(f"Iteration [{current_model_num}/{all_models_num}] ...") + nncf_logger.info(f"Iteration [{current_model_num}/{len(graphs)}] ...") parent_graph = graphs[current_model_num] quantized_model = algorithm.apply(parent_model, parent_graph, parent_statistic_points, parent_dataset) if get_number_if_op(parent_model) == 0: @@ -188,7 +187,6 @@ def apply_algorithm_if_bodies( then_dataset, subset_size, current_model_num + 1, - all_models_num, ) else_quantized_model, current_model_num = apply_algorithm_if_bodies( algorithm, @@ -197,7 +195,6 @@ def apply_algorithm_if_bodies( else_dataset, subset_size, current_model_num + 1, - all_models_num, ) model_transformer_int8 = factory.ModelTransformerFactory.create(quantized_model) quantized_model = _update_if_body(model_transformer_int8, if_node, True, then_quantized_model) diff --git a/nncf/openvino/quantization/quantize_model.py b/nncf/openvino/quantization/quantize_model.py index c9d39b48aa1..7729b0f2c2e 100644 --- a/nncf/openvino/quantization/quantize_model.py +++ b/nncf/openvino/quantization/quantize_model.py @@ -51,8 +51,9 @@ from nncf.quantization.telemetry_extractors import CompressionStartedWithQuantizeApi from nncf.scopes import IgnoredScope from nncf.scopes import error_unmatched_ignored_scope -from nncf.scopes import get_matches_from_ignored_scope +from nncf.scopes import get_matched_ignored_scope from nncf.scopes import get_unmatched_ignored_scope +from nncf.scopes import merge_ignored_scopes from nncf.telemetry.decorator import tracked_function from nncf.telemetry.events import NNCF_OV_CATEGORY @@ -92,12 +93,18 @@ def native_quantize_if_op_impl( graphs = {} get_all_graphs(model, graphs, 1) if ignored_scope and ignored_scope.validate: - matches = {} + matched_ignored_scope = IgnoredScope() for graph in graphs.values(): - matches.update(get_matches_from_ignored_scope(ignored_scope, graph)) - unmatched = get_unmatched_ignored_scope(ignored_scope, matches) - if any(unmatched.values()): - raise nncf.ValidationError(error_unmatched_ignored_scope(unmatched, ignored_scope)) + match = get_matched_ignored_scope(ignored_scope, graph) + matched_ignored_scope = merge_ignored_scopes(matched_ignored_scope, match.matched_ignored_scope) + unmatched_ignored_scope = get_unmatched_ignored_scope(ignored_scope, matched_ignored_scope) + if ( + any(unmatched_ignored_scope.names) + or any(unmatched_ignored_scope.types) + or any(unmatched_ignored_scope.patterns) + or any(unmatched_ignored_scope.subgraphs) + ): + raise nncf.ValidationError(error_unmatched_ignored_scope(unmatched_ignored_scope)) ignored_scope = IgnoredScope( ignored_scope.names, ignored_scope.patterns, ignored_scope.types, ignored_scope.subgraphs, validate=False ) @@ -116,13 +123,12 @@ def native_quantize_if_op_impl( nncf_logger.warning(BATCHWISE_STATISTICS_WARNING) break if_ops_number = get_number_if_op(model) - all_models_number = if_ops_number * 2 + 1 nncf_logger.info( f"The model consists of {if_ops_number} If node(-s) with then and else bodies. \ Main model and all If bodies will be quantized recursively." ) quantized_model, _ = apply_algorithm_if_bodies( - quantization_algorithm, model, graphs, calibration_dataset, subset_size, 1, all_models_number + quantization_algorithm, model, graphs, calibration_dataset, subset_size, 1 ) if is_weight_compression_needed(advanced_parameters): diff --git a/nncf/scopes.py b/nncf/scopes.py index ad9184d46bb..26786e85ddd 100644 --- a/nncf/scopes.py +++ b/nncf/scopes.py @@ -9,13 +9,13 @@ # See the License for the specific language governing permissions and # limitations under the License. +import collections +import itertools import re -import typing -from collections import OrderedDict -from collections import defaultdict +from dataclasses import asdict from dataclasses import dataclass from dataclasses import field -from typing import Any, Dict, List, Optional, Set +from typing import List, Optional, OrderedDict, Set import nncf from nncf.common.graph.graph import NNCFGraph @@ -133,7 +133,23 @@ def convert_ignored_scope_to_list(ignored_scope: Optional[IgnoredScope]) -> List return results -def get_matches_from_ignored_scope(ignored_scope: IgnoredScope, nncf_graph: NNCFGraph) -> typing.OrderedDict[str, Dict]: +def merge_ignored_scopes(ignored_scope_1: IgnoredScope, ignored_scope_2: IgnoredScope): + assert ignored_scope_1.validate == ignored_scope_2.validate + d1 = asdict(ignored_scope_1) + for attr, val in asdict(ignored_scope_2).items(): + if attr == "validate": + continue + d1[attr] = list(set(itertools.chain(d1[attr], val))) + return IgnoredScope(**d1, validate=ignored_scope_1.validate) + + +@dataclass +class IgnoredScopeMatch: + matched_ignored_scope: IgnoredScope = IgnoredScope() + matches: OrderedDict[str, Set[str]] = field(default_factory=lambda: OrderedDict(dict)) + + +def get_matched_ignored_scope(ignored_scope: IgnoredScope, nncf_graph: NNCFGraph) -> IgnoredScopeMatch: """ Returns ignored names according to ignored scope and NNCFGraph. If strict is True, raises RuntimeError if any ignored name is not found in the NNCFGraph or @@ -145,79 +161,64 @@ def get_matches_from_ignored_scope(ignored_scope: IgnoredScope, nncf_graph: NNCF :param strict: Whether all ignored_scopes must match at least one node or not. :returns: NNCF node names from given NNCFGraph specified in given ignored scope. """ - matches = OrderedDict() + + matched_ignored_scope = IgnoredScope() + matches = collections.OrderedDict({"name": set(), "patterns": set(), "types": set(), "subgraphs": set()}) node_names = set(node.node_name for node in nncf_graph.nodes.values()) - matched_by_names = {} for ignored_node_name in ignored_scope.names: if ignored_node_name in node_names: - matched_by_names[ignored_node_name] = ignored_node_name - matches["name"] = matched_by_names + matches["name"].add(ignored_node_name) + matched_ignored_scope.names.append(ignored_node_name) - matched_by_patterns = defaultdict(set) for str_pattern in ignored_scope.patterns: pattern = re.compile(str_pattern) matches = list(filter(pattern.match, node_names)) - matched_by_patterns[str_pattern].update(matches) - matches["patterns"] = matched_by_patterns + matches["patterns"].add(matches) + matched_ignored_scope.patterns.append(str_pattern) - matched_by_types = defaultdict(set) ignored_scope_types = set(ignored_scope.types) for node in nncf_graph.get_nodes_by_types(ignored_scope_types): - matched_by_types[node.node_type].add(node.node_name) - matches["types"] = matched_by_types + matches["types"].add(node.node_name) + matched_ignored_scope.types.append(node.node_type) - matched_by_subgraphs = defaultdict(set) - for i, subgraph in enumerate(ignored_scope.subgraphs): + for subgraph in ignored_scope.subgraphs: names_from_subgraph = get_ignored_node_names_from_subgraph(nncf_graph, subgraph) - matched_by_subgraphs[i].update(names_from_subgraph) - matches["subgraphs"] = matched_by_subgraphs - return matches - - -def get_unmatched_ignored_scope( - ignored_scope: IgnoredScope, matches: Dict[str, Dict[Any, Set[str]]] -) -> typing.OrderedDict[str, Dict]: - return OrderedDict( - { - "names": [name for name in ignored_scope.names if name not in matches["names"]], - "patterns": [pattern for pattern in ignored_scope.patterns if pattern not in matches["patterns"]], - "types": [type for type in ignored_scope.types if type not in matches["types"]], - "subgraphs": [ - subgraph_i - for subgraph_i in range(len(ignored_scope.subgraphs)) - if subgraph_i not in matches["subgraphs"] - ], - } + matches["subgraphs"].update(names_from_subgraph) + matched_ignored_scope.subgraphs.append(subgraph) + return IgnoredScopeMatch(matched_ignored_scope, matches) + + +def get_unmatched_ignored_scope(matched_ignored_scope: IgnoredScope, ignored_scope: IgnoredScope) -> IgnoredScope: + return IgnoredScope( + names=[name for name in ignored_scope.names if name not in matched_ignored_scope.names], + patterns=[pattern for pattern in ignored_scope.patterns if pattern not in matched_ignored_scope.patterns], + types=[type for type in ignored_scope.types if type not in matched_ignored_scope.types], + subgraphs=[subgraph for subgraph in ignored_scope.subgraphs if subgraph not in matched_ignored_scope.subgraphs], ) -def info_matched_ignored_scope(matches): +def _info_matched_ignored_scope(matches): + info_msg = "" for rule_type, rules in matches.items(): - total = 0 - for matched_nodes in rules.values(): - total += len(matched_nodes) - if total: - nncf_logger.info(f"{total} ignored nodes were found by {rule_type} in the NNCFGraph") + if rules: + info_msg += f"{len(rules)} ignored nodes were found by {rule_type} in the NNCFGraph" + return info_msg -def error_unmatched_ignored_scope(unmatched, ignored_scope: IgnoredScope): +def _error_unmatched_ignored_scope(unmatched_ignored_scope: IgnoredScope): err_msg = "" - for rule_type, unmatched_rule in unmatched.items(): - if unmatched_rule: - if rule_type == "names": - err_msg += f"Ignored nodes with name {list(unmatched_rule)} were not found in the NNCFGraph. " - if rule_type == "patterns": - err_msg += f"No matches for ignored patterns {list(unmatched_rule)} in the NNCFGraph. " - if rule_type == "patterns": - err_msg += f"Nodes with ignored types {list(unmatched_rule)} were not found in the NNCFGraph. " - if rule_type == "subgraphs": - for subgraph in unmatched_rule: - inps, outs = ignored_scope.subgraphs[subgraph].inputs, ignored_scope.subgraphs[subgraph].outputs - err_msg += ( - f"Ignored subgraph with input names {inps} and output names {outs} " - "was not found in the NNCFGraph. " - ) + if unmatched_ignored_scope.names: + err_msg += f"Ignored nodes with name {unmatched_ignored_scope.names} were not found in the NNCFGraph. " + if unmatched_ignored_scope.patterns: + err_msg += f"No matches for ignored patterns {unmatched_ignored_scope.patterns} in the NNCFGraph. " + if unmatched_ignored_scope.types: + err_msg += f"Nodes with ignored types {unmatched_ignored_scope.types} were not found in the NNCFGraph. " + for subgraph in unmatched_ignored_scope.subgraphs: + err_msg += ( + f"Ignored subgraph with input names {subgraph.inputs} and output names {subgraph.outputs} " + "was not found in the NNCFGraph. " + ) return err_msg + ( "Refer to the original_graph.dot to discover the operations" "in the model currently visible to NNCF and specify the ignored/target" @@ -225,18 +226,15 @@ def error_unmatched_ignored_scope(unmatched, ignored_scope: IgnoredScope): ) -def get_ignored_node_names(matches: Dict[str, Dict[Any, Set[str]]]): - output = set() - for rules in matches.values(): - for matched_nodes in rules.values(): - output.update(matched_nodes) - return output - - -def get_ignored_node_names_from_ignored_scope(ignored_scope: IgnoredScope, nncf_graph: NNCFGraph, strict): - matches = get_matches_from_ignored_scope(ignored_scope, nncf_graph) - unmatched = get_unmatched_ignored_scope(ignored_scope, matches) - if strict and any(unmatched.values()): - raise nncf.ValidationError(error_unmatched_ignored_scope(unmatched, ignored_scope)) - info_matched_ignored_scope(matches) - return get_ignored_node_names(matches) +def get_ignored_node_names_from_ignored_scope(ignored_scope: IgnoredScope, nncf_graph: NNCFGraph, strict: bool = True): + match = get_matched_ignored_scope(ignored_scope, nncf_graph) + unmatched_ignored_scope = get_unmatched_ignored_scope(match.matched_ignored_scope, ignored_scope) + if strict and ( + any(unmatched_ignored_scope.names) + or any(unmatched_ignored_scope.types) + or any(unmatched_ignored_scope.patterns) + or any(unmatched_ignored_scope.subgraphs) + ): + raise nncf.ValidationError(_error_unmatched_ignored_scope(unmatched_ignored_scope, ignored_scope)) + nncf_logger.info(_info_matched_ignored_scope(match.matches)) + return {name for match in match.matches.values() for name in match} From 3bb3cdab997fd3eacf6142d883ccfaa4998c812a Mon Sep 17 00:00:00 2001 From: Aleksei Kashapov Date: Thu, 6 Jun 2024 12:12:46 +0200 Subject: [PATCH 03/30] minor --- nncf/openvino/quantization/quantize_model.py | 40 ++++++-------------- nncf/scopes.py | 36 ++++++++++++------ 2 files changed, 36 insertions(+), 40 deletions(-) diff --git a/nncf/openvino/quantization/quantize_model.py b/nncf/openvino/quantization/quantize_model.py index 7729b0f2c2e..79e8bf73c72 100644 --- a/nncf/openvino/quantization/quantize_model.py +++ b/nncf/openvino/quantization/quantize_model.py @@ -15,7 +15,6 @@ import openvino.runtime as ov from openvino._offline_transformations import compress_quantize_weights_transformation -import nncf from nncf.common.factory import NNCFGraphFactory from nncf.common.logging import nncf_logger from nncf.common.quantization.structs import QuantizationPreset @@ -50,26 +49,13 @@ from nncf.quantization.quantize_model import quantize_with_tune_hyperparams from nncf.quantization.telemetry_extractors import CompressionStartedWithQuantizeApi from nncf.scopes import IgnoredScope -from nncf.scopes import error_unmatched_ignored_scope -from nncf.scopes import get_matched_ignored_scope -from nncf.scopes import get_unmatched_ignored_scope -from nncf.scopes import merge_ignored_scopes +from nncf.scopes import validate_ignored_scope from nncf.telemetry.decorator import tracked_function from nncf.telemetry.events import NNCF_OV_CATEGORY TTensor = TypeVar("TTensor") -# DFS traversal number rule. -def get_all_graphs(model, graphs, current_cnt): - graphs[current_cnt] = NNCFGraphFactory.create(model) - for op in model.get_ops(): - if get_node_metatype(op) == OVIfMetatype: - current_cnt = get_all_graphs(op.get_function(0), graphs, current_cnt + 1) - current_cnt = get_all_graphs(op.get_function(1), graphs, current_cnt + 1) - return current_cnt - - @tracked_function(NNCF_OV_CATEGORY, [CompressionStartedWithQuantizeApi(), "target_device", "preset"]) def native_quantize_if_op_impl( model: ov.Model, @@ -91,20 +77,18 @@ def native_quantize_if_op_impl( "The BiasCorrection algorithm is not supported for OpenVINO models with If operation." ) graphs = {} - get_all_graphs(model, graphs, 1) + + def _get_all_graphs(model, current_cnt): + for op in model.get_ops(): + if get_node_metatype(op) == OVIfMetatype: + _get_all_graphs(op.get_function(0), current_cnt + 1) + _get_all_graphs(op.get_function(1), current_cnt + 2) + graphs[current_cnt] = NNCFGraphFactory.create(model) + return graphs + + _get_all_graphs(model, 1) if ignored_scope and ignored_scope.validate: - matched_ignored_scope = IgnoredScope() - for graph in graphs.values(): - match = get_matched_ignored_scope(ignored_scope, graph) - matched_ignored_scope = merge_ignored_scopes(matched_ignored_scope, match.matched_ignored_scope) - unmatched_ignored_scope = get_unmatched_ignored_scope(ignored_scope, matched_ignored_scope) - if ( - any(unmatched_ignored_scope.names) - or any(unmatched_ignored_scope.types) - or any(unmatched_ignored_scope.patterns) - or any(unmatched_ignored_scope.subgraphs) - ): - raise nncf.ValidationError(error_unmatched_ignored_scope(unmatched_ignored_scope)) + validate_ignored_scope(ignored_scope, graphs) ignored_scope = IgnoredScope( ignored_scope.names, ignored_scope.patterns, ignored_scope.types, ignored_scope.subgraphs, validate=False ) diff --git a/nncf/scopes.py b/nncf/scopes.py index 26786e85ddd..a4f58843cfc 100644 --- a/nncf/scopes.py +++ b/nncf/scopes.py @@ -140,7 +140,7 @@ def merge_ignored_scopes(ignored_scope_1: IgnoredScope, ignored_scope_2: Ignored if attr == "validate": continue d1[attr] = list(set(itertools.chain(d1[attr], val))) - return IgnoredScope(**d1, validate=ignored_scope_1.validate) + return IgnoredScope(**d1) @dataclass @@ -166,10 +166,9 @@ def get_matched_ignored_scope(ignored_scope: IgnoredScope, nncf_graph: NNCFGraph matches = collections.OrderedDict({"name": set(), "patterns": set(), "types": set(), "subgraphs": set()}) node_names = set(node.node_name for node in nncf_graph.nodes.values()) - for ignored_node_name in ignored_scope.names: - if ignored_node_name in node_names: - matches["name"].add(ignored_node_name) - matched_ignored_scope.names.append(ignored_node_name) + for ignored_node_name in filter(lambda name: name in node_names, ignored_scope.names): + matches["name"].add(ignored_node_name) + matched_ignored_scope.names.append(ignored_node_name) for str_pattern in ignored_scope.patterns: pattern = re.compile(str_pattern) @@ -198,15 +197,13 @@ def get_unmatched_ignored_scope(matched_ignored_scope: IgnoredScope, ignored_sco ) -def _info_matched_ignored_scope(matches): - info_msg = "" +def info_matched_ignored_scope(matches): for rule_type, rules in matches.items(): if rules: - info_msg += f"{len(rules)} ignored nodes were found by {rule_type} in the NNCFGraph" - return info_msg + nncf_logger.info(f"{len(rules)} ignored nodes were found by {rule_type} in the NNCFGraph") -def _error_unmatched_ignored_scope(unmatched_ignored_scope: IgnoredScope): +def get_error_unmatched_ignored_scope(unmatched_ignored_scope: IgnoredScope): err_msg = "" if unmatched_ignored_scope.names: err_msg += f"Ignored nodes with name {unmatched_ignored_scope.names} were not found in the NNCFGraph. " @@ -235,6 +232,21 @@ def get_ignored_node_names_from_ignored_scope(ignored_scope: IgnoredScope, nncf_ or any(unmatched_ignored_scope.patterns) or any(unmatched_ignored_scope.subgraphs) ): - raise nncf.ValidationError(_error_unmatched_ignored_scope(unmatched_ignored_scope, ignored_scope)) - nncf_logger.info(_info_matched_ignored_scope(match.matches)) + raise nncf.ValidationError(get_error_unmatched_ignored_scope(unmatched_ignored_scope, ignored_scope)) + info_matched_ignored_scope(match.matches) return {name for match in match.matches.values() for name in match} + + +def validate_ignored_scope(ignored_scope, graphs): + matched_ignored_scope = IgnoredScope() + for graph in graphs.values(): + match = get_matched_ignored_scope(ignored_scope, graph) + matched_ignored_scope = merge_ignored_scopes(matched_ignored_scope, match.matched_ignored_scope) + unmatched_ignored_scope = get_unmatched_ignored_scope(ignored_scope, matched_ignored_scope) + if ( + any(unmatched_ignored_scope.names) + or any(unmatched_ignored_scope.types) + or any(unmatched_ignored_scope.patterns) + or any(unmatched_ignored_scope.subgraphs) + ): + raise nncf.ValidationError(get_error_unmatched_ignored_scope(unmatched_ignored_scope)) From 9948daa654fb264ac3009394ac5d1dfc87174f15 Mon Sep 17 00:00:00 2001 From: Aleksei Kashapov Date: Thu, 6 Jun 2024 12:58:36 +0200 Subject: [PATCH 04/30] code polishing --- .../openvino/quantization/quantize_ifmodel.py | 4 +- nncf/openvino/quantization/quantize_model.py | 5 +- nncf/scopes.py | 146 ++++++++++-------- 3 files changed, 88 insertions(+), 67 deletions(-) diff --git a/nncf/openvino/quantization/quantize_ifmodel.py b/nncf/openvino/quantization/quantize_ifmodel.py index f7939dd31b0..a6c6336f91e 100644 --- a/nncf/openvino/quantization/quantize_ifmodel.py +++ b/nncf/openvino/quantization/quantize_ifmodel.py @@ -131,7 +131,6 @@ def _add_outputs_before_if_node(model_transformer: ModelTransformer, model: ov.M return model_transformer.transform(transformation_layout) -# MAYBE IMPLEMENT THROUH TREE STRICTUE? def apply_algorithm_if_bodies( algorithm: Algorithm, parent_model: ov.Model, @@ -145,11 +144,10 @@ def apply_algorithm_if_bodies( Applies an algorithm recursievley to each bodies of If node. :param parent_model: Model to apply algorithm. - :param parent_graph: Graph of a model. TODO:fix + :param graphs: Mapping from model_number and its graph. :param parent_dataset: Dataset for algorithm. :param subset_size: Size of a dataset to use for calibration. :param current_model_num: Current model number. - :param all_models_num: All model numbers. :param parent_statistic_points: Statistics points for algorithm. :return: A model for every bodies of If nodes the algorithm was applied and the latest model number. """ diff --git a/nncf/openvino/quantization/quantize_model.py b/nncf/openvino/quantization/quantize_model.py index 79e8bf73c72..91260d9c983 100644 --- a/nncf/openvino/quantization/quantize_model.py +++ b/nncf/openvino/quantization/quantize_model.py @@ -49,6 +49,7 @@ from nncf.quantization.quantize_model import quantize_with_tune_hyperparams from nncf.quantization.telemetry_extractors import CompressionStartedWithQuantizeApi from nncf.scopes import IgnoredScope +from nncf.scopes import get_ignored_scope_match from nncf.scopes import validate_ignored_scope from nncf.telemetry.decorator import tracked_function from nncf.telemetry.events import NNCF_OV_CATEGORY @@ -88,7 +89,9 @@ def _get_all_graphs(model, current_cnt): _get_all_graphs(model, 1) if ignored_scope and ignored_scope.validate: - validate_ignored_scope(ignored_scope, graphs) + validate_ignored_scope( + ignored_scope, get_ignored_scope_match(ignored_scope, graphs.values()).matched_ignored_scope + ) ignored_scope = IgnoredScope( ignored_scope.names, ignored_scope.patterns, ignored_scope.types, ignored_scope.subgraphs, validate=False ) diff --git a/nncf/scopes.py b/nncf/scopes.py index a4f58843cfc..c3edabfcb65 100644 --- a/nncf/scopes.py +++ b/nncf/scopes.py @@ -10,9 +10,7 @@ # limitations under the License. import collections -import itertools import re -from dataclasses import asdict from dataclasses import dataclass from dataclasses import field from typing import List, Optional, OrderedDict, Set @@ -133,77 +131,91 @@ def convert_ignored_scope_to_list(ignored_scope: Optional[IgnoredScope]) -> List return results -def merge_ignored_scopes(ignored_scope_1: IgnoredScope, ignored_scope_2: IgnoredScope): - assert ignored_scope_1.validate == ignored_scope_2.validate - d1 = asdict(ignored_scope_1) - for attr, val in asdict(ignored_scope_2).items(): - if attr == "validate": - continue - d1[attr] = list(set(itertools.chain(d1[attr], val))) - return IgnoredScope(**d1) - - @dataclass class IgnoredScopeMatch: matched_ignored_scope: IgnoredScope = IgnoredScope() matches: OrderedDict[str, Set[str]] = field(default_factory=lambda: OrderedDict(dict)) -def get_matched_ignored_scope(ignored_scope: IgnoredScope, nncf_graph: NNCFGraph) -> IgnoredScopeMatch: +def get_ignored_scope_match(ignored_scope: IgnoredScope, nncf_graphs: List[NNCFGraph]) -> IgnoredScopeMatch: """ - Returns ignored names according to ignored scope and NNCFGraph. - If strict is True, raises RuntimeError if any ignored name is not found in the NNCFGraph or - any ignored pattern or any ignored type match 0 nodes in the NNCFGraph. - If strict is False, returns all possible matches. + Returns ignored scope match for provided NNCFGraphs. + The resulted match is a union of all matches across graphs. - :param ignored_scope: Given ignored scope instance. - :param nncf_graph: Given NNCFGraph. - :param strict: Whether all ignored_scopes must match at least one node or not. - :returns: NNCF node names from given NNCFGraph specified in given ignored scope. + :param ignored_scope: Ignored scope instance. + :param nncf_graphs: NNCFGraphs. + :returns: ignored scope match united all mathces across graphs """ - - matched_ignored_scope = IgnoredScope() - matches = collections.OrderedDict({"name": set(), "patterns": set(), "types": set(), "subgraphs": set()}) - node_names = set(node.node_name for node in nncf_graph.nodes.values()) - - for ignored_node_name in filter(lambda name: name in node_names, ignored_scope.names): - matches["name"].add(ignored_node_name) - matched_ignored_scope.names.append(ignored_node_name) - - for str_pattern in ignored_scope.patterns: - pattern = re.compile(str_pattern) - matches = list(filter(pattern.match, node_names)) - matches["patterns"].add(matches) - matched_ignored_scope.patterns.append(str_pattern) - - ignored_scope_types = set(ignored_scope.types) - for node in nncf_graph.get_nodes_by_types(ignored_scope_types): - matches["types"].add(node.node_name) - matched_ignored_scope.types.append(node.node_type) - - for subgraph in ignored_scope.subgraphs: - names_from_subgraph = get_ignored_node_names_from_subgraph(nncf_graph, subgraph) - matches["subgraphs"].update(names_from_subgraph) - matched_ignored_scope.subgraphs.append(subgraph) + names, patterns, types, subgraphs_numbers = set(), set(), set(), set() + for graph in nncf_graphs: + matches = collections.OrderedDict({"name": set(), "patterns": set(), "types": set(), "subgraphs": set()}) + node_names = set(node.node_name for node in graph.nodes.values()) + + for ignored_node_name in filter(lambda name: name in node_names, ignored_scope.names): + matches["name"].add(ignored_node_name) + names.add(ignored_node_name) + + for str_pattern in ignored_scope.patterns: + pattern = re.compile(str_pattern) + matches = list(filter(pattern.match, node_names)) + matches["patterns"].add(matches) + patterns.add(str_pattern) + + ignored_scope_types = set(ignored_scope.types) + for node in graph.get_nodes_by_types(ignored_scope_types): + matches["types"].add(node.node_name) + types.add(node.node_type) + + for i, subgraph in enumerate(ignored_scope.subgraphs): + names_from_subgraph = get_ignored_node_names_from_subgraph(graph, subgraph) + matches["subgraphs"].update(names_from_subgraph) + subgraphs_numbers.add(i) + matched_ignored_scope = IgnoredScope( + names=list(names), + patterns=list(patterns), + types=list(types), + subgraphs=[subgraph for i, subgraph in enumerate(ignored_scope.subgraphs) if i in subgraphs_numbers], + validate=ignored_scope.validate, + ) return IgnoredScopeMatch(matched_ignored_scope, matches) def get_unmatched_ignored_scope(matched_ignored_scope: IgnoredScope, ignored_scope: IgnoredScope) -> IgnoredScope: + """ + Returns unmatched ignored scope rules from full ignored scope and matched ignored scope. + + :param matched_ignored_scope: Matched ingored scope. + :param ignored_scope: Full ignored scope. + :return: Unmatched ignored scope. + """ + assert matched_ignored_scope.validate == ignored_scope.validate return IgnoredScope( names=[name for name in ignored_scope.names if name not in matched_ignored_scope.names], patterns=[pattern for pattern in ignored_scope.patterns if pattern not in matched_ignored_scope.patterns], types=[type for type in ignored_scope.types if type not in matched_ignored_scope.types], subgraphs=[subgraph for subgraph in ignored_scope.subgraphs if subgraph not in matched_ignored_scope.subgraphs], + validate=matched_ignored_scope.validate, ) -def info_matched_ignored_scope(matches): +def info_matched_ignored_scope(matches) -> None: + """ + Log matches. + + :param matches: Matches. + """ for rule_type, rules in matches.items(): if rules: nncf_logger.info(f"{len(rules)} ignored nodes were found by {rule_type} in the NNCFGraph") -def get_error_unmatched_ignored_scope(unmatched_ignored_scope: IgnoredScope): +def error_unmatched_ignored_scope(unmatched_ignored_scope: IgnoredScope) -> str: + """ + Returns an error message for unmatched ignored scope. + + :param unmatched_ignored_scope: Unmatched ignored scope. + :return str: Error message. + """ err_msg = "" if unmatched_ignored_scope.names: err_msg += f"Ignored nodes with name {unmatched_ignored_scope.names} were not found in the NNCFGraph. " @@ -223,25 +235,33 @@ def get_error_unmatched_ignored_scope(unmatched_ignored_scope: IgnoredScope): ) -def get_ignored_node_names_from_ignored_scope(ignored_scope: IgnoredScope, nncf_graph: NNCFGraph, strict: bool = True): - match = get_matched_ignored_scope(ignored_scope, nncf_graph) - unmatched_ignored_scope = get_unmatched_ignored_scope(match.matched_ignored_scope, ignored_scope) - if strict and ( - any(unmatched_ignored_scope.names) - or any(unmatched_ignored_scope.types) - or any(unmatched_ignored_scope.patterns) - or any(unmatched_ignored_scope.subgraphs) - ): - raise nncf.ValidationError(get_error_unmatched_ignored_scope(unmatched_ignored_scope, ignored_scope)) +def get_ignored_node_names_from_ignored_scope( + ignored_scope: IgnoredScope, nncf_graph: NNCFGraph, strict: bool = True +) -> Set[str]: + """ + Returns ignored names according to ignored scope and NNCFGraph. + If strict is True, raises RuntimeError if any ignored rule was not matched. + If strict is False, returns all possible matches. + + :param ignored_scope: Ignored scope. + :param nncf_graph: Graph. + :param strict: Whether all ignored_scopes must match at least one node or not. + :return: NNCF node names from given NNCFGraph specified in given ignored scope. + """ + match = get_ignored_scope_match(ignored_scope, [nncf_graph]) + if strict: + validate_ignored_scope(ignored_scope, match.matched_ignored_scope) info_matched_ignored_scope(match.matches) return {name for match in match.matches.values() for name in match} -def validate_ignored_scope(ignored_scope, graphs): - matched_ignored_scope = IgnoredScope() - for graph in graphs.values(): - match = get_matched_ignored_scope(ignored_scope, graph) - matched_ignored_scope = merge_ignored_scopes(matched_ignored_scope, match.matched_ignored_scope) +def validate_ignored_scope(ignored_scope: IgnoredScope, matched_ignored_scope: IgnoredScope): + """ + Checks whether the every rule in ignored scope has a match. + + :param ignored_scope: Ignored scope. + :param matched_ignored_scope: Matched Ignored scope. + """ unmatched_ignored_scope = get_unmatched_ignored_scope(ignored_scope, matched_ignored_scope) if ( any(unmatched_ignored_scope.names) @@ -249,4 +269,4 @@ def validate_ignored_scope(ignored_scope, graphs): or any(unmatched_ignored_scope.patterns) or any(unmatched_ignored_scope.subgraphs) ): - raise nncf.ValidationError(get_error_unmatched_ignored_scope(unmatched_ignored_scope)) + raise nncf.ValidationError(error_unmatched_ignored_scope(unmatched_ignored_scope)) From a08960e84938efb4f3918cf512ab531ec3221ad6 Mon Sep 17 00:00:00 2001 From: Aleksei Kashapov Date: Thu, 6 Jun 2024 13:07:24 +0200 Subject: [PATCH 05/30] fix --- nncf/scopes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nncf/scopes.py b/nncf/scopes.py index c3edabfcb65..1b4879d64c4 100644 --- a/nncf/scopes.py +++ b/nncf/scopes.py @@ -262,7 +262,7 @@ def validate_ignored_scope(ignored_scope: IgnoredScope, matched_ignored_scope: I :param ignored_scope: Ignored scope. :param matched_ignored_scope: Matched Ignored scope. """ - unmatched_ignored_scope = get_unmatched_ignored_scope(ignored_scope, matched_ignored_scope) + unmatched_ignored_scope = get_unmatched_ignored_scope(matched_ignored_scope, ignored_scope) if ( any(unmatched_ignored_scope.names) or any(unmatched_ignored_scope.types) From c402e9bfc6738ee78ddaa264a628b2fe75230612 Mon Sep 17 00:00:00 2001 From: Aleksei Kashapov Date: Thu, 6 Jun 2024 13:18:59 +0200 Subject: [PATCH 06/30] fix --- nncf/scopes.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/nncf/scopes.py b/nncf/scopes.py index 1b4879d64c4..85d044c7a9f 100644 --- a/nncf/scopes.py +++ b/nncf/scopes.py @@ -147,8 +147,8 @@ def get_ignored_scope_match(ignored_scope: IgnoredScope, nncf_graphs: List[NNCFG :returns: ignored scope match united all mathces across graphs """ names, patterns, types, subgraphs_numbers = set(), set(), set(), set() + matches = collections.OrderedDict({"name": set(), "patterns": set(), "types": set(), "subgraphs": set()}) for graph in nncf_graphs: - matches = collections.OrderedDict({"name": set(), "patterns": set(), "types": set(), "subgraphs": set()}) node_names = set(node.node_name for node in graph.nodes.values()) for ignored_node_name in filter(lambda name: name in node_names, ignored_scope.names): @@ -161,8 +161,7 @@ def get_ignored_scope_match(ignored_scope: IgnoredScope, nncf_graphs: List[NNCFG matches["patterns"].add(matches) patterns.add(str_pattern) - ignored_scope_types = set(ignored_scope.types) - for node in graph.get_nodes_by_types(ignored_scope_types): + for node in graph.get_nodes_by_types(set(ignored_scope.types)): matches["types"].add(node.node_name) types.add(node.node_type) From 71773d1d1102d29cffe644437ce64c0543c5148e Mon Sep 17 00:00:00 2001 From: Aleksei Kashapov Date: Thu, 6 Jun 2024 14:31:05 +0200 Subject: [PATCH 07/30] fix is_model_no_batchwise_support rm IgnoredScopeMatch fix determing graphs --- nncf/onnx/quantization/quantize_model.py | 6 ++-- nncf/openvino/quantization/quantize_model.py | 12 +++---- nncf/scopes.py | 36 ++++++++------------ nncf/torch/quantization/quantize_model.py | 7 ++-- 4 files changed, 29 insertions(+), 32 deletions(-) diff --git a/nncf/onnx/quantization/quantize_model.py b/nncf/onnx/quantization/quantize_model.py index 094b98e81af..18129d4ebf2 100644 --- a/nncf/onnx/quantization/quantize_model.py +++ b/nncf/onnx/quantization/quantize_model.py @@ -30,8 +30,9 @@ from nncf.quantization.algorithms.accuracy_control.algorithm import calculate_accuracy_drop from nncf.quantization.algorithms.accuracy_control.evaluator import Evaluator from nncf.quantization.algorithms.post_training.algorithm import PostTrainingQuantization +from nncf.quantization.quantize_model import BATCHWISE_STATISTICS_WARNING +from nncf.quantization.quantize_model import is_model_no_batchwise_support from nncf.quantization.quantize_model import quantize_with_tune_hyperparams -from nncf.quantization.quantize_model import warning_model_no_batchwise_support from nncf.quantization.telemetry_extractors import CompressionStartedWithQuantizeApi from nncf.scopes import IgnoredScope from nncf.telemetry import tracked_function @@ -83,7 +84,8 @@ def quantize_impl( ) graph = GraphConverter.create_nncf_graph(model) - warning_model_no_batchwise_support(graph, advanced_parameters, model_type, OPERATIONS_OUTPUT_HAS_NO_BATCH_AXIS) + if is_model_no_batchwise_support(graph, advanced_parameters, model_type, OPERATIONS_OUTPUT_HAS_NO_BATCH_AXIS): + nncf_logger.warning(BATCHWISE_STATISTICS_WARNING) quantized_model = quantization_algorithm.apply(model, graph, dataset=calibration_dataset) return quantized_model diff --git a/nncf/openvino/quantization/quantize_model.py b/nncf/openvino/quantization/quantize_model.py index 91260d9c983..c8551722fdd 100644 --- a/nncf/openvino/quantization/quantize_model.py +++ b/nncf/openvino/quantization/quantize_model.py @@ -80,18 +80,16 @@ def native_quantize_if_op_impl( graphs = {} def _get_all_graphs(model, current_cnt): + graphs[current_cnt] = NNCFGraphFactory.create(model) for op in model.get_ops(): if get_node_metatype(op) == OVIfMetatype: - _get_all_graphs(op.get_function(0), current_cnt + 1) - _get_all_graphs(op.get_function(1), current_cnt + 2) - graphs[current_cnt] = NNCFGraphFactory.create(model) - return graphs + current_cnt = _get_all_graphs(op.get_function(0), current_cnt + 1) + current_cnt = _get_all_graphs(op.get_function(1), current_cnt + 1) + return current_cnt _get_all_graphs(model, 1) if ignored_scope and ignored_scope.validate: - validate_ignored_scope( - ignored_scope, get_ignored_scope_match(ignored_scope, graphs.values()).matched_ignored_scope - ) + validate_ignored_scope(ignored_scope, get_ignored_scope_match(ignored_scope, graphs.values())[0]) ignored_scope = IgnoredScope( ignored_scope.names, ignored_scope.patterns, ignored_scope.types, ignored_scope.subgraphs, validate=False ) diff --git a/nncf/scopes.py b/nncf/scopes.py index 85d044c7a9f..48ea8f741d7 100644 --- a/nncf/scopes.py +++ b/nncf/scopes.py @@ -9,11 +9,10 @@ # See the License for the specific language governing permissions and # limitations under the License. -import collections import re from dataclasses import dataclass from dataclasses import field -from typing import List, Optional, OrderedDict, Set +from typing import Dict, List, Optional, Set, Tuple import nncf from nncf.common.graph.graph import NNCFGraph @@ -131,34 +130,29 @@ def convert_ignored_scope_to_list(ignored_scope: Optional[IgnoredScope]) -> List return results -@dataclass -class IgnoredScopeMatch: - matched_ignored_scope: IgnoredScope = IgnoredScope() - matches: OrderedDict[str, Set[str]] = field(default_factory=lambda: OrderedDict(dict)) - - -def get_ignored_scope_match(ignored_scope: IgnoredScope, nncf_graphs: List[NNCFGraph]) -> IgnoredScopeMatch: +def get_ignored_scope_match( + ignored_scope: IgnoredScope, nncf_graphs: List[NNCFGraph] +) -> Tuple[IgnoredScope, Dict[str, Set[str]]]: """ - Returns ignored scope match for provided NNCFGraphs. - The resulted match is a union of all matches across graphs. + Returns matched ignored scope for provided NNCFGraphs along with all matches. + The resulted match is a union of all matches across all graphs. :param ignored_scope: Ignored scope instance. :param nncf_graphs: NNCFGraphs. - :returns: ignored scope match united all mathces across graphs + :returns: united mathced ingnored scope along with united matches. """ names, patterns, types, subgraphs_numbers = set(), set(), set(), set() - matches = collections.OrderedDict({"name": set(), "patterns": set(), "types": set(), "subgraphs": set()}) + matches = {"names": set(), "patterns": set(), "types": set(), "subgraphs": set()} for graph in nncf_graphs: node_names = set(node.node_name for node in graph.nodes.values()) for ignored_node_name in filter(lambda name: name in node_names, ignored_scope.names): - matches["name"].add(ignored_node_name) + matches["names"].add(ignored_node_name) names.add(ignored_node_name) for str_pattern in ignored_scope.patterns: pattern = re.compile(str_pattern) - matches = list(filter(pattern.match, node_names)) - matches["patterns"].add(matches) + matches["patterns"].update(filter(pattern.match, node_names)) patterns.add(str_pattern) for node in graph.get_nodes_by_types(set(ignored_scope.types)): @@ -176,7 +170,7 @@ def get_ignored_scope_match(ignored_scope: IgnoredScope, nncf_graphs: List[NNCFG subgraphs=[subgraph for i, subgraph in enumerate(ignored_scope.subgraphs) if i in subgraphs_numbers], validate=ignored_scope.validate, ) - return IgnoredScopeMatch(matched_ignored_scope, matches) + return matched_ignored_scope, matches def get_unmatched_ignored_scope(matched_ignored_scope: IgnoredScope, ignored_scope: IgnoredScope) -> IgnoredScope: @@ -247,11 +241,11 @@ def get_ignored_node_names_from_ignored_scope( :param strict: Whether all ignored_scopes must match at least one node or not. :return: NNCF node names from given NNCFGraph specified in given ignored scope. """ - match = get_ignored_scope_match(ignored_scope, [nncf_graph]) + matched_ignored_scope, matches = get_ignored_scope_match(ignored_scope, [nncf_graph]) if strict: - validate_ignored_scope(ignored_scope, match.matched_ignored_scope) - info_matched_ignored_scope(match.matches) - return {name for match in match.matches.values() for name in match} + validate_ignored_scope(ignored_scope, matched_ignored_scope) + info_matched_ignored_scope(matches) + return {name for match in matches.values() for name in match} def validate_ignored_scope(ignored_scope: IgnoredScope, matched_ignored_scope: IgnoredScope): diff --git a/nncf/torch/quantization/quantize_model.py b/nncf/torch/quantization/quantize_model.py index 0893c179b17..345e691faeb 100644 --- a/nncf/torch/quantization/quantize_model.py +++ b/nncf/torch/quantization/quantize_model.py @@ -16,6 +16,7 @@ import nncf from nncf.common.factory import NNCFGraphFactory +from nncf.common.logging.logger import nncf_logger from nncf.common.quantization.structs import QuantizationPreset from nncf.data import Dataset from nncf.parameters import CompressWeightsMode @@ -27,7 +28,8 @@ from nncf.quantization.advanced_parameters import AdvancedQuantizationParameters from nncf.quantization.algorithms.post_training.algorithm import PostTrainingQuantization from nncf.quantization.algorithms.weight_compression.algorithm import WeightCompression -from nncf.quantization.quantize_model import warning_model_no_batchwise_support +from nncf.quantization.quantize_model import BATCHWISE_STATISTICS_WARNING +from nncf.quantization.quantize_model import is_model_no_batchwise_support from nncf.scopes import IgnoredScope from nncf.torch.graph.operator_metatypes import OPERATIONS_OUTPUT_HAS_NO_BATCH_AXIS from nncf.torch.model_creation import wrap_model @@ -72,7 +74,8 @@ def quantize_impl( advanced_parameters=advanced_parameters, ) graph = nncf_network.nncf.get_graph() - warning_model_no_batchwise_support(graph, advanced_parameters, model_type, OPERATIONS_OUTPUT_HAS_NO_BATCH_AXIS) + if is_model_no_batchwise_support(graph, advanced_parameters, model_type, OPERATIONS_OUTPUT_HAS_NO_BATCH_AXIS): + nncf_logger.warning(BATCHWISE_STATISTICS_WARNING) quantized_model = quantization_algorithm.apply(nncf_network, graph, dataset=calibration_dataset) quantized_model.nncf.disable_dynamic_graph_building() From 396bc6d1b0cd05f37bea83de9a5c537b27cba7dd Mon Sep 17 00:00:00 2001 From: Aleksei Kashapov Date: Thu, 6 Jun 2024 15:15:20 +0200 Subject: [PATCH 08/30] add check find pattern/subgraph --- nncf/scopes.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/nncf/scopes.py b/nncf/scopes.py index 48ea8f741d7..311f096ada3 100644 --- a/nncf/scopes.py +++ b/nncf/scopes.py @@ -152,8 +152,10 @@ def get_ignored_scope_match( for str_pattern in ignored_scope.patterns: pattern = re.compile(str_pattern) - matches["patterns"].update(filter(pattern.match, node_names)) - patterns.add(str_pattern) + pattern_matched_names = set(filter(pattern.match, node_names)) + if pattern_matched_names: + matches["patterns"].update(pattern_matched_names) + patterns.add(str_pattern) for node in graph.get_nodes_by_types(set(ignored_scope.types)): matches["types"].add(node.node_name) @@ -161,8 +163,10 @@ def get_ignored_scope_match( for i, subgraph in enumerate(ignored_scope.subgraphs): names_from_subgraph = get_ignored_node_names_from_subgraph(graph, subgraph) - matches["subgraphs"].update(names_from_subgraph) - subgraphs_numbers.add(i) + if names_from_subgraph: + matches["subgraphs"].update(names_from_subgraph) + subgraphs_numbers.add(i) + matched_ignored_scope = IgnoredScope( names=list(names), patterns=list(patterns), From 85d580f13cb062e5df86bfcca0c84eda881b6ea4 Mon Sep 17 00:00:00 2001 From: Aleksei Kashapov Date: Thu, 6 Jun 2024 15:27:26 +0200 Subject: [PATCH 09/30] polishing --- nncf/openvino/quantization/quantize_model.py | 2 +- nncf/scopes.py | 14 ++++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/nncf/openvino/quantization/quantize_model.py b/nncf/openvino/quantization/quantize_model.py index c8551722fdd..e8ac15cef87 100644 --- a/nncf/openvino/quantization/quantize_model.py +++ b/nncf/openvino/quantization/quantize_model.py @@ -79,7 +79,7 @@ def native_quantize_if_op_impl( ) graphs = {} - def _get_all_graphs(model, current_cnt): + def _get_all_graphs(model: ov.Model, current_cnt: int): graphs[current_cnt] = NNCFGraphFactory.create(model) for op in model.get_ops(): if get_node_metatype(op) == OVIfMetatype: diff --git a/nncf/scopes.py b/nncf/scopes.py index 311f096ada3..10cc187cb68 100644 --- a/nncf/scopes.py +++ b/nncf/scopes.py @@ -134,15 +134,17 @@ def get_ignored_scope_match( ignored_scope: IgnoredScope, nncf_graphs: List[NNCFGraph] ) -> Tuple[IgnoredScope, Dict[str, Set[str]]]: """ - Returns matched ignored scope for provided NNCFGraphs along with all matches. - The resulted match is a union of all matches across all graphs. + Returns matched ignored scope for provided graphs along with all found matches. + The resulted ignored scope consist of all matched rules. + The found matches consist of a ditionary with a rule name as a key and matched node names as a value. :param ignored_scope: Ignored scope instance. - :param nncf_graphs: NNCFGraphs. - :returns: united mathced ingnored scope along with united matches. + :param nncf_graphs: Graphs. + :returns: Mathced ignored scope along with all matches. """ names, patterns, types, subgraphs_numbers = set(), set(), set(), set() matches = {"names": set(), "patterns": set(), "types": set(), "subgraphs": set()} + for graph in nncf_graphs: node_names = set(node.node_name for node in graph.nodes.values()) @@ -195,7 +197,7 @@ def get_unmatched_ignored_scope(matched_ignored_scope: IgnoredScope, ignored_sco ) -def info_matched_ignored_scope(matches) -> None: +def info_matched_ignored_scope(matches: Dict[str, Set[str]]) -> None: """ Log matches. @@ -237,7 +239,7 @@ def get_ignored_node_names_from_ignored_scope( ) -> Set[str]: """ Returns ignored names according to ignored scope and NNCFGraph. - If strict is True, raises RuntimeError if any ignored rule was not matched. + If strict is True, raises nncf.ValidationError if any ignored rule was not matched. If strict is False, returns all possible matches. :param ignored_scope: Ignored scope. From d94fd59dbc94d98cc2b198edf612064528447cda Mon Sep 17 00:00:00 2001 From: Aleksei Kashapov Date: Thu, 6 Jun 2024 15:42:09 +0200 Subject: [PATCH 10/30] doc --- nncf/quantization/quantize_model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nncf/quantization/quantize_model.py b/nncf/quantization/quantize_model.py index fec5b744e47..ad5d51aecea 100644 --- a/nncf/quantization/quantize_model.py +++ b/nncf/quantization/quantize_model.py @@ -53,7 +53,7 @@ def is_model_no_batchwise_support( no_batchwise_support_metatypes: List[OperatorMetatype], ) -> None: """ - Prints the warning message if batchwise statistics could lead to a significant accuracy drop. + Returns True if batchwise statistics could lead to a significant accuracy drop. :param graph: Model's NNCFGraph. :param advanced_quantization_parameters: AdvancedQuantizationParameters. From 8e87d6052a01bbff8a139b99687acc17f2f79be2 Mon Sep 17 00:00:00 2001 From: Aleksei Kashapov Date: Fri, 7 Jun 2024 11:20:46 +0200 Subject: [PATCH 11/30] formatting --- nncf/quantization/quantize_model.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/nncf/quantization/quantize_model.py b/nncf/quantization/quantize_model.py index ad5d51aecea..6fd51c9af0f 100644 --- a/nncf/quantization/quantize_model.py +++ b/nncf/quantization/quantize_model.py @@ -60,13 +60,11 @@ def is_model_no_batchwise_support( :param model_type: Model type algorithm option. :param no_batchwise_support_metatypes: Meatypes having no batchwise statistics support. """ - if ( + return ( advanced_quantization_parameters and advanced_quantization_parameters.batchwise_statistics and (graph.get_nodes_by_metatypes(no_batchwise_support_metatypes) or model_type == ModelType.TRANSFORMER) - ): - return True - return False + ) def _update_advanced_quantization_parameters( From de3646412c76b294143d59c3be3ee778b794f213 Mon Sep 17 00:00:00 2001 From: Aleksei Kashapov Date: Mon, 10 Jun 2024 13:15:19 +0200 Subject: [PATCH 12/30] grammar, renames, typehints, docstrings --- nncf/openvino/quantization/quantize_model.py | 19 +++++++++++++------ nncf/quantization/quantize_model.py | 2 +- nncf/scopes.py | 20 ++++++++++---------- 3 files changed, 24 insertions(+), 17 deletions(-) diff --git a/nncf/openvino/quantization/quantize_model.py b/nncf/openvino/quantization/quantize_model.py index e8ac15cef87..5bf6aae9ea1 100644 --- a/nncf/openvino/quantization/quantize_model.py +++ b/nncf/openvino/quantization/quantize_model.py @@ -49,7 +49,7 @@ from nncf.quantization.quantize_model import quantize_with_tune_hyperparams from nncf.quantization.telemetry_extractors import CompressionStartedWithQuantizeApi from nncf.scopes import IgnoredScope -from nncf.scopes import get_ignored_scope_match +from nncf.scopes import get_matched_ignored_scope_info from nncf.scopes import validate_ignored_scope from nncf.telemetry.decorator import tracked_function from nncf.telemetry.events import NNCF_OV_CATEGORY @@ -79,17 +79,24 @@ def native_quantize_if_op_impl( ) graphs = {} - def _get_all_graphs(model: ov.Model, current_cnt: int): + def _extract_all_subgraphs(model: ov.Model, current_cnt: int) -> int: + """ + Creates all inner subgraphs from If nodes and adds them to 'graphs'. + + :param model: Model. + :param current_cnt: Current graph number. + :return: The next graph number. + """ graphs[current_cnt] = NNCFGraphFactory.create(model) for op in model.get_ops(): if get_node_metatype(op) == OVIfMetatype: - current_cnt = _get_all_graphs(op.get_function(0), current_cnt + 1) - current_cnt = _get_all_graphs(op.get_function(1), current_cnt + 1) + current_cnt = _extract_all_subgraphs(op.get_function(0), current_cnt + 1) + current_cnt = _extract_all_subgraphs(op.get_function(1), current_cnt + 1) return current_cnt - _get_all_graphs(model, 1) + _extract_all_subgraphs(model, 1) if ignored_scope and ignored_scope.validate: - validate_ignored_scope(ignored_scope, get_ignored_scope_match(ignored_scope, graphs.values())[0]) + validate_ignored_scope(ignored_scope, get_matched_ignored_scope_info(ignored_scope, graphs.values())[0]) ignored_scope = IgnoredScope( ignored_scope.names, ignored_scope.patterns, ignored_scope.types, ignored_scope.subgraphs, validate=False ) diff --git a/nncf/quantization/quantize_model.py b/nncf/quantization/quantize_model.py index 6fd51c9af0f..8a8b61b328f 100644 --- a/nncf/quantization/quantize_model.py +++ b/nncf/quantization/quantize_model.py @@ -41,7 +41,7 @@ BATCHWISE_STATISTICS_WARNING = ( "For the particular model the batchwise statistics collection can lead to inaccurate statistics. " "If the accuracy degradation after compression is unsatisfactory, then " - "the recomendation is to turn off batchwise statistics. If the results are still unsatisfactory, " + "the recommendation is to turn off batchwise statistics. If the results are still unsatisfactory, " "provide a dataloader with batch_size = 1 to the calibration dataset." ) diff --git a/nncf/scopes.py b/nncf/scopes.py index 10cc187cb68..4cdc97152ab 100644 --- a/nncf/scopes.py +++ b/nncf/scopes.py @@ -130,17 +130,17 @@ def convert_ignored_scope_to_list(ignored_scope: Optional[IgnoredScope]) -> List return results -def get_ignored_scope_match( +def get_matched_ignored_scope_info( ignored_scope: IgnoredScope, nncf_graphs: List[NNCFGraph] ) -> Tuple[IgnoredScope, Dict[str, Set[str]]]: """ Returns matched ignored scope for provided graphs along with all found matches. The resulted ignored scope consist of all matched rules. - The found matches consist of a ditionary with a rule name as a key and matched node names as a value. + The found matches consist of a dictionary with a rule name as a key and matched node names as a value. :param ignored_scope: Ignored scope instance. :param nncf_graphs: Graphs. - :returns: Mathced ignored scope along with all matches. + :returns: Matched ignored scope along with all matches. """ names, patterns, types, subgraphs_numbers = set(), set(), set(), set() matches = {"names": set(), "patterns": set(), "types": set(), "subgraphs": set()} @@ -183,7 +183,7 @@ def get_unmatched_ignored_scope(matched_ignored_scope: IgnoredScope, ignored_sco """ Returns unmatched ignored scope rules from full ignored scope and matched ignored scope. - :param matched_ignored_scope: Matched ingored scope. + :param matched_ignored_scope: Matched ignored scope. :param ignored_scope: Full ignored scope. :return: Unmatched ignored scope. """ @@ -217,15 +217,15 @@ def error_unmatched_ignored_scope(unmatched_ignored_scope: IgnoredScope) -> str: """ err_msg = "" if unmatched_ignored_scope.names: - err_msg += f"Ignored nodes with name {unmatched_ignored_scope.names} were not found in the NNCFGraph. " + err_msg += f"Ignored nodes with name {unmatched_ignored_scope.names} were not found in the NNCFGraph.\n" if unmatched_ignored_scope.patterns: - err_msg += f"No matches for ignored patterns {unmatched_ignored_scope.patterns} in the NNCFGraph. " + err_msg += f"No matches for ignored patterns {unmatched_ignored_scope.patterns} in the NNCFGraph.\n" if unmatched_ignored_scope.types: - err_msg += f"Nodes with ignored types {unmatched_ignored_scope.types} were not found in the NNCFGraph. " + err_msg += f"Nodes with ignored types {unmatched_ignored_scope.types} were not found in the NNCFGraph.\n" for subgraph in unmatched_ignored_scope.subgraphs: err_msg += ( f"Ignored subgraph with input names {subgraph.inputs} and output names {subgraph.outputs} " - "was not found in the NNCFGraph. " + "was not found in the NNCFGraph.\n" ) return err_msg + ( "Refer to the original_graph.dot to discover the operations" @@ -247,14 +247,14 @@ def get_ignored_node_names_from_ignored_scope( :param strict: Whether all ignored_scopes must match at least one node or not. :return: NNCF node names from given NNCFGraph specified in given ignored scope. """ - matched_ignored_scope, matches = get_ignored_scope_match(ignored_scope, [nncf_graph]) + matched_ignored_scope, matches = get_matched_ignored_scope_info(ignored_scope, [nncf_graph]) if strict: validate_ignored_scope(ignored_scope, matched_ignored_scope) info_matched_ignored_scope(matches) return {name for match in matches.values() for name in match} -def validate_ignored_scope(ignored_scope: IgnoredScope, matched_ignored_scope: IgnoredScope): +def validate_ignored_scope(ignored_scope: IgnoredScope, matched_ignored_scope: IgnoredScope) -> None: """ Checks whether the every rule in ignored scope has a match. From 670b8e231b5006c4e204c93b3fd21971aec83d12 Mon Sep 17 00:00:00 2001 From: Aleksei Kashapov Date: Mon, 10 Jun 2024 13:22:00 +0200 Subject: [PATCH 13/30] warning_model_no_batchwise_support --- nncf/onnx/quantization/quantize_model.py | 6 ++---- nncf/openvino/quantization/quantize_model.py | 4 ++-- nncf/quantization/quantize_model.py | 21 ++++++++++++++++++++ nncf/torch/quantization/quantize_model.py | 7 ++----- 4 files changed, 27 insertions(+), 11 deletions(-) diff --git a/nncf/onnx/quantization/quantize_model.py b/nncf/onnx/quantization/quantize_model.py index 18129d4ebf2..094b98e81af 100644 --- a/nncf/onnx/quantization/quantize_model.py +++ b/nncf/onnx/quantization/quantize_model.py @@ -30,9 +30,8 @@ from nncf.quantization.algorithms.accuracy_control.algorithm import calculate_accuracy_drop from nncf.quantization.algorithms.accuracy_control.evaluator import Evaluator from nncf.quantization.algorithms.post_training.algorithm import PostTrainingQuantization -from nncf.quantization.quantize_model import BATCHWISE_STATISTICS_WARNING -from nncf.quantization.quantize_model import is_model_no_batchwise_support from nncf.quantization.quantize_model import quantize_with_tune_hyperparams +from nncf.quantization.quantize_model import warning_model_no_batchwise_support from nncf.quantization.telemetry_extractors import CompressionStartedWithQuantizeApi from nncf.scopes import IgnoredScope from nncf.telemetry import tracked_function @@ -84,8 +83,7 @@ def quantize_impl( ) graph = GraphConverter.create_nncf_graph(model) - if is_model_no_batchwise_support(graph, advanced_parameters, model_type, OPERATIONS_OUTPUT_HAS_NO_BATCH_AXIS): - nncf_logger.warning(BATCHWISE_STATISTICS_WARNING) + warning_model_no_batchwise_support(graph, advanced_parameters, model_type, OPERATIONS_OUTPUT_HAS_NO_BATCH_AXIS) quantized_model = quantization_algorithm.apply(model, graph, dataset=calibration_dataset) return quantized_model diff --git a/nncf/openvino/quantization/quantize_model.py b/nncf/openvino/quantization/quantize_model.py index 5bf6aae9ea1..e110343d535 100644 --- a/nncf/openvino/quantization/quantize_model.py +++ b/nncf/openvino/quantization/quantize_model.py @@ -47,6 +47,7 @@ from nncf.quantization.quantize_model import BATCHWISE_STATISTICS_WARNING from nncf.quantization.quantize_model import is_model_no_batchwise_support from nncf.quantization.quantize_model import quantize_with_tune_hyperparams +from nncf.quantization.quantize_model import warning_model_no_batchwise_support from nncf.quantization.telemetry_extractors import CompressionStartedWithQuantizeApi from nncf.scopes import IgnoredScope from nncf.scopes import get_matched_ignored_scope_info @@ -168,8 +169,7 @@ def native_quantize_impl( advanced_parameters=advanced_parameters, ) graph = GraphConverter.create_nncf_graph(model) - if is_model_no_batchwise_support(graph, advanced_parameters, model_type, OPERATIONS_OUTPUT_HAS_NO_BATCH_AXIS): - nncf_logger.warning(BATCHWISE_STATISTICS_WARNING) + warning_model_no_batchwise_support(graph, advanced_parameters, model_type, OPERATIONS_OUTPUT_HAS_NO_BATCH_AXIS) quantized_model = quantization_algorithm.apply(model, graph, dataset=calibration_dataset) if is_weight_compression_needed(advanced_parameters): diff --git a/nncf/quantization/quantize_model.py b/nncf/quantization/quantize_model.py index 8a8b61b328f..5f791e9bbcd 100644 --- a/nncf/quantization/quantize_model.py +++ b/nncf/quantization/quantize_model.py @@ -16,6 +16,7 @@ from nncf.common.deprecation import warning_deprecated from nncf.common.graph import NNCFGraph from nncf.common.graph.operator_metatypes import OperatorMetatype +from nncf.common.logging.logger import nncf_logger from nncf.common.quantization.structs import QuantizationPreset from nncf.common.utils.api_marker import api from nncf.common.utils.backend import BackendType @@ -46,6 +47,26 @@ ) +def warning_model_no_batchwise_support( + graph: NNCFGraph, + advanced_quantization_parameters: Optional[AdvancedQuantizationParameters], + model_type: ModelType, + no_batchwise_support_metatypes: List[OperatorMetatype], +) -> None: + """ + Logs when is_model_no_batchwise_support(...) returns True. + + :param graph: Model's NNCFGraph. + :param advanced_quantization_parameters: AdvancedQuantizationParameters. + :param model_type: Model type algorithm option. + :param no_batchwise_support_metatypes: Meatypes having no batchwise statistics support. + """ + if is_model_no_batchwise_support( + graph, advanced_quantization_parameters, model_type, no_batchwise_support_metatypes + ): + nncf_logger.warning(BATCHWISE_STATISTICS_WARNING) + + def is_model_no_batchwise_support( graph: NNCFGraph, advanced_quantization_parameters: Optional[AdvancedQuantizationParameters], diff --git a/nncf/torch/quantization/quantize_model.py b/nncf/torch/quantization/quantize_model.py index 345e691faeb..0893c179b17 100644 --- a/nncf/torch/quantization/quantize_model.py +++ b/nncf/torch/quantization/quantize_model.py @@ -16,7 +16,6 @@ import nncf from nncf.common.factory import NNCFGraphFactory -from nncf.common.logging.logger import nncf_logger from nncf.common.quantization.structs import QuantizationPreset from nncf.data import Dataset from nncf.parameters import CompressWeightsMode @@ -28,8 +27,7 @@ from nncf.quantization.advanced_parameters import AdvancedQuantizationParameters from nncf.quantization.algorithms.post_training.algorithm import PostTrainingQuantization from nncf.quantization.algorithms.weight_compression.algorithm import WeightCompression -from nncf.quantization.quantize_model import BATCHWISE_STATISTICS_WARNING -from nncf.quantization.quantize_model import is_model_no_batchwise_support +from nncf.quantization.quantize_model import warning_model_no_batchwise_support from nncf.scopes import IgnoredScope from nncf.torch.graph.operator_metatypes import OPERATIONS_OUTPUT_HAS_NO_BATCH_AXIS from nncf.torch.model_creation import wrap_model @@ -74,8 +72,7 @@ def quantize_impl( advanced_parameters=advanced_parameters, ) graph = nncf_network.nncf.get_graph() - if is_model_no_batchwise_support(graph, advanced_parameters, model_type, OPERATIONS_OUTPUT_HAS_NO_BATCH_AXIS): - nncf_logger.warning(BATCHWISE_STATISTICS_WARNING) + warning_model_no_batchwise_support(graph, advanced_parameters, model_type, OPERATIONS_OUTPUT_HAS_NO_BATCH_AXIS) quantized_model = quantization_algorithm.apply(nncf_network, graph, dataset=calibration_dataset) quantized_model.nncf.disable_dynamic_graph_building() From e5753f4698bb4ccefd0a458921f4dbf9841929aa Mon Sep 17 00:00:00 2001 From: Aleksei Kashapov Date: Mon, 10 Jun 2024 14:15:08 +0200 Subject: [PATCH 14/30] error_unmatched_ignored_scope --- nncf/scopes.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/nncf/scopes.py b/nncf/scopes.py index 4cdc97152ab..4a4f020f88b 100644 --- a/nncf/scopes.py +++ b/nncf/scopes.py @@ -10,6 +10,7 @@ # limitations under the License. import re +from dataclasses import asdict from dataclasses import dataclass from dataclasses import field from typing import Dict, List, Optional, Set, Tuple @@ -216,16 +217,15 @@ def error_unmatched_ignored_scope(unmatched_ignored_scope: IgnoredScope) -> str: :return str: Error message. """ err_msg = "" - if unmatched_ignored_scope.names: - err_msg += f"Ignored nodes with name {unmatched_ignored_scope.names} were not found in the NNCFGraph.\n" - if unmatched_ignored_scope.patterns: - err_msg += f"No matches for ignored patterns {unmatched_ignored_scope.patterns} in the NNCFGraph.\n" - if unmatched_ignored_scope.types: - err_msg += f"Nodes with ignored types {unmatched_ignored_scope.types} were not found in the NNCFGraph.\n" + unmatched_dict = asdict(unmatched_ignored_scope) + for ingored_type in ("names", "types", "patterns"): + unmatched_rules = unmatched_dict.get(ingored_type) + if unmatched_rules: + err_msg += f"Ignored nodes that matches {ingored_type} {unmatched_rules} were not found in the NNCFGraph.\n" for subgraph in unmatched_ignored_scope.subgraphs: err_msg += ( - f"Ignored subgraph with input names {subgraph.inputs} and output names {subgraph.outputs} " - "was not found in the NNCFGraph.\n" + f"Ignored nodes that matches subgraph with input names {subgraph.inputs} " + f"and output names {subgraph.outputs} were not found in the NNCFGraph.\n" ) return err_msg + ( "Refer to the original_graph.dot to discover the operations" From 2d89d52ec0d4820291108117c3002908a188667c Mon Sep 17 00:00:00 2001 From: Aleksei Kashapov Date: Mon, 10 Jun 2024 14:20:16 +0200 Subject: [PATCH 15/30] asdict -> getattr --- nncf/scopes.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/nncf/scopes.py b/nncf/scopes.py index 4a4f020f88b..d08bed7a84c 100644 --- a/nncf/scopes.py +++ b/nncf/scopes.py @@ -10,7 +10,6 @@ # limitations under the License. import re -from dataclasses import asdict from dataclasses import dataclass from dataclasses import field from typing import Dict, List, Optional, Set, Tuple @@ -217,9 +216,8 @@ def error_unmatched_ignored_scope(unmatched_ignored_scope: IgnoredScope) -> str: :return str: Error message. """ err_msg = "" - unmatched_dict = asdict(unmatched_ignored_scope) for ingored_type in ("names", "types", "patterns"): - unmatched_rules = unmatched_dict.get(ingored_type) + unmatched_rules = getattr(unmatched_ignored_scope, ingored_type) if unmatched_rules: err_msg += f"Ignored nodes that matches {ingored_type} {unmatched_rules} were not found in the NNCFGraph.\n" for subgraph in unmatched_ignored_scope.subgraphs: From 40e864f54ef7bd7d9541ab8546a8a80a075481b6 Mon Sep 17 00:00:00 2001 From: Aleksei Kashapov Date: Mon, 10 Jun 2024 14:37:11 +0200 Subject: [PATCH 16/30] upd error message in test --- tests/post_training/test_templates/test_ptq_params.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/post_training/test_templates/test_ptq_params.py b/tests/post_training/test_templates/test_ptq_params.py index f56db68f938..c40f80b79e2 100644 --- a/tests/post_training/test_templates/test_ptq_params.py +++ b/tests/post_training/test_templates/test_ptq_params.py @@ -388,7 +388,7 @@ def test_validate_scope(self, test_params, validate_scopes): ) algo._backend_entity = self.get_algo_backend() if validate_scopes: - with pytest.raises(nncf.ValidationError, match="Ignored nodes with name"): + with pytest.raises(nncf.ValidationError, match="Ignored nodes that matches names"): algo._get_ignored_names(nncf_graph, inference_nncf_graph, ignored_patterns) else: algo._get_ignored_names(nncf_graph, inference_nncf_graph, ignored_patterns) From 8a3276cd132c079971c2e72a81b3882580c2512f Mon Sep 17 00:00:00 2001 From: Aleksei Kashapov Date: Mon, 10 Jun 2024 18:02:55 +0200 Subject: [PATCH 17/30] rm unnecessary set --- nncf/scopes.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/nncf/scopes.py b/nncf/scopes.py index d08bed7a84c..ce9a1359112 100644 --- a/nncf/scopes.py +++ b/nncf/scopes.py @@ -143,13 +143,12 @@ def get_matched_ignored_scope_info( :returns: Matched ignored scope along with all matches. """ names, patterns, types, subgraphs_numbers = set(), set(), set(), set() - matches = {"names": set(), "patterns": set(), "types": set(), "subgraphs": set()} + matches = {"names": names, "patterns": set(), "types": set(), "subgraphs": set()} for graph in nncf_graphs: node_names = set(node.node_name for node in graph.nodes.values()) for ignored_node_name in filter(lambda name: name in node_names, ignored_scope.names): - matches["names"].add(ignored_node_name) names.add(ignored_node_name) for str_pattern in ignored_scope.patterns: From f8cbfdfcc7fd07b57dc931a669bd01dbf975fb6a Mon Sep 17 00:00:00 2001 From: Aleksei Kashapov Date: Tue, 11 Jun 2024 09:24:50 +0200 Subject: [PATCH 18/30] rm any --- nncf/scopes.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/nncf/scopes.py b/nncf/scopes.py index ce9a1359112..07520da090d 100644 --- a/nncf/scopes.py +++ b/nncf/scopes.py @@ -260,9 +260,9 @@ def validate_ignored_scope(ignored_scope: IgnoredScope, matched_ignored_scope: I """ unmatched_ignored_scope = get_unmatched_ignored_scope(matched_ignored_scope, ignored_scope) if ( - any(unmatched_ignored_scope.names) - or any(unmatched_ignored_scope.types) - or any(unmatched_ignored_scope.patterns) - or any(unmatched_ignored_scope.subgraphs) + unmatched_ignored_scope.names + or unmatched_ignored_scope.types + or unmatched_ignored_scope.patterns + or unmatched_ignored_scope.subgraphs ): raise nncf.ValidationError(error_unmatched_ignored_scope(unmatched_ignored_scope)) From d5264af5ce8f32008ceef04fd432f18c0aafa497 Mon Sep 17 00:00:00 2001 From: Aleksei Kashapov Date: Tue, 11 Jun 2024 16:08:40 +0200 Subject: [PATCH 19/30] rm assert --- nncf/scopes.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/nncf/scopes.py b/nncf/scopes.py index 07520da090d..e86f55f88ee 100644 --- a/nncf/scopes.py +++ b/nncf/scopes.py @@ -186,13 +186,12 @@ def get_unmatched_ignored_scope(matched_ignored_scope: IgnoredScope, ignored_sco :param ignored_scope: Full ignored scope. :return: Unmatched ignored scope. """ - assert matched_ignored_scope.validate == ignored_scope.validate return IgnoredScope( names=[name for name in ignored_scope.names if name not in matched_ignored_scope.names], patterns=[pattern for pattern in ignored_scope.patterns if pattern not in matched_ignored_scope.patterns], types=[type for type in ignored_scope.types if type not in matched_ignored_scope.types], subgraphs=[subgraph for subgraph in ignored_scope.subgraphs if subgraph not in matched_ignored_scope.subgraphs], - validate=matched_ignored_scope.validate, + validate=ignored_scope.validate, ) From 6343dd73d9c2101c88c80c2aca67d1fa3fdc8c4d Mon Sep 17 00:00:00 2001 From: Aleksei Kashapov Date: Tue, 11 Jun 2024 16:39:11 +0200 Subject: [PATCH 20/30] minor --- nncf/scopes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nncf/scopes.py b/nncf/scopes.py index e86f55f88ee..bceb588a6d3 100644 --- a/nncf/scopes.py +++ b/nncf/scopes.py @@ -158,7 +158,7 @@ def get_matched_ignored_scope_info( matches["patterns"].update(pattern_matched_names) patterns.add(str_pattern) - for node in graph.get_nodes_by_types(set(ignored_scope.types)): + for node in graph.get_nodes_by_types(ignored_scope.types): matches["types"].add(node.node_name) types.add(node.node_type) From d3c2326b639605691b3291810c73e8dfbcbd4e11 Mon Sep 17 00:00:00 2001 From: Aleksei Kashapov Date: Thu, 13 Jun 2024 11:41:45 +0200 Subject: [PATCH 21/30] refactoring --- nncf/common/graph/graph.py | 2 + nncf/openvino/quantization/quantize_model.py | 3 +- nncf/scopes.py | 108 ++++++++++--------- 3 files changed, 63 insertions(+), 50 deletions(-) diff --git a/nncf/common/graph/graph.py b/nncf/common/graph/graph.py index d71b5cb9888..2a91902e186 100644 --- a/nncf/common/graph/graph.py +++ b/nncf/common/graph/graph.py @@ -238,6 +238,8 @@ def get_nodes_by_types(self, type_list: List[str]) -> List[NNCFNode]: :return: List of nodes with provided types. """ all_nodes_of_type = [] + if not type_list: + return all_nodes_of_type for nncf_node in self.nodes.values(): if nncf_node.node_type in type_list: all_nodes_of_type.append(nncf_node) diff --git a/nncf/openvino/quantization/quantize_model.py b/nncf/openvino/quantization/quantize_model.py index cb631704aff..fa6e68db3fb 100644 --- a/nncf/openvino/quantization/quantize_model.py +++ b/nncf/openvino/quantization/quantize_model.py @@ -50,7 +50,6 @@ from nncf.quantization.quantize_model import warning_model_no_batchwise_support from nncf.quantization.telemetry_extractors import CompressionStartedWithQuantizeApi from nncf.scopes import IgnoredScope -from nncf.scopes import get_matched_ignored_scope_info from nncf.scopes import validate_ignored_scope from nncf.telemetry.decorator import tracked_function from nncf.telemetry.events import NNCF_OV_CATEGORY @@ -97,7 +96,7 @@ def _extract_all_subgraphs(model: ov.Model, current_cnt: int) -> int: _extract_all_subgraphs(model, 1) if ignored_scope and ignored_scope.validate: - validate_ignored_scope(ignored_scope, get_matched_ignored_scope_info(ignored_scope, graphs.values())[0]) + validate_ignored_scope(ignored_scope, graphs.values()) ignored_scope = IgnoredScope( ignored_scope.names, ignored_scope.patterns, ignored_scope.types, ignored_scope.subgraphs, validate=False ) diff --git a/nncf/scopes.py b/nncf/scopes.py index bceb588a6d3..c2fad6f936a 100644 --- a/nncf/scopes.py +++ b/nncf/scopes.py @@ -111,6 +111,23 @@ class IgnoredScope: validate: bool = True +def get_difference_ignored_scope(ignored_scope_1: IgnoredScope, ignored_scope_2: IgnoredScope) -> IgnoredScope: + """ + Returns ignored scope with rules from 'ignored_scope_1' not presented at 'ignored_scope_2' + + :param ignored_scope_1: First ignored scope. + :param ignored_scope_2: Second ignored scope. + :return: Ignored scope. + """ + return IgnoredScope( + names=list(set(ignored_scope_1.names) - set(ignored_scope_2.names)), + patterns=list(set(ignored_scope_1.patterns) - set(ignored_scope_2.patterns)), + types=list(set(ignored_scope_1.types) - set(ignored_scope_2.types)), + subgraphs=[subgraph for subgraph in ignored_scope_1.subgraphs if subgraph not in ignored_scope_2.subgraphs], + validate=ignored_scope_1.validate, + ) + + def convert_ignored_scope_to_list(ignored_scope: Optional[IgnoredScope]) -> List[str]: """ Convert the contents of the `IgnoredScope` class to the legacy ignored @@ -139,31 +156,32 @@ def get_matched_ignored_scope_info( The found matches consist of a dictionary with a rule name as a key and matched node names as a value. :param ignored_scope: Ignored scope instance. - :param nncf_graphs: Graphs. + :param nncf_graph: Graph. :returns: Matched ignored scope along with all matches. """ names, patterns, types, subgraphs_numbers = set(), set(), set(), set() matches = {"names": names, "patterns": set(), "types": set(), "subgraphs": set()} - for graph in nncf_graphs: - node_names = set(node.node_name for node in graph.nodes.values()) + for nncf_graph in nncf_graphs: + if ignored_scope.names or ignored_scope.patterns: + node_names = set(node.node_name for node in nncf_graph.nodes.values()) - for ignored_node_name in filter(lambda name: name in node_names, ignored_scope.names): - names.add(ignored_node_name) + for ignored_node_name in filter(lambda name: name in node_names, ignored_scope.names): + names.add(ignored_node_name) - for str_pattern in ignored_scope.patterns: - pattern = re.compile(str_pattern) - pattern_matched_names = set(filter(pattern.match, node_names)) - if pattern_matched_names: - matches["patterns"].update(pattern_matched_names) - patterns.add(str_pattern) + for str_pattern in ignored_scope.patterns: + pattern = re.compile(str_pattern) + pattern_matched_names = set(filter(pattern.match, node_names)) + if pattern_matched_names: + matches["patterns"].update(pattern_matched_names) + patterns.add(str_pattern) - for node in graph.get_nodes_by_types(ignored_scope.types): + for node in nncf_graph.get_nodes_by_types(ignored_scope.types): matches["types"].add(node.node_name) types.add(node.node_type) for i, subgraph in enumerate(ignored_scope.subgraphs): - names_from_subgraph = get_ignored_node_names_from_subgraph(graph, subgraph) + names_from_subgraph = get_ignored_node_names_from_subgraph(nncf_graph, subgraph) if names_from_subgraph: matches["subgraphs"].update(names_from_subgraph) subgraphs_numbers.add(i) @@ -178,24 +196,7 @@ def get_matched_ignored_scope_info( return matched_ignored_scope, matches -def get_unmatched_ignored_scope(matched_ignored_scope: IgnoredScope, ignored_scope: IgnoredScope) -> IgnoredScope: - """ - Returns unmatched ignored scope rules from full ignored scope and matched ignored scope. - - :param matched_ignored_scope: Matched ignored scope. - :param ignored_scope: Full ignored scope. - :return: Unmatched ignored scope. - """ - return IgnoredScope( - names=[name for name in ignored_scope.names if name not in matched_ignored_scope.names], - patterns=[pattern for pattern in ignored_scope.patterns if pattern not in matched_ignored_scope.patterns], - types=[type for type in ignored_scope.types if type not in matched_ignored_scope.types], - subgraphs=[subgraph for subgraph in ignored_scope.subgraphs if subgraph not in matched_ignored_scope.subgraphs], - validate=ignored_scope.validate, - ) - - -def info_matched_ignored_scope(matches: Dict[str, Set[str]]) -> None: +def _info_matched_ignored_scope(matches: Dict[str, Set[str]]) -> None: """ Log matches. @@ -206,7 +207,7 @@ def info_matched_ignored_scope(matches: Dict[str, Set[str]]) -> None: nncf_logger.info(f"{len(rules)} ignored nodes were found by {rule_type} in the NNCFGraph") -def error_unmatched_ignored_scope(unmatched_ignored_scope: IgnoredScope) -> str: +def _error_unmatched_ignored_scope(unmatched_ignored_scope: IgnoredScope) -> str: """ Returns an error message for unmatched ignored scope. @@ -214,10 +215,10 @@ def error_unmatched_ignored_scope(unmatched_ignored_scope: IgnoredScope) -> str: :return str: Error message. """ err_msg = "" - for ingored_type in ("names", "types", "patterns"): - unmatched_rules = getattr(unmatched_ignored_scope, ingored_type) + for ignored_type in ("names", "types", "patterns"): + unmatched_rules = getattr(unmatched_ignored_scope, ignored_type) if unmatched_rules: - err_msg += f"Ignored nodes that matches {ingored_type} {unmatched_rules} were not found in the NNCFGraph.\n" + err_msg += f"Ignored nodes that matches {ignored_type} {unmatched_rules} were not found in the NNCFGraph.\n" for subgraph in unmatched_ignored_scope.subgraphs: err_msg += ( f"Ignored nodes that matches subgraph with input names {subgraph.inputs} " @@ -230,6 +231,23 @@ def error_unmatched_ignored_scope(unmatched_ignored_scope: IgnoredScope) -> str: ) +def _check_ignored_scope_strictly_matched(ignored_scope: IgnoredScope, matched_ignored_scope: IgnoredScope) -> None: + """ + Passes when ignored_scope and matched_ignored_scope are equal, otherwise - raises ValidationError. + + :param ignored_scope: Ignored scope. + :param matched_ignored_scope: Matched ignored scope. + """ + unmatched_ignored_scope = get_difference_ignored_scope(ignored_scope, matched_ignored_scope) + if ( + unmatched_ignored_scope.names + or unmatched_ignored_scope.types + or unmatched_ignored_scope.patterns + or unmatched_ignored_scope.subgraphs + ): + raise nncf.ValidationError(_error_unmatched_ignored_scope(unmatched_ignored_scope)) + + def get_ignored_node_names_from_ignored_scope( ignored_scope: IgnoredScope, nncf_graph: NNCFGraph, strict: bool = True ) -> Set[str]: @@ -245,23 +263,17 @@ def get_ignored_node_names_from_ignored_scope( """ matched_ignored_scope, matches = get_matched_ignored_scope_info(ignored_scope, [nncf_graph]) if strict: - validate_ignored_scope(ignored_scope, matched_ignored_scope) - info_matched_ignored_scope(matches) + _check_ignored_scope_strictly_matched(ignored_scope, matched_ignored_scope) + _info_matched_ignored_scope(matches) return {name for match in matches.values() for name in match} -def validate_ignored_scope(ignored_scope: IgnoredScope, matched_ignored_scope: IgnoredScope) -> None: +def validate_ignored_scope(ignored_scope: IgnoredScope, nncf_graphs: List[NNCFGraph]) -> None: """ - Checks whether the every rule in ignored scope has a match. + Passes whether all rules at 'ignored_scope' have matches at provided graphs, otherwise - raises ValidationError. :param ignored_scope: Ignored scope. - :param matched_ignored_scope: Matched Ignored scope. + :param nncf_graphs: Graphs. """ - unmatched_ignored_scope = get_unmatched_ignored_scope(matched_ignored_scope, ignored_scope) - if ( - unmatched_ignored_scope.names - or unmatched_ignored_scope.types - or unmatched_ignored_scope.patterns - or unmatched_ignored_scope.subgraphs - ): - raise nncf.ValidationError(error_unmatched_ignored_scope(unmatched_ignored_scope)) + matched_ignored_scope, _ = get_matched_ignored_scope_info(ignored_scope, nncf_graphs) + _check_ignored_scope_strictly_matched(ignored_scope, matched_ignored_scope) From d60bc0e6a5e5f710b27eb9d4ad6039ee1c8f76bd Mon Sep 17 00:00:00 2001 From: Aleksei Kashapov Date: Thu, 13 Jun 2024 11:56:02 +0200 Subject: [PATCH 22/30] minor --- nncf/scopes.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/nncf/scopes.py b/nncf/scopes.py index c2fad6f936a..67a2fa0335a 100644 --- a/nncf/scopes.py +++ b/nncf/scopes.py @@ -156,15 +156,15 @@ def get_matched_ignored_scope_info( The found matches consist of a dictionary with a rule name as a key and matched node names as a value. :param ignored_scope: Ignored scope instance. - :param nncf_graph: Graph. + :param nncf_graphs: Graphs. :returns: Matched ignored scope along with all matches. """ names, patterns, types, subgraphs_numbers = set(), set(), set(), set() matches = {"names": names, "patterns": set(), "types": set(), "subgraphs": set()} - for nncf_graph in nncf_graphs: + for graph in nncf_graphs: if ignored_scope.names or ignored_scope.patterns: - node_names = set(node.node_name for node in nncf_graph.nodes.values()) + node_names = set(node.node_name for node in graph.nodes.values()) for ignored_node_name in filter(lambda name: name in node_names, ignored_scope.names): names.add(ignored_node_name) @@ -176,12 +176,12 @@ def get_matched_ignored_scope_info( matches["patterns"].update(pattern_matched_names) patterns.add(str_pattern) - for node in nncf_graph.get_nodes_by_types(ignored_scope.types): + for node in graph.get_nodes_by_types(ignored_scope.types): matches["types"].add(node.node_name) types.add(node.node_type) for i, subgraph in enumerate(ignored_scope.subgraphs): - names_from_subgraph = get_ignored_node_names_from_subgraph(nncf_graph, subgraph) + names_from_subgraph = get_ignored_node_names_from_subgraph(graph, subgraph) if names_from_subgraph: matches["subgraphs"].update(names_from_subgraph) subgraphs_numbers.add(i) From a364cfdd03ab20756f3980689bc1b924ab3ed079 Mon Sep 17 00:00:00 2001 From: Aleksei Kashapov Date: Thu, 13 Jun 2024 12:42:13 +0200 Subject: [PATCH 23/30] upd err msg --- nncf/scopes.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/nncf/scopes.py b/nncf/scopes.py index 67a2fa0335a..a2cee4b319b 100644 --- a/nncf/scopes.py +++ b/nncf/scopes.py @@ -214,7 +214,7 @@ def _error_unmatched_ignored_scope(unmatched_ignored_scope: IgnoredScope) -> str :param unmatched_ignored_scope: Unmatched ignored scope. :return str: Error message. """ - err_msg = "" + err_msg = "\n" for ignored_type in ("names", "types", "patterns"): unmatched_rules = getattr(unmatched_ignored_scope, ignored_type) if unmatched_rules: @@ -224,11 +224,7 @@ def _error_unmatched_ignored_scope(unmatched_ignored_scope: IgnoredScope) -> str f"Ignored nodes that matches subgraph with input names {subgraph.inputs} " f"and output names {subgraph.outputs} were not found in the NNCFGraph.\n" ) - return err_msg + ( - "Refer to the original_graph.dot to discover the operations" - "in the model currently visible to NNCF and specify the ignored/target" - " scopes in terms of the names there." - ) + return err_msg def _check_ignored_scope_strictly_matched(ignored_scope: IgnoredScope, matched_ignored_scope: IgnoredScope) -> None: From 37b8432ffc4559d81f892fd46cfde7594c59471b Mon Sep 17 00:00:00 2001 From: Aleksei Kashapov Date: Thu, 13 Jun 2024 12:45:42 +0200 Subject: [PATCH 24/30] mypy --- nncf/common/graph/graph.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nncf/common/graph/graph.py b/nncf/common/graph/graph.py index 2a91902e186..128c4cc3e3f 100644 --- a/nncf/common/graph/graph.py +++ b/nncf/common/graph/graph.py @@ -237,7 +237,7 @@ def get_nodes_by_types(self, type_list: List[str]) -> List[NNCFNode]: :param type_list: List of types to look for. :return: List of nodes with provided types. """ - all_nodes_of_type = [] + all_nodes_of_type: List[NNCFNode] = [] if not type_list: return all_nodes_of_type for nncf_node in self.nodes.values(): From 39f85444afcc176d5f459f381d4dba981686d919 Mon Sep 17 00:00:00 2001 From: Aleksei Kashapov Date: Thu, 13 Jun 2024 14:06:33 +0200 Subject: [PATCH 25/30] aligned traversing --- nncf/openvino/quantization/quantize_ifmodel.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/nncf/openvino/quantization/quantize_ifmodel.py b/nncf/openvino/quantization/quantize_ifmodel.py index a6c6336f91e..a7e48d0d960 100644 --- a/nncf/openvino/quantization/quantize_ifmodel.py +++ b/nncf/openvino/quantization/quantize_ifmodel.py @@ -27,6 +27,7 @@ from nncf.common.logging.track_progress import track from nncf.common.tensor_statistics.statistic_point import StatisticPointsContainer from nncf.openvino.graph.metatypes.openvino_metatypes import OVIfMetatype +from nncf.openvino.graph.metatypes.openvino_metatypes import get_node_metatype from nncf.openvino.graph.model_utils import remove_friendly_name_duplicates from nncf.openvino.graph.node_utils import get_number_if_op from nncf.openvino.graph.transformations.commands import OVExtractIfBodyCommand @@ -157,7 +158,10 @@ def apply_algorithm_if_bodies( if get_number_if_op(parent_model) == 0: return quantized_model, current_model_num model_transformer_fp32 = factory.ModelTransformerFactory.create(parent_model) - for if_node in parent_graph.get_nodes_by_metatypes(OVBackend.if_node_metatypes()): + for op in parent_model.get_ops(): + if get_node_metatype(op) != OVIfMetatype: + continue + if_node = parent_graph.get_node_by_name(op.get_friendly_name()) parent_model_with_additional_outputs = _add_outputs_before_if_node( model_transformer_fp32, parent_model, if_node ) From a4a219c6c63801254dccdc5bb6b163bb8733400f Mon Sep 17 00:00:00 2001 From: Aleksei Kashapov Date: Thu, 13 Jun 2024 14:22:09 +0200 Subject: [PATCH 26/30] upd graph ids based on IF node name --- nncf/openvino/quantization/quantize_ifmodel.py | 18 +++++++++--------- nncf/openvino/quantization/quantize_model.py | 18 +++++++++--------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/nncf/openvino/quantization/quantize_ifmodel.py b/nncf/openvino/quantization/quantize_ifmodel.py index a7e48d0d960..8f106d7387c 100644 --- a/nncf/openvino/quantization/quantize_ifmodel.py +++ b/nncf/openvino/quantization/quantize_ifmodel.py @@ -27,7 +27,6 @@ from nncf.common.logging.track_progress import track from nncf.common.tensor_statistics.statistic_point import StatisticPointsContainer from nncf.openvino.graph.metatypes.openvino_metatypes import OVIfMetatype -from nncf.openvino.graph.metatypes.openvino_metatypes import get_node_metatype from nncf.openvino.graph.model_utils import remove_friendly_name_duplicates from nncf.openvino.graph.node_utils import get_number_if_op from nncf.openvino.graph.transformations.commands import OVExtractIfBodyCommand @@ -135,17 +134,19 @@ def _add_outputs_before_if_node(model_transformer: ModelTransformer, model: ov.M def apply_algorithm_if_bodies( algorithm: Algorithm, parent_model: ov.Model, - graphs: Dict[int, NNCFGraph], + graphs: Dict[str, NNCFGraph], + graph_id: str, parent_dataset: Dataset, subset_size: int, current_model_num: int, parent_statistic_points: Optional[StatisticPointsContainer] = None, ) -> Tuple[ov.Model, int]: """ - Applies an algorithm recursievley to each bodies of If node. + Applies an algorithm recursively to each bodies of If node. :param parent_model: Model to apply algorithm. - :param graphs: Mapping from model_number and its graph. + :param graphs: All model graphs. + :param graph_id: Current graph id in the graphs. :param parent_dataset: Dataset for algorithm. :param subset_size: Size of a dataset to use for calibration. :param current_model_num: Current model number. @@ -153,15 +154,12 @@ def apply_algorithm_if_bodies( :return: A model for every bodies of If nodes the algorithm was applied and the latest model number. """ nncf_logger.info(f"Iteration [{current_model_num}/{len(graphs)}] ...") - parent_graph = graphs[current_model_num] + parent_graph = graphs[graph_id] quantized_model = algorithm.apply(parent_model, parent_graph, parent_statistic_points, parent_dataset) if get_number_if_op(parent_model) == 0: return quantized_model, current_model_num model_transformer_fp32 = factory.ModelTransformerFactory.create(parent_model) - for op in parent_model.get_ops(): - if get_node_metatype(op) != OVIfMetatype: - continue - if_node = parent_graph.get_node_by_name(op.get_friendly_name()) + for if_node in parent_graph.get_nodes_by_metatypes(OVBackend.if_node_metatypes()): parent_model_with_additional_outputs = _add_outputs_before_if_node( model_transformer_fp32, parent_model, if_node ) @@ -186,6 +184,7 @@ def apply_algorithm_if_bodies( algorithm, then_model, graphs, + if_node.node_name + "_then", then_dataset, subset_size, current_model_num + 1, @@ -194,6 +193,7 @@ def apply_algorithm_if_bodies( algorithm, else_model, graphs, + if_node.node_name + "_else", else_dataset, subset_size, current_model_num + 1, diff --git a/nncf/openvino/quantization/quantize_model.py b/nncf/openvino/quantization/quantize_model.py index fa6e68db3fb..e73c9883a4c 100644 --- a/nncf/openvino/quantization/quantize_model.py +++ b/nncf/openvino/quantization/quantize_model.py @@ -79,22 +79,22 @@ def native_quantize_if_op_impl( ) graphs = {} - def _extract_all_subgraphs(model: ov.Model, current_cnt: int) -> int: + def _extract_all_subgraphs(model: ov.Model, current_id: str) -> int: """ Creates all inner subgraphs from If nodes and adds them to 'graphs'. :param model: Model. - :param current_cnt: Current graph number. - :return: The next graph number. + :param current_id: Current graph id. + :return: The next graph id. """ - graphs[current_cnt] = NNCFGraphFactory.create(model) + graphs[current_id] = NNCFGraphFactory.create(model) for op in model.get_ops(): if get_node_metatype(op) == OVIfMetatype: - current_cnt = _extract_all_subgraphs(op.get_function(0), current_cnt + 1) - current_cnt = _extract_all_subgraphs(op.get_function(1), current_cnt + 1) - return current_cnt + _extract_all_subgraphs(op.get_function(0), op.get_friendly_name() + "_then") + _extract_all_subgraphs(op.get_function(1), op.get_friendly_name() + "_else") - _extract_all_subgraphs(model, 1) + main_model_graph_id = "main_model_graph" + _extract_all_subgraphs(model, main_model_graph_id) if ignored_scope and ignored_scope.validate: validate_ignored_scope(ignored_scope, graphs.values()) ignored_scope = IgnoredScope( @@ -120,7 +120,7 @@ def _extract_all_subgraphs(model: ov.Model, current_cnt: int) -> int: Main model and all If bodies will be quantized recursively." ) quantized_model, _ = apply_algorithm_if_bodies( - quantization_algorithm, model, graphs, calibration_dataset, subset_size, 1 + quantization_algorithm, model, graphs, main_model_graph_id, calibration_dataset, subset_size, 1 ) if is_weight_compression_needed(advanced_parameters): From a0e3b92df08c0cf8b3b9869f79350634591a9da9 Mon Sep 17 00:00:00 2001 From: Aleksei Kashapov Date: Thu, 13 Jun 2024 15:00:55 +0200 Subject: [PATCH 27/30] typehint --- nncf/openvino/quantization/quantize_model.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/nncf/openvino/quantization/quantize_model.py b/nncf/openvino/quantization/quantize_model.py index e73c9883a4c..d2db3eaf947 100644 --- a/nncf/openvino/quantization/quantize_model.py +++ b/nncf/openvino/quantization/quantize_model.py @@ -79,13 +79,12 @@ def native_quantize_if_op_impl( ) graphs = {} - def _extract_all_subgraphs(model: ov.Model, current_id: str) -> int: + def _extract_all_subgraphs(model: ov.Model, current_id: str) -> None: """ Creates all inner subgraphs from If nodes and adds them to 'graphs'. :param model: Model. :param current_id: Current graph id. - :return: The next graph id. """ graphs[current_id] = NNCFGraphFactory.create(model) for op in model.get_ops(): From b66028aa3f54c41745e390f23e6990e1fa6356ca Mon Sep 17 00:00:00 2001 From: Aleksei Kashapov Date: Tue, 18 Jun 2024 10:14:32 +0200 Subject: [PATCH 28/30] add test_ignored_scope_diff --- tests/common/test_scopes.py | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/tests/common/test_scopes.py b/tests/common/test_scopes.py index 0ae8ea78ba2..abb12a0e68c 100644 --- a/tests/common/test_scopes.py +++ b/tests/common/test_scopes.py @@ -13,6 +13,8 @@ from nncf.common.graph import NNCFNode from nncf.common.scopes import get_not_matched_scopes from nncf.scopes import IgnoredScope +from nncf.scopes import Subgraph +from nncf.scopes import get_difference_ignored_scope @pytest.mark.parametrize( @@ -38,3 +40,38 @@ def test_get_not_matched_scopes(scope, ref): ] not_matched = get_not_matched_scopes(scope, node_lists) assert not set(not_matched) - set(ref) + + +@pytest.mark.parametrize( + "scope_1, scope_2, ref", + ( + ( + IgnoredScope( + names=["A_name", "B_name"], + patterns=["A_pattern", "B_pattern"], + types=["A_type", "B_type"], + subgraphs=[ + Subgraph(inputs=["A_input"], outputs=["A_output"]), + Subgraph(inputs=["B_input"], outputs=["B_output"]), + ], + ), + IgnoredScope( + names=["B_name", "C_name"], + patterns=["B_pattern", "C_pattern"], + types=["B_type", "C_type"], + subgraphs=[ + Subgraph(inputs=["B_input"], outputs=["B_output"]), + Subgraph(inputs=["C_input"], outputs=["C_output"]), + ], + ), + IgnoredScope( + names=["A_name"], + patterns=["A_pattern"], + types=["A_type"], + subgraphs=[Subgraph(inputs=["A_input"], outputs=["A_output"])], + ), + ), + ), +) +def test_ignored_scope_diff(scope_1, scope_2, ref): + assert get_difference_ignored_scope(scope_1, scope_2) == ref From e59610c2ef029abf9ed6692894f63d8d25b17c90 Mon Sep 17 00:00:00 2001 From: Aleksei Kashapov Date: Tue, 18 Jun 2024 11:09:37 +0200 Subject: [PATCH 29/30] add test if model with ingored scope --- .../IfModel_2_ignored_scope_else.dot | 13 +++++++ .../IfModel_2_ignored_scope_main.dot | 9 +++++ .../IfModel_2_ignored_scope_then.dot | 35 +++++++++++++++++++ tests/openvino/native/models.py | 18 ++++++++++ .../native/quantization/test_graphs.py | 27 ++++++++++++++ 5 files changed, 102 insertions(+) create mode 100644 tests/openvino/native/data/2024.2/reference_graphs/quantized/IfModel_2_ignored_scope_else.dot create mode 100644 tests/openvino/native/data/2024.2/reference_graphs/quantized/IfModel_2_ignored_scope_main.dot create mode 100644 tests/openvino/native/data/2024.2/reference_graphs/quantized/IfModel_2_ignored_scope_then.dot diff --git a/tests/openvino/native/data/2024.2/reference_graphs/quantized/IfModel_2_ignored_scope_else.dot b/tests/openvino/native/data/2024.2/reference_graphs/quantized/IfModel_2_ignored_scope_else.dot new file mode 100644 index 00000000000..a0e344d66ea --- /dev/null +++ b/tests/openvino/native/data/2024.2/reference_graphs/quantized/IfModel_2_ignored_scope_else.dot @@ -0,0 +1,13 @@ +strict digraph { +"0 Input" [id=0, type=Parameter]; +"1 MatMul" [id=1, type=MatMul]; +"2 Add" [id=2, type=Add]; +"3 Result_Add" [id=3, type=Result]; +"4 Add/Constant_16" [id=4, type=Constant]; +"5 MatMul/Constant_14" [id=5, type=Constant]; +"0 Input" -> "1 MatMul" [label="[1, 3, 4, 2]", style=solid]; +"1 MatMul" -> "2 Add" [label="[1, 3, 2, 5]", style=solid]; +"2 Add" -> "3 Result_Add" [label="[1, 3, 2, 5]", style=solid]; +"4 Add/Constant_16" -> "2 Add" [label="[1, 3, 1, 1]", style=solid]; +"5 MatMul/Constant_14" -> "1 MatMul" [label="[1, 3, 4, 5]", style=solid]; +} diff --git a/tests/openvino/native/data/2024.2/reference_graphs/quantized/IfModel_2_ignored_scope_main.dot b/tests/openvino/native/data/2024.2/reference_graphs/quantized/IfModel_2_ignored_scope_main.dot new file mode 100644 index 00000000000..9ac187f8df5 --- /dev/null +++ b/tests/openvino/native/data/2024.2/reference_graphs/quantized/IfModel_2_ignored_scope_main.dot @@ -0,0 +1,9 @@ +strict digraph { +"0 Input_1" [id=0, type=Parameter]; +"1 Cond_input" [id=1, type=Parameter]; +"2 If_19" [id=2, type=If]; +"3 Result" [id=3, type=Result]; +"0 Input_1" -> "2 If_19" [label="[1, 3, 4, 2]", style=solid]; +"1 Cond_input" -> "2 If_19" [label="[]", style=dashed]; +"2 If_19" -> "3 Result" [label="[1, 3, 4, 5]", style=solid]; +} diff --git a/tests/openvino/native/data/2024.2/reference_graphs/quantized/IfModel_2_ignored_scope_then.dot b/tests/openvino/native/data/2024.2/reference_graphs/quantized/IfModel_2_ignored_scope_then.dot new file mode 100644 index 00000000000..63d45ac61ea --- /dev/null +++ b/tests/openvino/native/data/2024.2/reference_graphs/quantized/IfModel_2_ignored_scope_then.dot @@ -0,0 +1,35 @@ +strict digraph { +"0 Input_1" [id=0, type=Parameter]; +"1 Sub" [id=1, type=Subtract]; +"2 Sub/fq_output_0" [id=2, type=FakeQuantize]; +"3 Conv" [id=3, type=Convolution]; +"4 Conv_Add" [id=4, type=Add]; +"5 Relu" [id=5, type=Relu]; +"6 Result" [id=6, type=Result]; +"7 NotBias" [id=7, type=Constant]; +"8 Conv/fq_weights_1" [id=8, type=Multiply]; +"9 Constant_14900" [id=9, type=Constant]; +"10 Convert_14899" [id=10, type=Convert]; +"11 Constant_14898" [id=11, type=Constant]; +"12 Sub/fq_output_0/output_high" [id=12, type=Constant]; +"13 Sub/fq_output_0/output_low" [id=13, type=Constant]; +"14 Sub/fq_output_0/input_high" [id=14, type=Constant]; +"15 Sub/fq_output_0/input_low" [id=15, type=Constant]; +"16 Sub/Constant_4" [id=16, type=Constant]; +"0 Input_1" -> "1 Sub" [label="[1, 3, 4, 2]", style=solid]; +"1 Sub" -> "2 Sub/fq_output_0" [label="[1, 3, 4, 2]", style=solid]; +"2 Sub/fq_output_0" -> "3 Conv" [label="[1, 3, 4, 2]", style=solid]; +"3 Conv" -> "4 Conv_Add" [label="[1, 3, 4, 2]", style=solid]; +"4 Conv_Add" -> "5 Relu" [label="[1, 3, 4, 2]", style=solid]; +"5 Relu" -> "6 Result" [label="[1, 3, 4, 2]", style=solid]; +"7 NotBias" -> "4 Conv_Add" [label="[1, 3, 4, 2]", style=solid]; +"8 Conv/fq_weights_1" -> "3 Conv" [label="[3, 3, 1, 1]", style=solid]; +"9 Constant_14900" -> "8 Conv/fq_weights_1" [label="[3, 1, 1, 1]", style=solid]; +"10 Convert_14899" -> "8 Conv/fq_weights_1" [label="[3, 3, 1, 1]", style=solid]; +"11 Constant_14898" -> "10 Convert_14899" [label="[3, 3, 1, 1]", style=dashed]; +"12 Sub/fq_output_0/output_high" -> "2 Sub/fq_output_0" [label="[]", style=solid]; +"13 Sub/fq_output_0/output_low" -> "2 Sub/fq_output_0" [label="[]", style=solid]; +"14 Sub/fq_output_0/input_high" -> "2 Sub/fq_output_0" [label="[]", style=solid]; +"15 Sub/fq_output_0/input_low" -> "2 Sub/fq_output_0" [label="[]", style=solid]; +"16 Sub/Constant_4" -> "1 Sub" [label="[1, 3, 1, 1]", style=solid]; +} diff --git a/tests/openvino/native/models.py b/tests/openvino/native/models.py index 99640f6d175..840d5224248 100644 --- a/tests/openvino/native/models.py +++ b/tests/openvino/native/models.py @@ -1058,3 +1058,21 @@ def _create_ov_model(self, stateful=True): model = ov.Model(results=[result], parameters=[input_data], name="TestModel") return model + + +class IfModel_2(OVReferenceModel): + def _create_ov_model(self): + input_1 = opset.parameter([1, 3, 4, 2], name="Input_1") + input_2 = opset.parameter([], dtype=bool, name="Cond_input") + + then_body = ConvNotBiasModel().ov_model + else_body = FPModel().ov_model + + if_node = opset.if_op(input_2) + if_node.set_then_body(then_body) + if_node.set_else_body(else_body) + if_node.set_input(input_1.outputs()[0], then_body.get_parameters()[0], else_body.get_parameters()[0]) + if_node.set_output(then_body.results[0], else_body.results[0]) + result = opset.result(if_node, name="Result") + model = ov.Model([result], [input_1, input_2]) + return model diff --git a/tests/openvino/native/quantization/test_graphs.py b/tests/openvino/native/quantization/test_graphs.py index c97a4873f70..1a36a7c0331 100644 --- a/tests/openvino/native/quantization/test_graphs.py +++ b/tests/openvino/native/quantization/test_graphs.py @@ -26,6 +26,7 @@ from nncf.parameters import QuantizationMode from nncf.parameters import TargetDevice from nncf.quantization.algorithms.smooth_quant.algorithm import SmoothQuant +from nncf.scopes import IgnoredScope from tests.openvino.native.common import compare_nncf_graphs from tests.openvino.native.common import convert_torch_model from tests.openvino.native.common import dump_model @@ -38,6 +39,7 @@ from tests.openvino.native.models import DepthwiseConv5DModel from tests.openvino.native.models import GRUSequenceModel from tests.openvino.native.models import IfModel +from tests.openvino.native.models import IfModel_2 from tests.openvino.native.models import MatmulSoftmaxMatmulBlock from tests.openvino.native.models import ScaledDotProductAttentionModel from tests.openvino.native.models import get_torch_model_info @@ -202,6 +204,31 @@ def test_if_model_fq_placement(): ) +def test_if_model_fq_placement_ignored_scope(): + if_model = IfModel_2() + ov_model = if_model.ov_model + dataset = get_dataset_for_if_model(ov_model) + quantized_model = quantize_impl( + ov_model, dataset, subset_size=2, fast_bias_correction=True, ignored_scope=IgnoredScope(names=["MatMul"]) + ) + if_ops = [op for op in quantized_model.get_ops() if op.get_type_name() == "If"] + assert len(if_ops) == 1 + if_op = if_ops[0] + main_model_path = if_model.ref_model_name + "_ignored_scope_main.dot" + then_body_path = if_model.ref_model_name + "_ignored_scope_then.dot" + else_body_path = if_model.ref_model_name + "_ignored_scope_else.dot" + + compare_nncf_graphs( + quantized_model, get_actual_reference_for_current_openvino(QUANTIZED_REF_GRAPHS_DIR / main_model_path) + ) + compare_nncf_graphs( + if_op.get_function(0), get_actual_reference_for_current_openvino(QUANTIZED_REF_GRAPHS_DIR / then_body_path) + ) + compare_nncf_graphs( + if_op.get_function(1), get_actual_reference_for_current_openvino(QUANTIZED_REF_GRAPHS_DIR / else_body_path) + ) + + @pytest.mark.parametrize("q_params", [{}, {"model_type": ModelType.TRANSFORMER}], ids=["default", "transformer"]) def test_scaled_dot_product_attention_placement(q_params, tmp_path): ov_major_version, ov_minor_version = get_openvino_major_minor_version() From 1c88dee9d0a294cf57af87946ea5121101bd1377 Mon Sep 17 00:00:00 2001 From: Aleksei Kashapov Date: Tue, 18 Jun 2024 11:18:30 +0200 Subject: [PATCH 30/30] mv refs --- .../reference_graphs/quantized/IfModel_2_ignored_scope_else.dot | 0 .../reference_graphs/quantized/IfModel_2_ignored_scope_main.dot | 0 .../reference_graphs/quantized/IfModel_2_ignored_scope_then.dot | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename tests/openvino/native/data/{2024.2 => 2024.1}/reference_graphs/quantized/IfModel_2_ignored_scope_else.dot (100%) rename tests/openvino/native/data/{2024.2 => 2024.1}/reference_graphs/quantized/IfModel_2_ignored_scope_main.dot (100%) rename tests/openvino/native/data/{2024.2 => 2024.1}/reference_graphs/quantized/IfModel_2_ignored_scope_then.dot (100%) diff --git a/tests/openvino/native/data/2024.2/reference_graphs/quantized/IfModel_2_ignored_scope_else.dot b/tests/openvino/native/data/2024.1/reference_graphs/quantized/IfModel_2_ignored_scope_else.dot similarity index 100% rename from tests/openvino/native/data/2024.2/reference_graphs/quantized/IfModel_2_ignored_scope_else.dot rename to tests/openvino/native/data/2024.1/reference_graphs/quantized/IfModel_2_ignored_scope_else.dot diff --git a/tests/openvino/native/data/2024.2/reference_graphs/quantized/IfModel_2_ignored_scope_main.dot b/tests/openvino/native/data/2024.1/reference_graphs/quantized/IfModel_2_ignored_scope_main.dot similarity index 100% rename from tests/openvino/native/data/2024.2/reference_graphs/quantized/IfModel_2_ignored_scope_main.dot rename to tests/openvino/native/data/2024.1/reference_graphs/quantized/IfModel_2_ignored_scope_main.dot diff --git a/tests/openvino/native/data/2024.2/reference_graphs/quantized/IfModel_2_ignored_scope_then.dot b/tests/openvino/native/data/2024.1/reference_graphs/quantized/IfModel_2_ignored_scope_then.dot similarity index 100% rename from tests/openvino/native/data/2024.2/reference_graphs/quantized/IfModel_2_ignored_scope_then.dot rename to tests/openvino/native/data/2024.1/reference_graphs/quantized/IfModel_2_ignored_scope_then.dot