diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index bee5dccbf88..d2c6d89093a 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -10,4 +10,4 @@ jobs: runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 - - uses: AlexanderDokuchaev/md-dead-link-check@v0.6 + - uses: AlexanderDokuchaev/md-dead-link-check@v0.8 diff --git a/.github/workflows/pre-commit-linters.yml b/.github/workflows/pre-commit-linters.yml index 0c911b8f0c6..288566d66d2 100644 --- a/.github/workflows/pre-commit-linters.yml +++ b/.github/workflows/pre-commit-linters.yml @@ -23,4 +23,4 @@ jobs: runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 - - uses: AlexanderDokuchaev/md-dead-link-check@v0.6 + - uses: AlexanderDokuchaev/md-dead-link-check@v0.8 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 11dd747ce2e..34a3e5a293f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -17,7 +17,7 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: v0.1.3 + rev: v0.3.7 hooks: - id: ruff diff --git a/README.md b/README.md index ac9f925c905..24978e23c4a 100644 --- a/README.md +++ b/README.md @@ -374,6 +374,10 @@ pip install nncf[torch] Other viable options besides `[torch]` are `[tf]`, `[onnx]` and `[openvino]`. +> [!WARNING] +> The way to install the module package with the extra dependency like `pip install nncf[torch]` is deprecated and will be removed in a future release. +> Instead, it is recommended to install additional dependencies separately using the pip install command (e.g., `pip install torch`) or by explicitly specifying the dependency in your requirements file. + NNCF is also available via [conda](https://anaconda.org/conda-forge/nncf): ```bash @@ -383,7 +387,7 @@ conda install -c conda-forge nncf ### System requirements - Ubuntu\* 18.04 or later (64-bit) -- Python\* 3.7 or later +- Python\* 3.8 or later - Supported frameworks: - PyTorch\* >=2.1, <2.3 - TensorFlow\* >=2.8.4, <=2.12.1 diff --git a/docs/Installation.md b/docs/Installation.md index 3fdd4acfe0a..3d0f825ab5a 100644 --- a/docs/Installation.md +++ b/docs/Installation.md @@ -12,14 +12,6 @@ NNCF can be installed as a regular PyPI package via pip: pip install nncf ``` -If you want to install both NNCF and the supported PyTorch version in one line, you can do this by simply running: - -```bash -pip install nncf[torch] -``` - -Other viable options besides `[torch]` are `[tf]`, `[onnx]` and `[openvino]`. - ## As a package built from a checked-out repository Install the package and its dependencies by running the following command in the repository root directory: @@ -28,20 +20,6 @@ Install the package and its dependencies by running the following command in the pip install . ``` -Use the same `pip install` syntax as above to install NNCF along with the backend package version in one go: - -```bash -pip install .[] -``` - -List of supported backends: `torch`, `tf`, `onnx` and `openvino`. - -For development purposes install extra packages by - -```bash -pip install .[dev,tests] -``` - _NB_: For launching example scripts in this repository, we recommend setting the `PYTHONPATH` variable to the root of the checked-out repository once the installation is completed. NNCF is also available via [conda](https://anaconda.org/conda-forge/nncf): @@ -65,7 +43,7 @@ as well as the supported versions of Python: | NNCF | OpenVINO | PyTorch | ONNX | TensorFlow | Python | |-----------|------------|----------|----------|------------|--------| -| `develop` | `2024.4.0` | `2.2.1` | `1.13.1` | `2.12.0` | `3.8` | +| `develop` | `2024.4.0` | `2.2.1` | `1.16.0` | `2.12.0` | `3.8` | | `2.9.0` | `2024.4.0` | `2.1.2` | `1.13.1` | `2.12.0` | `3.8` | | `2.8.1` | `2023.3.0` | `2.1.2` | `1.13.1` | `2.12.0` | `3.8` | | `2.8.0` | `2023.3.0` | `2.1.2` | `1.13.1` | `2.12.0` | `3.8` | diff --git a/examples/post_training_quantization/onnx/yolov8_quantize_with_accuracy_control/main.py b/examples/post_training_quantization/onnx/yolov8_quantize_with_accuracy_control/main.py index 16d7bf53a1f..2109b750b3b 100644 --- a/examples/post_training_quantization/onnx/yolov8_quantize_with_accuracy_control/main.py +++ b/examples/post_training_quantization/onnx/yolov8_quantize_with_accuracy_control/main.py @@ -195,20 +195,17 @@ def validation_ac( preset=nncf.QuantizationPreset.MIXED, ignored_scope=nncf.IgnoredScope( types=["Mul", "Sub", "Sigmoid"], # ignore operations - names=[ - "/model.22/dfl/conv/Conv", # in the post-processing subgraph - "/model.22/Add", - "/model.22/Add_1", - "/model.22/Add_2", - "/model.22/Add_3", - "/model.22/Add_4", - "/model.22/Add_5", - "/model.22/Add_6", - "/model.22/Add_7", - "/model.22/Add_8", - "/model.22/Add_9", - "/model.22/Add_10", - "/model.22/Add_11", + subgraphs=[ + nncf.Subgraph( + inputs=[ + "/model.22/Concat_3", + "/model.22/Concat_6", + "/model.22/Concat_24", + "/model.22/Concat_5", + "/model.22/Concat_4", + ], + outputs=["/model.22/Concat_29"], + ) ], ), ) diff --git a/examples/post_training_quantization/openvino/yolov8/main.py b/examples/post_training_quantization/openvino/yolov8/main.py index d8513400e4d..5ecdedcb120 100644 --- a/examples/post_training_quantization/openvino/yolov8/main.py +++ b/examples/post_training_quantization/openvino/yolov8/main.py @@ -122,20 +122,12 @@ def transform_fn(data_item: Dict): quantization_dataset, preset=nncf.QuantizationPreset.MIXED, ignored_scope=nncf.IgnoredScope( - types=["Multiply", "Subtract", "Sigmoid"], # ignore operations - names=[ - "/model.22/dfl/conv/Conv", # in the post-processing subgraph - "/model.22/Add", - "/model.22/Add_1", - "/model.22/Add_2", - "/model.22/Add_3", - "/model.22/Add_4", - "/model.22/Add_5", - "/model.22/Add_6", - "/model.22/Add_7", - "/model.22/Add_8", - "/model.22/Add_9", - "/model.22/Add_10", + types=["Multiply", "Subtract", "Sigmoid"], + subgraphs=[ + nncf.Subgraph( + inputs=["/model.22/Concat", "/model.22/Concat_1", "/model.22/Concat_2"], + outputs=["output0/sink_port_0"], + ) ], ), ) diff --git a/examples/post_training_quantization/openvino/yolov8_quantize_with_accuracy_control/main.py b/examples/post_training_quantization/openvino/yolov8_quantize_with_accuracy_control/main.py index 79722754ced..255fe9fe5f9 100644 --- a/examples/post_training_quantization/openvino/yolov8_quantize_with_accuracy_control/main.py +++ b/examples/post_training_quantization/openvino/yolov8_quantize_with_accuracy_control/main.py @@ -186,20 +186,17 @@ def validation_ac( preset=nncf.QuantizationPreset.MIXED, ignored_scope=nncf.IgnoredScope( types=["Multiply", "Subtract", "Sigmoid"], # ignore operations - names=[ - "/model.22/dfl/conv/Conv", # in the post-processing subgraph - "/model.22/Add", - "/model.22/Add_1", - "/model.22/Add_2", - "/model.22/Add_3", - "/model.22/Add_4", - "/model.22/Add_5", - "/model.22/Add_6", - "/model.22/Add_7", - "/model.22/Add_8", - "/model.22/Add_9", - "/model.22/Add_10", - "/model.22/Add_11", + subgraphs=[ + nncf.Subgraph( + inputs=[ + "/model.22/Concat_3", + "/model.22/Concat_6", + "/model.22/Concat_24", + "/model.22/Concat_5", + "/model.22/Concat_4", + ], + outputs=["output0"], + ) ], ), ) diff --git a/examples/post_training_quantization/torch/ssd300_vgg16/main.py b/examples/post_training_quantization/torch/ssd300_vgg16/main.py index fc359e750b3..1b586f4a995 100644 --- a/examples/post_training_quantization/torch/ssd300_vgg16/main.py +++ b/examples/post_training_quantization/torch/ssd300_vgg16/main.py @@ -70,9 +70,9 @@ def run_benchmark(model_path: str, shape=None, verbose: bool = True) -> float: class COCO128Dataset(torch.utils.data.Dataset): category_mapping = [ - 1,2,3,4,5,6,7,8,9,10,11,13,14,15,16,17,18,19,20,21,22,23,24,25,27,28,31,32,33, - 34,35,36,37,38,39,40,41,42,43,44,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60, - 61,62,63,64,65,67,70,72,73,74,75,76,77,78,79,80,81,82,84,85,86,87,88,89,90 + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 27, 28, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, + 61, 62, 63, 64, 65, 67, 70, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 84, 85, 86, 87, 88, 89, 90 ] # fmt: skip def __init__(self, data_path: str, transform: Callable): diff --git a/examples/torch/common/models/classification/mobilenet_v3_tv_092.py b/examples/torch/common/models/classification/mobilenet_v3_tv_092.py index 5ce32170bd2..1ba5f4d5cb3 100644 --- a/examples/torch/common/models/classification/mobilenet_v3_tv_092.py +++ b/examples/torch/common/models/classification/mobilenet_v3_tv_092.py @@ -305,7 +305,7 @@ def _mobilenet_v3_model( ): model = MobileNetV3(inverted_residual_setting, last_channel, **kwargs) if pretrained: - if model_urls.get(arch, None) is None: + if model_urls.get(arch) is None: raise ValueError("No checkpoint is available for model type {}".format(arch)) state_dict = load_state_dict_from_url(model_urls[arch], progress=progress) model.load_state_dict(state_dict) diff --git a/nncf/__init__.py b/nncf/__init__.py index 34f1d3a1d69..9af922c21a5 100644 --- a/nncf/__init__.py +++ b/nncf/__init__.py @@ -45,7 +45,9 @@ from nncf.quantization.advanced_parameters import ( AdvancedAccuracyRestorerParameters as AdvancedAccuracyRestorerParameters, ) +from nncf.quantization.advanced_parameters import AdvancedBiasCorrectionParameters as AdvancedBiasCorrectionParameters from nncf.quantization.advanced_parameters import AdvancedQuantizationParameters as AdvancedQuantizationParameters +from nncf.quantization.advanced_parameters import AdvancedSmoothQuantParameters as AdvancedSmoothQuantParameters from nncf.quantization.advanced_parameters import OverflowFix as OverflowFix from nncf.scopes import IgnoredScope as IgnoredScope from nncf.scopes import Subgraph as Subgraph diff --git a/nncf/common/graph/operator_metatypes.py b/nncf/common/graph/operator_metatypes.py index 27fc60213a4..100f510428a 100644 --- a/nncf/common/graph/operator_metatypes.py +++ b/nncf/common/graph/operator_metatypes.py @@ -79,11 +79,12 @@ def __init__(self, name: str): super().__init__(name) self._op_name_to_op_meta_dict: Dict[str, Type[OperatorMetatype]] = {} - def register(self, name: Optional[str] = None) -> Callable[..., Type[OperatorMetatype]]: + def register(self, name: Optional[str] = None, is_subtype: bool = False) -> Callable[..., Type[OperatorMetatype]]: """ Decorator for registering operator metatypes. :param name: The registration name. + :param is_subtype: Whether the decorated metatype is a subtype of another registered operator. :return: The inner function for registering operator metatypes. """ name_ = name @@ -100,15 +101,15 @@ def wrap(obj: Type[OperatorMetatype]) -> Type[OperatorMetatype]: if cls_name is None: cls_name = obj.__name__ super_register(obj, cls_name) - op_names = obj.get_all_aliases() - for name in op_names: - if name in self._op_name_to_op_meta_dict and not obj.subtype_check(self._op_name_to_op_meta_dict[name]): - raise nncf.InternalError( - "Inconsistent operator metatype registry - single patched " - "op name maps to multiple metatypes!" - ) - - self._op_name_to_op_meta_dict[name] = obj + if not is_subtype: + op_names = obj.get_all_aliases() + for name in op_names: + if name in self._op_name_to_op_meta_dict: + raise nncf.InternalError( + "Inconsistent operator metatype registry - single patched " + f"op name `{name}` maps to multiple metatypes!" + ) + self._op_name_to_op_meta_dict[name] = obj return obj return wrap diff --git a/nncf/common/sparsity/schedulers.py b/nncf/common/sparsity/schedulers.py index b8acc2bc809..c23131f7c0a 100644 --- a/nncf/common/sparsity/schedulers.py +++ b/nncf/common/sparsity/schedulers.py @@ -133,7 +133,7 @@ def __init__(self, controller: SparsityController, params: Dict[str, Any]): self._update_per_optimizer_step = params.get( "update_per_optimizer_step", SPARSITY_SCHEDULER_UPDATE_PER_OPTIMIZER_STEP ) - self._steps_per_epoch = params.get("steps_per_epoch", None) + self._steps_per_epoch = params.get("steps_per_epoch") self._should_skip = False def step(self, next_step: Optional[int] = None) -> None: diff --git a/nncf/config/schemata/algo/filter_pruning.py b/nncf/config/schemata/algo/filter_pruning.py index 31c184346ac..25efa70c930 100644 --- a/nncf/config/schemata/algo/filter_pruning.py +++ b/nncf/config/schemata/algo/filter_pruning.py @@ -27,7 +27,11 @@ from nncf.config.schemata.defaults import PRUNING_INTERLAYER_RANKING_TYPE from nncf.config.schemata.defaults import PRUNING_LEGR_GENERATIONS from nncf.config.schemata.defaults import PRUNING_LEGR_MAX_PRUNING +from nncf.config.schemata.defaults import PRUNING_LEGR_MUTATE_PERCENT +from nncf.config.schemata.defaults import PRUNING_LEGR_NUM_SAMPLES +from nncf.config.schemata.defaults import PRUNING_LEGR_POPULATION_SIZE from nncf.config.schemata.defaults import PRUNING_LEGR_RANDOM_SEED +from nncf.config.schemata.defaults import PRUNING_LEGR_SIGMA_SCALE from nncf.config.schemata.defaults import PRUNING_LEGR_TRAIN_STEPS from nncf.config.schemata.defaults import PRUNING_NUM_INIT_STEPS from nncf.config.schemata.defaults import PRUNING_SCHEDULE @@ -162,6 +166,26 @@ description="Random seed for LeGR coefficients generation.", default=PRUNING_LEGR_RANDOM_SEED, ), + "population_size": with_attributes( + NUMBER, + description="Size of population for the evolution algorithm.", + default=PRUNING_LEGR_POPULATION_SIZE, + ), + "num_samples": with_attributes( + NUMBER, + description="Number of samples for the evolution algorithm.", + default=PRUNING_LEGR_NUM_SAMPLES, + ), + "mutate_percent": with_attributes( + NUMBER, + description="Percent of mutate for the evolution algorithm.", + default=PRUNING_LEGR_MUTATE_PERCENT, + ), + "scale_sigma": with_attributes( + NUMBER, + description="Scale sigma for the evolution algorithm.", + default=PRUNING_LEGR_SIGMA_SCALE, + ), }, "additionalProperties": False, }, diff --git a/nncf/config/schemata/defaults.py b/nncf/config/schemata/defaults.py index d1a6471a768..3bf599fce95 100644 --- a/nncf/config/schemata/defaults.py +++ b/nncf/config/schemata/defaults.py @@ -59,6 +59,10 @@ PRUNING_LEGR_TRAIN_STEPS = 200 PRUNING_LEGR_MAX_PRUNING = 0.8 PRUNING_LEGR_RANDOM_SEED = 42 +PRUNING_LEGR_POPULATION_SIZE = 64 +PRUNING_LEGR_NUM_SAMPLES = 16 +PRUNING_LEGR_MUTATE_PERCENT = 0.1 +PRUNING_LEGR_SIGMA_SCALE = 1 SPARSITY_INIT = 0.0 MAGNITUDE_SPARSITY_WEIGHT_IMPORTANCE = "normed_abs" diff --git a/nncf/experimental/common/tensor_statistics/collectors.py b/nncf/experimental/common/tensor_statistics/collectors.py index 1017877bf24..700cabb63a3 100644 --- a/nncf/experimental/common/tensor_statistics/collectors.py +++ b/nncf/experimental/common/tensor_statistics/collectors.py @@ -461,7 +461,9 @@ def __init__(self, tensor_collectors: List[TensorCollector]) -> None: self._aggregators[key] = unique_aggregator -##################################################Reducers################################################## +################################################## +# Reducers +################################################## class NoopReducer(TensorReducerBase): @@ -578,7 +580,9 @@ def __hash__(self) -> int: return hash((self.__class__.__name__, self.inplace, self._reduction_axes, self._channel_axis)) -##################################################Aggregators################################################## +################################################## +# Aggregators +################################################## class NoopAggregator(AggregatorBase): diff --git a/nncf/experimental/torch/nas/bootstrapNAS/search/search.py b/nncf/experimental/torch/nas/bootstrapNAS/search/search.py index d5ca9cd55e1..23800b019b5 100644 --- a/nncf/experimental/torch/nas/bootstrapNAS/search/search.py +++ b/nncf/experimental/torch/nas/bootstrapNAS/search/search.py @@ -710,9 +710,8 @@ def _evaluate(self, x: List[float], out: Dict[str, Any], *args, **kargs) -> NoRe result = [sample] - eval_idx = 0 bn_adaption_executed = False - for evaluator_handler in self._evaluator_handlers: + for eval_idx, evaluator_handler in enumerate(self._evaluator_handlers): in_cache, value = evaluator_handler.retrieve_from_cache(tuple(x_i)) if not in_cache: if not bn_adaption_executed and self._search.bn_adaptation is not None: @@ -720,7 +719,6 @@ def _evaluate(self, x: List[float], out: Dict[str, Any], *args, **kargs) -> NoRe bn_adaption_executed = True value = evaluator_handler.evaluate_and_add_to_cache_from_pymoo(tuple(x_i)) evaluators_arr[eval_idx].append(value) - eval_idx += 1 result.append(evaluator_handler.name) result.append(value) diff --git a/nncf/experimental/torch/nas/bootstrapNAS/training/scheduler.py b/nncf/experimental/torch/nas/bootstrapNAS/training/scheduler.py index 3579dbb6dff..9dafe0d0ee9 100644 --- a/nncf/experimental/torch/nas/bootstrapNAS/training/scheduler.py +++ b/nncf/experimental/torch/nas/bootstrapNAS/training/scheduler.py @@ -191,12 +191,10 @@ def get_current_stage_desc(self) -> Tuple[Optional[StageDescriptor], int]: :return: current stage descriptor and its index in the list of all descriptors """ partial_epochs = 0 - stage_desc_idx = 0 - for stage_desc in self.list_stage_descriptors: + for stage_desc_idx, stage_desc in enumerate(self.list_stage_descriptors): partial_epochs += stage_desc.epochs if self.current_epoch < partial_epochs: return stage_desc, stage_desc_idx - stage_desc_idx += 1 return None, -1 def get_total_training_epochs(self) -> int: diff --git a/nncf/experimental/torch/sparsity/movement/scheduler.py b/nncf/experimental/torch/sparsity/movement/scheduler.py index 4274e4206e5..33c8d9d51da 100644 --- a/nncf/experimental/torch/sparsity/movement/scheduler.py +++ b/nncf/experimental/torch/sparsity/movement/scheduler.py @@ -97,16 +97,16 @@ def from_dict(cls, params: Dict[str, Any]) -> "MovementSchedulerParams": :param params: A dict that specifies the parameters of movement sparsity scheduler. :return: A `MovementSchedulerParams` object that stores the parameters from `params`. """ - warmup_start_epoch: int = params.get("warmup_start_epoch", None) - warmup_end_epoch: int = params.get("warmup_end_epoch", None) - importance_regularization_factor: float = params.get("importance_regularization_factor", None) + warmup_start_epoch: int = params.get("warmup_start_epoch") + warmup_end_epoch: int = params.get("warmup_end_epoch") + importance_regularization_factor: float = params.get("importance_regularization_factor") enable_structured_masking: bool = params.get("enable_structured_masking", MOVEMENT_ENABLE_STRUCTURED_MASKING) - init_importance_threshold: Optional[float] = params.get("init_importance_threshold", None) + init_importance_threshold: Optional[float] = params.get("init_importance_threshold") final_importance_threshold: float = params.get( "final_importance_threshold", MOVEMENT_FINAL_IMPORTANCE_THRESHOLD ) power: float = params.get("power", MOVEMENT_POWER) - steps_per_epoch: Optional[int] = params.get("steps_per_epoch", None) + steps_per_epoch: Optional[int] = params.get("steps_per_epoch") if None in [warmup_start_epoch, warmup_end_epoch, importance_regularization_factor]: raise ValueError( diff --git a/nncf/onnx/graph/metatypes/onnx_metatypes.py b/nncf/onnx/graph/metatypes/onnx_metatypes.py index d7e984b8d44..2105f31a216 100644 --- a/nncf/onnx/graph/metatypes/onnx_metatypes.py +++ b/nncf/onnx/graph/metatypes/onnx_metatypes.py @@ -71,7 +71,7 @@ class ONNXOpWithWeightsMetatype(ONNXOpMetatype): bias_port_id: Optional[int] = None -@ONNX_OPERATION_METATYPES.register() +@ONNX_OPERATION_METATYPES.register(is_subtype=True) class ONNXDepthwiseConvolutionMetatype(ONNXOpWithWeightsMetatype): name = "DepthwiseConvOp" op_names = ["Conv"] @@ -86,7 +86,7 @@ def matches(cls, model: onnx.ModelProto, node: onnx.NodeProto) -> bool: return _is_depthwise_conv(model, node) -@ONNX_OPERATION_METATYPES.register() +@ONNX_OPERATION_METATYPES.register(is_subtype=True) class ONNXGroupConvolutionMetatype(ONNXOpWithWeightsMetatype): name = "GroupConvOp" op_names = ["Conv"] @@ -420,7 +420,7 @@ class ONNXReciprocalMetatype(ONNXOpMetatype): hw_config_names = [HWConfigOpName.POWER] -@ONNX_OPERATION_METATYPES.register() +@ONNX_OPERATION_METATYPES.register(is_subtype=True) class ONNXEmbeddingMetatype(ONNXOpMetatype): name = "EmbeddingOp" hw_config_names = [HWConfigOpName.EMBEDDING] diff --git a/nncf/openvino/graph/metatypes/openvino_metatypes.py b/nncf/openvino/graph/metatypes/openvino_metatypes.py index 3600ef7e71b..eb806ddfffa 100644 --- a/nncf/openvino/graph/metatypes/openvino_metatypes.py +++ b/nncf/openvino/graph/metatypes/openvino_metatypes.py @@ -70,7 +70,7 @@ class OVConvolutionBackpropDataMetatype(OVOpMetatype): output_channel_axis = 1 -@OV_OPERATOR_METATYPES.register() +@OV_OPERATOR_METATYPES.register(is_subtype=True) class OVDepthwiseConvolutionMetatype(OVOpMetatype): name = "DepthwiseConvolutionOp" op_names = ["GroupConvolution"] @@ -410,7 +410,7 @@ class OVLogicalXorMetatype(OVOpMetatype): hw_config_names = [HWConfigOpName.LOGICALXOR] -@OV_OPERATOR_METATYPES.register() +@OV_OPERATOR_METATYPES.register(is_subtype=True) class OVEmbeddingMetatype(OVOpMetatype): name = "EmbeddingOp" hw_config_names = [HWConfigOpName.EMBEDDING] diff --git a/nncf/quantization/advanced_parameters.py b/nncf/quantization/advanced_parameters.py index fe058c9c779..dd74741eecc 100644 --- a/nncf/quantization/advanced_parameters.py +++ b/nncf/quantization/advanced_parameters.py @@ -59,7 +59,7 @@ class OverflowFix(StrEnum): @api() -class FP8Type(Enum): +class FP8Type(StrEnum): """ Defines FP8 special types (https://arxiv.org/pdf/2209.05433.pdf). diff --git a/nncf/quantization/algorithms/min_max/algorithm.py b/nncf/quantization/algorithms/min_max/algorithm.py index 97f6cc53ca2..92b2d5067f5 100644 --- a/nncf/quantization/algorithms/min_max/algorithm.py +++ b/nncf/quantization/algorithms/min_max/algorithm.py @@ -863,25 +863,29 @@ def filter_func(point: StatisticPoint) -> bool: group_statistics.append(statistics) unified_values = self._backend_entity.unify_statistics(group_statistics) - for quantization_target_point in unified_scale_group: - qconfig = quantization_target_points[quantization_target_point] - q_group = QuantizerGroup.ACTIVATIONS - narrow_range = get_quantizer_narrow_range(qconfig, q_group) - if self._mode is not None: - destination_type = self._quantization_params[q_group].destination_type - parameters = calculate_convert_parameters( - unified_values, is_per_channel=qconfig.per_channel, destination_type=destination_type - ) - command = self._backend_entity.create_convert_insertion_command( - quantization_target_point, parameters - ) - else: - parameters = calculate_quantizer_parameters(unified_values, qconfig, q_group, narrow_range) - command = self._backend_entity.create_quantizer_insertion_command( - graph, quantization_target_point, qconfig, parameters + qconfigs = [quantization_target_points[qtp] for qtp in unified_scale_group] + if any(qconfigs[0] != qconfig for qconfig in qconfigs[1:]): + raise nncf.InternalError(f"QConfigs for unified scale group {unified_scale_group} are not equal") + qconfig = qconfigs[0] + q_group = QuantizerGroup.ACTIVATIONS + narrow_range = get_quantizer_narrow_range(qconfig, q_group) + if self._mode is not None: + destination_type = self._quantization_params[q_group].destination_type + parameters = calculate_convert_parameters( + unified_values, is_per_channel=qconfig.per_channel, destination_type=destination_type + ) + for quantization_target_point in unified_scale_group: + transformation_layout.register( + self._backend_entity.create_convert_insertion_command(quantization_target_point, parameters) ) + continue + parameters = calculate_quantizer_parameters(unified_values, qconfig, q_group, narrow_range) + commands = self._backend_entity.create_unified_scales_quantizers_insertion_commands( + graph, unified_scale_group, qconfig, parameters + ) + for command in commands: transformation_layout.register(command) - unified_ops_list.add(quantization_target_point) + unified_ops_list.update(unified_scale_group) for quantization_target_point, qconfig in quantization_target_points.items(): if quantization_target_point in unified_ops_list: diff --git a/nncf/quantization/algorithms/min_max/backend.py b/nncf/quantization/algorithms/min_max/backend.py index d521f233243..2f2e7b7361d 100644 --- a/nncf/quantization/algorithms/min_max/backend.py +++ b/nncf/quantization/algorithms/min_max/backend.py @@ -141,12 +141,31 @@ def create_quantizer_insertion_command( Returns backend-specific quantizer insertion command. :param nncf_graph: NNCFGraph to get input/output shapes for the target point. - :param target_point: Target location for the correction. + :param target_point: Target location for the quantizer insertion. :param quantizer_config: QuantizerConfig instance for the current layer. :param parameters: FakeQuantizeParameters to calculate activation quantization parameters. :return: Backend-specific TransformationCommand for the quantizer insertion operation. """ + @staticmethod + @abstractmethod + def create_unified_scales_quantizers_insertion_commands( + nncf_graph: NNCFGraph, + target_points: List[TargetPoint], + quantizer_config: QuantizerConfig, + parameters: FakeQuantizeParameters, + ) -> List[TransformationCommand]: + """ + Returns backend-specific unified scales quantizers insertion commands. + + :param nncf_graph: NNCFGraph to get input/output shapes for the target point. + :param target_points: List of target locations for the quantizers insertion. + :param quantizer_config: QuantizerConfig instance for the current layer. + :param parameters: FakeQuantizeParameters to calculate activation quantization parameters. + :return: List of backend-specific TransformationCommands + for the quantizers with unified scales insertion operations. + """ + @staticmethod @abstractmethod def create_convert_insertion_command( diff --git a/nncf/quantization/algorithms/min_max/onnx_backend.py b/nncf/quantization/algorithms/min_max/onnx_backend.py index 16295518de1..f58299a5d10 100644 --- a/nncf/quantization/algorithms/min_max/onnx_backend.py +++ b/nncf/quantization/algorithms/min_max/onnx_backend.py @@ -118,7 +118,7 @@ def create_quantizer_insertion_command( target_point: ONNXTargetPoint, quantizer_config: QuantizerConfig, parameters: FakeQuantizeParameters, - ): + ) -> ONNXQuantizerInsertionCommand: tensor_type = np.int8 if np.any(parameters.input_low.data < 0) else np.uint8 is_weight = target_point.is_weight_target_point() if is_weight: @@ -131,6 +131,20 @@ def create_quantizer_insertion_command( onnx_parameters = convert_fq_params_to_onnx_params(parameters, quantizer_config.num_bits, tensor_type, axis) return ONNXQuantizerInsertionCommand(target_point, nncf_input_node_next_nodes, onnx_parameters) + @staticmethod + def create_unified_scales_quantizers_insertion_commands( + nncf_graph: NNCFGraph, + target_points: List[ONNXTargetPoint], + quantizer_config: QuantizerConfig, + parameters: FakeQuantizeParameters, + ) -> List[ONNXQuantizerInsertionCommand]: + return [ + ONNXMinMaxAlgoBackend.create_quantizer_insertion_command( + nncf_graph, target_point, quantizer_config, parameters + ) + for target_point in target_points + ] + @staticmethod def create_convert_insertion_command( target_point: ONNXTargetPoint, diff --git a/nncf/quantization/algorithms/min_max/openvino_backend.py b/nncf/quantization/algorithms/min_max/openvino_backend.py index d621993c3ae..417f9c7cbec 100644 --- a/nncf/quantization/algorithms/min_max/openvino_backend.py +++ b/nncf/quantization/algorithms/min_max/openvino_backend.py @@ -120,6 +120,15 @@ def create_quantizer_insertion_command( ) -> OVQuantizerInsertionCommand: return OVQuantizerInsertionCommand(target_point, parameters) + @staticmethod + def create_unified_scales_quantizers_insertion_commands( + nncf_graph: NNCFGraph, + target_points: List[OVTargetPoint], + quantizer_config: QuantizerConfig, + parameters: FakeQuantizeParameters, + ) -> List[OVQuantizerInsertionCommand]: + return [OVQuantizerInsertionCommand(target_point, parameters) for target_point in target_points] + @staticmethod def create_convert_insertion_command( target_point: OVTargetPoint, diff --git a/nncf/quantization/algorithms/min_max/torch_backend.py b/nncf/quantization/algorithms/min_max/torch_backend.py index d5a37ddcbe5..541792eca78 100644 --- a/nncf/quantization/algorithms/min_max/torch_backend.py +++ b/nncf/quantization/algorithms/min_max/torch_backend.py @@ -37,6 +37,7 @@ from nncf.torch.graph.graph import PTNNCFGraph from nncf.torch.graph.graph import PTTargetPoint from nncf.torch.graph.transformations.command_creation import create_quantizer_insertion_command +from nncf.torch.graph.transformations.command_creation import create_shared_quantizer_insertion_command from nncf.torch.graph.transformations.commands import PTInsertionCommand from nncf.torch.graph.transformations.commands import PTSharedFnInsertionCommand from nncf.torch.hardware.config import PTHWConfig @@ -296,6 +297,22 @@ def create_quantizer_insertion_command( ) return create_quantizer_insertion_command(target_point, quantizer) + @staticmethod + def create_unified_scales_quantizers_insertion_commands( + nncf_graph: NNCFGraph, + target_points: List[PTTargetPoint], + quantizer_config: QuantizerConfig, + parameters: FakeQuantizeParameters, + ) -> List[PTSharedFnInsertionCommand]: + _, scale_shape, _ = PTMinMaxAlgoBackend._get_input_scale_shape( + nncf_graph, target_points[0], quantizer_config.per_channel + ) + + quantizer = PTMinMaxAlgoBackend._create_quantizer( + quantizer_config, scale_shape, parameters, target_points[0].target_type + ) + return [create_shared_quantizer_insertion_command(target_points, quantizer)] + @staticmethod def get_ignored_metatypes(model_type: ModelType, device: TargetDevice) -> List[OperatorMetatype]: types = [] diff --git a/nncf/tensorflow/graph/converter.py b/nncf/tensorflow/graph/converter.py index 83c5d9f25cd..d7eb09a2a21 100644 --- a/nncf/tensorflow/graph/converter.py +++ b/nncf/tensorflow/graph/converter.py @@ -553,8 +553,7 @@ def _collect_edge_information(self): node_name = layer_name input_shapes = self._node_info[node_name]["input_shapes"] - layer_instance_input_port_id = 0 - for inbound_node in inbound_nodes: + for layer_instance_input_port_id, inbound_node in enumerate(inbound_nodes): producer_layer_name, producer_layer_instance, producer_layer_instance_output_port, _ = inbound_node if self._is_layer_shared(producer_layer_name): @@ -573,7 +572,6 @@ def _collect_edge_information(self): "to_node_input_port_id": layer_instance_input_port_id, "from_node_output_port_id": producer_layer_instance_output_port, } - layer_instance_input_port_id += 1 def convert(self) -> NNCFGraph: nncf_graph = NNCFGraph() diff --git a/nncf/tensorflow/graph/metatypes/keras_layers.py b/nncf/tensorflow/graph/metatypes/keras_layers.py index ce65fc63298..8f6636783bb 100644 --- a/nncf/tensorflow/graph/metatypes/keras_layers.py +++ b/nncf/tensorflow/graph/metatypes/keras_layers.py @@ -87,7 +87,7 @@ def get_all_aliases(cls) -> List[str]: return [cls.name] -@KERAS_LAYER_METATYPES.register() +@KERAS_LAYER_METATYPES.register(is_subtype=True) class TFDepthwiseConv1DSubLayerMetatype(TFLayerWithWeightsMetatype): name = "DepthwiseConv1D(Conv1DKerasLayer)" keras_layer_names = ["Conv1D", "Convolution1D"] @@ -112,7 +112,7 @@ class TFConv1DLayerMetatype(TFLayerWithWeightsMetatype): bias_attr_name = "bias" -@KERAS_LAYER_METATYPES.register() +@KERAS_LAYER_METATYPES.register(is_subtype=True) class TFDepthwiseConv2DSubLayerMetatype(TFLayerWithWeightsMetatype): name = "DepthwiseConv2D(Conv2DKerasLayer)" keras_layer_names = ["Conv2D", "Convolution2D"] @@ -137,7 +137,7 @@ class TFConv2DLayerMetatype(TFLayerWithWeightsMetatype): bias_attr_name = "bias" -@KERAS_LAYER_METATYPES.register() +@KERAS_LAYER_METATYPES.register(is_subtype=True) class TFDepthwiseConv3DSubLayerMetatype(TFLayerWithWeightsMetatype): name = "DepthwiseConv3D(Conv3DKerasLayer)" keras_layer_names = ["Conv3D", "Convolution3D"] diff --git a/nncf/torch/graph/operator_metatypes.py b/nncf/torch/graph/operator_metatypes.py index 0f2b9f9fdb1..b99bab5ebab 100644 --- a/nncf/torch/graph/operator_metatypes.py +++ b/nncf/torch/graph/operator_metatypes.py @@ -167,7 +167,7 @@ class PTNoopMetatype(PTOperatorMetatype): } -@PT_OPERATOR_METATYPES.register() +@PT_OPERATOR_METATYPES.register(is_subtype=True) class PTDepthwiseConv1dSubtype(PTDepthwiseConvOperatorSubtype): name = "Conv1DOp" hw_config_name = [HWConfigOpName.DEPTHWISECONVOLUTION] @@ -178,7 +178,7 @@ class PTDepthwiseConv1dSubtype(PTDepthwiseConvOperatorSubtype): bias_port_id = 2 -@PT_OPERATOR_METATYPES.register() +@PT_OPERATOR_METATYPES.register(is_subtype=True) class PTModuleConv1dMetatype(PTModuleOperatorSubtype): name = "Conv1DOp" hw_config_names = [HWConfigOpName.CONVOLUTION] @@ -202,7 +202,7 @@ class PTConv1dMetatype(PTOperatorMetatype): bias_port_id = 2 -@PT_OPERATOR_METATYPES.register() +@PT_OPERATOR_METATYPES.register(is_subtype=True) class PTDepthwiseConv2dSubtype(PTDepthwiseConvOperatorSubtype): name = "Conv2DOp" hw_config_names = [HWConfigOpName.DEPTHWISECONVOLUTION] @@ -213,7 +213,7 @@ class PTDepthwiseConv2dSubtype(PTDepthwiseConvOperatorSubtype): bias_port_id = 2 -@PT_OPERATOR_METATYPES.register() +@PT_OPERATOR_METATYPES.register(is_subtype=True) class PTModuleConv2dMetatype(PTModuleOperatorSubtype): name = "Conv2DOp" hw_config_names = [HWConfigOpName.CONVOLUTION] @@ -237,7 +237,7 @@ class PTConv2dMetatype(PTOperatorMetatype): bias_port_id = 2 -@PT_OPERATOR_METATYPES.register() +@PT_OPERATOR_METATYPES.register(is_subtype=True) class PTDepthwiseConv3dSubtype(PTDepthwiseConvOperatorSubtype): name = "Conv3DOp" hw_config_names = [HWConfigOpName.DEPTHWISECONVOLUTION] @@ -248,7 +248,7 @@ class PTDepthwiseConv3dSubtype(PTDepthwiseConvOperatorSubtype): bias_port_id = 2 -@PT_OPERATOR_METATYPES.register() +@PT_OPERATOR_METATYPES.register(is_subtype=True) class PTModuleConv3dMetatype(PTModuleOperatorSubtype): name = "Conv3DOp" hw_config_names = [HWConfigOpName.CONVOLUTION] @@ -272,7 +272,7 @@ class PTConv3dMetatype(PTOperatorMetatype): bias_port_id = 2 -@PT_OPERATOR_METATYPES.register() +@PT_OPERATOR_METATYPES.register(is_subtype=True) class PTModuleConvTranspose1dMetatype(PTModuleOperatorSubtype): name = "ConvTranspose1DOp" hw_config_names = [HWConfigOpName.CONVOLUTION] @@ -295,7 +295,7 @@ class PTConvTranspose1dMetatype(PTOperatorMetatype): bias_port_id = 2 -@PT_OPERATOR_METATYPES.register() +@PT_OPERATOR_METATYPES.register(is_subtype=True) class PTModuleConvTranspose2dMetatype(PTModuleOperatorSubtype): name = "ConvTranspose2DOp" hw_config_names = [HWConfigOpName.CONVOLUTION] @@ -318,7 +318,7 @@ class PTConvTranspose2dMetatype(PTOperatorMetatype): bias_port_id = 2 -@PT_OPERATOR_METATYPES.register() +@PT_OPERATOR_METATYPES.register(is_subtype=True) class PTModuleConvTranspose3dMetatype(PTModuleOperatorSubtype): name = "ConvTranspose3DOp" hw_config_names = [HWConfigOpName.CONVOLUTION] @@ -341,7 +341,7 @@ class PTConvTranspose3dMetatype(PTOperatorMetatype): bias_port_id = 2 -@PT_OPERATOR_METATYPES.register() +@PT_OPERATOR_METATYPES.register(is_subtype=True) class PTModuleDeformConv2dMetatype(PTModuleOperatorSubtype): name = "DeformConv2dOp" module_to_function_names = {NamespaceTarget.TORCH_NN_FUNCTIONAL: ["deform_conv2d"]} @@ -358,7 +358,7 @@ class PTDeformConv2dMetatype(PTOperatorMetatype): weight_port_ids = [2] -@PT_OPERATOR_METATYPES.register() +@PT_OPERATOR_METATYPES.register(is_subtype=True) class PTModuleLinearMetatype(PTModuleOperatorSubtype): name = "LinearOp" module_to_function_names = {NamespaceTarget.TORCH_NN_FUNCTIONAL: ["linear"]} @@ -428,7 +428,7 @@ class PTLeakyRELUMetatype(PTOperatorMetatype): num_expected_input_edges = 1 -@PT_OPERATOR_METATYPES.register() +@PT_OPERATOR_METATYPES.register(is_subtype=True) class PTModuleLayerNormMetatype(PTModuleOperatorSubtype): name = "LayerNormOp" module_to_function_names = {NamespaceTarget.TORCH_NN_FUNCTIONAL: ["layer_norm"]} @@ -445,7 +445,7 @@ class PTLayerNormMetatype(PTOperatorMetatype): num_expected_input_edges = 1 -@PT_OPERATOR_METATYPES.register() +@PT_OPERATOR_METATYPES.register(is_subtype=True) class PTModuleGroupNormMetatype(PTModuleOperatorSubtype): name = "GroupNormOp" module_to_function_names = {NamespaceTarget.TORCH_NN_FUNCTIONAL: ["group_norm"]} @@ -630,7 +630,7 @@ class PTThresholdMetatype(PTOperatorMetatype): module_to_function_names = {NamespaceTarget.TORCH_NN_FUNCTIONAL: ["threshold"]} -@PT_OPERATOR_METATYPES.register() +@PT_OPERATOR_METATYPES.register(is_subtype=True) class PTModuleBatchNormMetatype(PTModuleOperatorSubtype): name = "BatchNormOp" module_to_function_names = {NamespaceTarget.TORCH_NN_FUNCTIONAL: ["batch_norm"]} @@ -821,7 +821,7 @@ class PTExpandAsMetatype(PTOperatorMetatype): module_to_function_names = {NamespaceTarget.TORCH_TENSOR: ["expand_as"]} -@PT_OPERATOR_METATYPES.register() +@PT_OPERATOR_METATYPES.register(is_subtype=True) class PTModuleEmbeddingMetatype(PTModuleOperatorSubtype): name = "EmbeddingOp" module_to_function_names = {NamespaceTarget.TORCH_NN_FUNCTIONAL: ["embedding"]} @@ -838,7 +838,7 @@ class PTEmbeddingMetatype(PTOperatorMetatype): weight_port_ids = [1] -@PT_OPERATOR_METATYPES.register() +@PT_OPERATOR_METATYPES.register(is_subtype=True) class PTModuleEmbeddingBagMetatype(PTModuleOperatorSubtype): name = "EmbeddingBagOp" module_to_function_names = {NamespaceTarget.TORCH_NN_FUNCTIONAL: ["embedding_bag"]} diff --git a/nncf/torch/graph/transformations/command_creation.py b/nncf/torch/graph/transformations/command_creation.py index 16b14c3e172..6146803ae19 100644 --- a/nncf/torch/graph/transformations/command_creation.py +++ b/nncf/torch/graph/transformations/command_creation.py @@ -9,7 +9,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Union +from typing import List, Union from torch import Tensor @@ -65,3 +65,20 @@ def create_quantizer_insertion_command( compression_module_type=ExtraCompressionModuleType.EXTERNAL_QUANTIZER, priority=TransformationPriority.QUANTIZATION_PRIORITY, ) + + +def create_shared_quantizer_insertion_command( + target_points: List[PTTargetPoint], quantizer: BaseQuantizer +) -> PTSharedFnInsertionCommand: + quantizers_ids = [] + for target_point in target_points: + quantizers_ids.append(NonWeightQuantizerId(target_point.target_node_name, target_point.input_port_id)) + + storage_key = ";".join(str(quantizer_id) for quantizer_id in sorted(quantizers_ids, key=str)) + return PTSharedFnInsertionCommand( + target_points=target_points, + fn=quantizer, + op_unique_name=storage_key, + compression_module_type=ExtraCompressionModuleType.EXTERNAL_QUANTIZER, + priority=TransformationPriority.QUANTIZATION_PRIORITY, + ) diff --git a/nncf/torch/graph/transformations/commands.py b/nncf/torch/graph/transformations/commands.py index 4e27236f5e9..b2461277a5f 100644 --- a/nncf/torch/graph/transformations/commands.py +++ b/nncf/torch/graph/transformations/commands.py @@ -57,6 +57,7 @@ def __eq__(self, other: "PTTargetPoint"): isinstance(other, PTTargetPoint) and self.target_type == other.target_type and self.target_node_name == other.target_node_name + and self.input_port_id == other.input_port_id ) def __str__(self): diff --git a/nncf/torch/nested_objects_traversal.py b/nncf/torch/nested_objects_traversal.py index f8b7f942e7d..1507b3ade12 100644 --- a/nncf/torch/nested_objects_traversal.py +++ b/nncf/torch/nested_objects_traversal.py @@ -28,7 +28,7 @@ def is_tuple(obj) -> bool: def is_named_tuple(obj) -> bool: - return is_tuple(obj) and (obj.__class__ != tuple) + return is_tuple(obj) and (obj.__class__ is not tuple) def maybe_get_iterator(obj): diff --git a/nncf/torch/pruning/filter_pruning/global_ranking/evolutionary_optimization.py b/nncf/torch/pruning/filter_pruning/global_ranking/evolutionary_optimization.py index def4266cbd9..9d9d69a8de4 100644 --- a/nncf/torch/pruning/filter_pruning/global_ranking/evolutionary_optimization.py +++ b/nncf/torch/pruning/filter_pruning/global_ranking/evolutionary_optimization.py @@ -20,6 +20,11 @@ from torch import optim from nncf.config.config import NNCFConfig +from nncf.config.schemata.defaults import PRUNING_LEGR_GENERATIONS +from nncf.config.schemata.defaults import PRUNING_LEGR_MUTATE_PERCENT +from nncf.config.schemata.defaults import PRUNING_LEGR_NUM_SAMPLES +from nncf.config.schemata.defaults import PRUNING_LEGR_POPULATION_SIZE +from nncf.config.schemata.defaults import PRUNING_LEGR_SIGMA_SCALE from nncf.torch.utils import get_filters_num @@ -48,11 +53,11 @@ def __init__(self, initial_filter_norms: Dict, hparams: Dict, random_seed: int): """ self.random_seed = random_seed # Optimizer hyper-params - self.population_size = hparams.get("population_size", 64) - self.num_generations = hparams.get("num_generations", 400) - self.num_samples = hparams.get("num_samples", 16) - self.mutate_percent = hparams.get("mutate_percent", 0.1) - self.scale_sigma = hparams.get("sigma_scale", 1) + self.population_size = hparams.get("population_size", PRUNING_LEGR_POPULATION_SIZE) + self.num_generations = hparams.get("num_generations", PRUNING_LEGR_GENERATIONS) + self.num_samples = hparams.get("num_samples", PRUNING_LEGR_NUM_SAMPLES) + self.mutate_percent = hparams.get("mutate_percent", PRUNING_LEGR_MUTATE_PERCENT) + self.scale_sigma = hparams.get("sigma_scale", PRUNING_LEGR_SIGMA_SCALE) self.max_reward = -np.inf self.mean_rewards = [] diff --git a/nncf/torch/pruning/filter_pruning/global_ranking/legr.py b/nncf/torch/pruning/filter_pruning/global_ranking/legr.py index 2949ded0469..d307151eec2 100644 --- a/nncf/torch/pruning/filter_pruning/global_ranking/legr.py +++ b/nncf/torch/pruning/filter_pruning/global_ranking/legr.py @@ -15,7 +15,11 @@ from nncf.common.logging import nncf_logger from nncf.config.schemata.defaults import PRUNING_LEGR_GENERATIONS from nncf.config.schemata.defaults import PRUNING_LEGR_MAX_PRUNING +from nncf.config.schemata.defaults import PRUNING_LEGR_MUTATE_PERCENT +from nncf.config.schemata.defaults import PRUNING_LEGR_NUM_SAMPLES +from nncf.config.schemata.defaults import PRUNING_LEGR_POPULATION_SIZE from nncf.config.schemata.defaults import PRUNING_LEGR_RANDOM_SEED +from nncf.config.schemata.defaults import PRUNING_LEGR_SIGMA_SCALE from nncf.config.schemata.defaults import PRUNING_LEGR_TRAIN_STEPS from nncf.torch.pruning.filter_pruning.global_ranking.evolutionary_optimization import EvolutionOptimizer from nncf.torch.pruning.filter_pruning.global_ranking.evolutionary_optimization import LeGREvolutionEnv @@ -38,6 +42,10 @@ def __init__( generations: int = PRUNING_LEGR_GENERATIONS, max_pruning: float = PRUNING_LEGR_MAX_PRUNING, random_seed: int = PRUNING_LEGR_RANDOM_SEED, + population_size: int = PRUNING_LEGR_POPULATION_SIZE, + num_samples: int = PRUNING_LEGR_NUM_SAMPLES, + mutate_percent: float = PRUNING_LEGR_MUTATE_PERCENT, + scale_sigma: float = PRUNING_LEGR_SIGMA_SCALE, ): """ Initializing all necessary structures for optimization- LeGREvolutionEnv environment and EvolutionOptimizer @@ -53,10 +61,20 @@ def __init__( self.num_generations = generations self.max_pruning = max_pruning self.train_steps = train_steps + self.population_size = population_size + self.num_samples = num_samples + self.mutate_percent = mutate_percent + self.scale_sigma = scale_sigma self.pruner = LeGRPruner(pruning_ctrl, target_model) init_filter_norms = self.pruner.init_filter_norms - agent_hparams = {"num_generations": self.num_generations} + agent_hparams = { + "num_generations": self.num_generations, + "population_size": self.population_size, + "num_samples": self.num_samples, + "mutate_percent": self.mutate_percent, + "sigma_scale": self.scale_sigma, + } self.agent = EvolutionOptimizer(init_filter_norms, agent_hparams, random_seed) self.env = LeGREvolutionEnv( self.pruner, diff --git a/nncf/torch/quantization/algo.py b/nncf/torch/quantization/algo.py index 24ef0576e9b..1cbeeac4f69 100644 --- a/nncf/torch/quantization/algo.py +++ b/nncf/torch/quantization/algo.py @@ -534,7 +534,7 @@ def _parse_range_init_params(self) -> Optional[PTRangeInitParams]: return PTRangeInitParams(**range_init_params) if range_init_params is not None else None def _parse_precision_init_params(self, initializer_config: Dict) -> Tuple[str, BasePrecisionInitParams]: - init_precision_config = initializer_config.get("precision", None) + init_precision_config = initializer_config.get("precision") if not init_precision_config: return None, None precision_init_type = init_precision_config.get("type", "manual") @@ -934,7 +934,7 @@ def _build_insertion_commands_list_for_quantizer_setup( range_init_minmax_values = None if minmax_values_for_range_init: - minmax_stat = minmax_values_for_range_init[qp_id] if qp_id in minmax_values_for_range_init else None + minmax_stat = minmax_values_for_range_init.get(qp_id) if minmax_stat is not None: range_init_minmax_values = (minmax_stat.min_values, minmax_stat.max_values) @@ -1084,7 +1084,7 @@ def ip_str_repr_key_lambda(x): min_values = None max_values = None for qp_id in sorted_qp_ids: - minmax_stat = minmax_values_for_range_init[qp_id] if qp_id in minmax_values_for_range_init else None + minmax_stat = minmax_values_for_range_init.get(qp_id) if minmax_stat is None: continue diff --git a/nncf/torch/quantization/precision_init/hawq_init.py b/nncf/torch/quantization/precision_init/hawq_init.py index 743e458413a..a60b15ad563 100644 --- a/nncf/torch/quantization/precision_init/hawq_init.py +++ b/nncf/torch/quantization/precision_init/hawq_init.py @@ -95,7 +95,7 @@ def from_config( return cls( user_init_args=user_init_args, bitwidths=hawq_init_config_dict.get("bits", PRECISION_INIT_BITWIDTHS), - traces_per_layer_path=hawq_init_config_dict.get("traces_per_layer_path", None), + traces_per_layer_path=hawq_init_config_dict.get("traces_per_layer_path"), num_data_points=hawq_init_config_dict.get("num_data_points", HAWQ_NUM_DATA_POINTS), iter_number=hawq_init_config_dict.get("iter_number", HAWQ_ITER_NUMBER), tolerance=hawq_init_config_dict.get("tolerance", HAWQ_TOLERANCE), diff --git a/ruff.toml b/ruff.toml index 53940dc8dcb..cf3a51e0c36 100644 --- a/ruff.toml +++ b/ruff.toml @@ -1,10 +1,19 @@ line-length = 120 +exclude = ["nncf/tensorflow/__init__.py"] + +[lint] +preview = true ignore-init-module-imports = true ignore = [ + "E201", # whitespace-after-open-bracket + "E203", # whitespace-before-punctuation + "E231", # missing-whitespace + "E251", # unexpected-spaces-around-keyword-parameter-equals "E731", # lambda-assignment "SIM108", # if-else-block-instead-of-if-exp "SIM110", # reimplemented-builtin "SIM117", # multiple-with-statements + "SIM103", # needless-bool ] select = [ "E", # pycodestyle rules @@ -14,9 +23,8 @@ select = [ extend-select = [ "SIM", # https://pypi.org/project/flake8-simplify ] -exclude = ["nncf/tensorflow/__init__.py"] -[per-file-ignores] +[lint.per-file-ignores] "nncf/experimental/torch/nas/bootstrapNAS/__init__.py" = ["F401"] "nncf/torch/__init__.py" = ["F401", "E402"] "tests/**/*.py" = ["F403"] @@ -24,7 +32,7 @@ exclude = ["nncf/tensorflow/__init__.py"] "examples/**/*.py" = ["F403"] -[flake8-copyright] +[lint.flake8-copyright] notice-rgx = """\ # Copyright \\(c\\) (\\d{4}|\\d{4}-\\d{4}) Intel Corporation # Licensed under the Apache License, Version 2.0 \\(the "License"\\); diff --git a/tests/onnx/benchmarking/ac_wrapper.py b/tests/onnx/benchmarking/ac_wrapper.py index 160fa0a287d..9ae3d639859 100644 --- a/tests/onnx/benchmarking/ac_wrapper.py +++ b/tests/onnx/benchmarking/ac_wrapper.py @@ -35,7 +35,7 @@ def _read_image_annotation(image, annotations, label_id_to_label): @staticmethod def convert_to_voc(image_labels): - return [COCO_TO_VOC[label] if label in COCO_TO_VOC else 0 for label in image_labels] + return [COCO_TO_VOC.get(label, 0) for label in image_labels] if __name__ == "__main__": diff --git a/tests/onnx/quantization/test_ptq_params.py b/tests/onnx/quantization/test_ptq_params.py index daadc8a8337..f6c6c041459 100644 --- a/tests/onnx/quantization/test_ptq_params.py +++ b/tests/onnx/quantization/test_ptq_params.py @@ -9,22 +9,28 @@ # See the License for the specific language governing permissions and # limitations under the License. +import numpy as np import pytest +from nncf.common.graph.graph import NNCFGraph from nncf.common.graph.patterns import GraphPattern from nncf.common.graph.patterns.manager import PatternsManager from nncf.common.graph.transformations.commands import TargetType +from nncf.common.graph.transformations.commands import TransformationType from nncf.common.utils.backend import BackendType +from nncf.onnx.graph.metatypes.onnx_metatypes import ONNXConcatMetatype from nncf.onnx.graph.metatypes.onnx_metatypes import ONNXConvolutionMetatype from nncf.onnx.graph.metatypes.onnx_metatypes import ONNXGemmMetatype from nncf.onnx.graph.metatypes.onnx_metatypes import ONNXSoftmaxMetatype from nncf.onnx.graph.nncf_graph_builder import GraphConverter from nncf.onnx.graph.nncf_graph_builder import ONNXLayerAttributes +from nncf.onnx.graph.transformations.commands import ONNXQuantizerInsertionCommand from nncf.onnx.graph.transformations.commands import ONNXTargetPoint from nncf.parameters import TargetDevice from nncf.quantization.algorithms.min_max.algorithm import MinMaxQuantization from nncf.quantization.algorithms.min_max.onnx_backend import ONNXMinMaxAlgoBackend from nncf.scopes import IgnoredScope +from tests.common.quantization.metatypes import CatTestMetatype from tests.common.quantization.metatypes import Conv2dTestMetatype from tests.common.quantization.metatypes import LinearTestMetatype from tests.common.quantization.metatypes import SoftmaxTestMetatype @@ -61,17 +67,34 @@ def check_quantize_outputs_fq_num(self, quantize_outputs, act_num_q, weight_num_ assert act_num_q == 1 assert weight_num_q == 1 + def check_unified_scale_layout(self, layout, unified_scale_group): + assert len(layout.transformations) == len(unified_scale_group) + for t, ref_tp in zip(layout.transformations, unified_scale_group): + assert isinstance(t, ONNXQuantizerInsertionCommand) + assert t.target_point == ref_tp + assert t.type == TransformationType.INSERT + assert t.quantizer_parameters.zero_point == 0 + assert np.isclose(t.quantizer_parameters.scale, 0.03149606) + def target_point(self, target_type: TargetType, target_node_name: str, port_id: int) -> ONNXTargetPoint: return ONNXTargetPoint(target_type, target_node_name, port_id) + def get_backend_tensor(self, value): + return np.array(value) + @property def metatypes_mapping(self): return { Conv2dTestMetatype: ONNXConvolutionMetatype, LinearTestMetatype: ONNXGemmMetatype, SoftmaxTestMetatype: ONNXSoftmaxMetatype, + CatTestMetatype: ONNXConcatMetatype, } + @property + def nncf_graph_cls(self): + return NNCFGraph + @pytest.fixture(scope="session") def test_params(self): linear_model = LinearModel().onnx_model diff --git a/tests/openvino/native/data/2024.1/reference_scales/yolo-v3-tiny-onnx_mixed.json b/tests/openvino/native/data/2024.1/reference_scales/yolo-v3-tiny-onnx_mixed.json index c1114f17dce..b9d7b247efd 100644 --- a/tests/openvino/native/data/2024.1/reference_scales/yolo-v3-tiny-onnx_mixed.json +++ b/tests/openvino/native/data/2024.1/reference_scales/yolo-v3-tiny-onnx_mixed.json @@ -7155,7 +7155,7 @@ "output_low": -0.3303394019603729, "output_high": 3.881488084793091 }, - "Multiply_6911/fq_weights_1": { + "Multiply_6953/fq_weights_1": { "input_low": [ [ [ @@ -14339,7 +14339,7 @@ "output_low": -1.0861154794692993, "output_high": 7.848060131072998 }, - "Multiply_6869/fq_weights_1": { + "Multiply_6911/fq_weights_1": { "input_low": [ [ [ @@ -21523,7 +21523,7 @@ "output_low": -1.0673936605453491, "output_high": 13.258152961730957 }, - "Multiply_6862/fq_weights_1": { + "Multiply_6904/fq_weights_1": { "input_low": [ [ [ @@ -25123,7 +25123,7 @@ "output_low": -0.6687189340591431, "output_high": 11.511518478393555 }, - "Multiply_6855/fq_weights_1": { + "Multiply_6897/fq_weights_1": { "input_low": [ [ [ @@ -26931,7 +26931,7 @@ "output_low": -0.5482831001281738, "output_high": 6.109440326690674 }, - "Multiply_6848/fq_weights_1": { + "Multiply_6890/fq_weights_1": { "input_low": [ [ [ @@ -27843,7 +27843,7 @@ "output_low": -1.5592432022094727, "output_high": 3.473757028579712 }, - "Multiply_6841/fq_weights_1": { + "Multiply_6883/fq_weights_1": { "input_low": [ [ [ @@ -28319,7 +28319,7 @@ "output_low": -0.2852116525173187, "output_high": 3.020650625228882 }, - "Multiply_6904/fq_weights_1": { + "Multiply_6946/fq_weights_1": { "input_low": [ [ [ @@ -31919,7 +31919,7 @@ "output_low": -0.21894927322864532, "output_high": 1.706294298171997 }, - "Multiply_6890/fq_weights_1": { + "Multiply_6932/fq_weights_1": { "input_low": [ [ [ @@ -39103,7 +39103,7 @@ "output_low": -1.4062703847885132, "output_high": 7.131800174713135 }, - "Multiply_6883/fq_weights_1": { + "Multiply_6925/fq_weights_1": { "input_low": [ [ [ @@ -67791,7 +67791,7 @@ "output_low": -0.7759751081466675, "output_high": 3.3463926315307617 }, - "Multiply_6876/fq_weights_1": { + "Multiply_6918/fq_weights_1": { "input_low": [ [ [ @@ -89293,7 +89293,7 @@ "output_low": -0.3361658453941345, "output_high": 3.5603017807006836 }, - "Multiply_6897/fq_weights_1": { + "Multiply_6939/fq_weights_1": { "input_low": [ [ [ diff --git a/tests/openvino/native/data/2024.1/reference_scales/yolo-v3-tiny-onnx_performance.json b/tests/openvino/native/data/2024.1/reference_scales/yolo-v3-tiny-onnx_performance.json index 13034fcfcbf..d291895eeec 100644 --- a/tests/openvino/native/data/2024.1/reference_scales/yolo-v3-tiny-onnx_performance.json +++ b/tests/openvino/native/data/2024.1/reference_scales/yolo-v3-tiny-onnx_performance.json @@ -7155,7 +7155,7 @@ "output_low": -3.859119176864624, "output_high": 3.828969717025757 }, - "Multiply_6911/fq_weights_1": { + "Multiply_6953/fq_weights_1": { "input_low": [ [ [ @@ -14339,7 +14339,7 @@ "output_low": -7.785910606384277, "output_high": 7.725083351135254 }, - "Multiply_6869/fq_weights_1": { + "Multiply_6911/fq_weights_1": { "input_low": [ [ [ @@ -21523,7 +21523,7 @@ "output_low": -13.285137176513672, "output_high": 13.181346893310547 }, - "Multiply_6862/fq_weights_1": { + "Multiply_6904/fq_weights_1": { "input_low": [ [ [ @@ -25123,7 +25123,7 @@ "output_low": -11.602160453796387, "output_high": 11.511518478393555 }, - "Multiply_6855/fq_weights_1": { + "Multiply_6897/fq_weights_1": { "input_low": [ [ [ @@ -26931,7 +26931,7 @@ "output_low": -6.0120320320129395, "output_high": 5.965063095092773 }, - "Multiply_6848/fq_weights_1": { + "Multiply_6890/fq_weights_1": { "input_low": [ [ [ @@ -27843,7 +27843,7 @@ "output_low": -3.5011093616485596, "output_high": 3.473757028579712 }, - "Multiply_6841/fq_weights_1": { + "Multiply_6883/fq_weights_1": { "input_low": [ [ [ @@ -28319,7 +28319,7 @@ "output_low": -3.0029969215393066, "output_high": 2.9795360565185547 }, - "Multiply_6904/fq_weights_1": { + "Multiply_6946/fq_weights_1": { "input_low": [ [ [ @@ -31919,7 +31919,7 @@ "output_low": -1.6986862421035767, "output_high": 1.685415267944336 }, - "Multiply_6890/fq_weights_1": { + "Multiply_6932/fq_weights_1": { "input_low": [ [ [ @@ -39103,7 +39103,7 @@ "output_low": -7.094355583190918, "output_high": 7.038930892944336 }, - "Multiply_6883/fq_weights_1": { + "Multiply_6925/fq_weights_1": { "input_low": [ [ [ @@ -67791,7 +67791,7 @@ "output_low": -3.372742176055908, "output_high": 3.3463926315307617 }, - "Multiply_6876/fq_weights_1": { + "Multiply_6918/fq_weights_1": { "input_low": [ [ [ @@ -89293,7 +89293,7 @@ "output_low": -3.5883357524871826, "output_high": 3.5603017807006836 }, - "Multiply_6897/fq_weights_1": { + "Multiply_6939/fq_weights_1": { "input_low": [ [ [ diff --git a/tests/openvino/native/quantization/test_ptq_params.py b/tests/openvino/native/quantization/test_ptq_params.py index 71fda6d1a7c..73b73e511ea 100644 --- a/tests/openvino/native/quantization/test_ptq_params.py +++ b/tests/openvino/native/quantization/test_ptq_params.py @@ -9,22 +9,28 @@ # See the License for the specific language governing permissions and # limitations under the License. +import numpy as np import pytest +from nncf.common.graph.graph import NNCFGraph from nncf.common.graph.patterns import GraphPattern from nncf.common.graph.patterns.manager import PatternsManager from nncf.common.graph.transformations.commands import TargetType +from nncf.common.graph.transformations.commands import TransformationType from nncf.common.hardware.config import HW_CONFIG_TYPE_TARGET_DEVICE_MAP from nncf.common.utils.backend import BackendType +from nncf.openvino.graph.metatypes.openvino_metatypes import OVConcatMetatype from nncf.openvino.graph.metatypes.openvino_metatypes import OVConvolutionMetatype from nncf.openvino.graph.metatypes.openvino_metatypes import OVMatMulMetatype from nncf.openvino.graph.metatypes.openvino_metatypes import OVSoftmaxMetatype from nncf.openvino.graph.nncf_graph_builder import GraphConverter +from nncf.openvino.graph.transformations.commands import OVQuantizerInsertionCommand from nncf.openvino.graph.transformations.commands import OVTargetPoint from nncf.parameters import TargetDevice from nncf.quantization.algorithms.min_max.algorithm import MinMaxQuantization from nncf.quantization.algorithms.min_max.openvino_backend import OVMinMaxAlgoBackend from nncf.scopes import IgnoredScope +from tests.common.quantization.metatypes import CatTestMetatype from tests.common.quantization.metatypes import Conv2dTestMetatype from tests.common.quantization.metatypes import LinearTestMetatype from tests.common.quantization.metatypes import SoftmaxTestMetatype @@ -60,17 +66,34 @@ def check_quantize_outputs_fq_num(self, quantize_outputs, act_num_q, weight_num_ assert act_num_q == 1 assert weight_num_q == 1 + def check_unified_scale_layout(self, layout, unified_scale_group): + assert len(layout.transformations) == len(unified_scale_group) + for t, ref_tp in zip(layout.transformations, unified_scale_group): + assert isinstance(t, OVQuantizerInsertionCommand) + assert t.target_point == ref_tp + assert t.type == TransformationType.INSERT + assert np.isclose(t.quantizer_parameters.input_low.data, -4.031496) + assert np.isclose(t.quantizer_parameters.input_high.data, 4) + def target_point(self, target_type: TargetType, target_node_name: str, port_id: int) -> OVTargetPoint: return OVTargetPoint(target_type, target_node_name, port_id) + def get_backend_tensor(self, value): + return np.array(value) + @property def metatypes_mapping(self): return { Conv2dTestMetatype: OVConvolutionMetatype, LinearTestMetatype: OVMatMulMetatype, SoftmaxTestMetatype: OVSoftmaxMetatype, + CatTestMetatype: OVConcatMetatype, } + @property + def nncf_graph_cls(self): + return NNCFGraph + @pytest.fixture(scope="session") def test_params(self): linear_model = LinearModel().ov_model diff --git a/tests/openvino/requirements.txt b/tests/openvino/requirements.txt index 953534cf9be..f0cd9fea03b 100644 --- a/tests/openvino/requirements.txt +++ b/tests/openvino/requirements.txt @@ -2,6 +2,6 @@ pytest==8.0.2 virtualenv pytest-cov pytest-mock>=3.3.1 -openvino-dev[onnx,pytorch,tensorflow2]==2023.3 +openvino-dev[onnx,pytorch,tensorflow2]==2024.0 torch==2.2.1 fastdownload diff --git a/tests/openvino/tools/calibrate.py b/tests/openvino/tools/calibrate.py index 49a601ffc3d..4687c86ee03 100644 --- a/tests/openvino/tools/calibrate.py +++ b/tests/openvino/tools/calibrate.py @@ -1086,7 +1086,7 @@ def main(): "quantize_with_accuracy_control": quantize_model_with_accuracy_control, } for algo_name, algo_config in nncf_algorithms_config.items(): - algo_fn = algo_name_to_method_map.get(algo_name, None) + algo_fn = algo_name_to_method_map.get(algo_name) if algo_fn: quantize_model_arguments = { "xml_path": xml_path, diff --git a/tests/openvino/tools/config.py b/tests/openvino/tools/config.py index e05de9620e7..723dbb5f72a 100644 --- a/tests/openvino/tools/config.py +++ b/tests/openvino/tools/config.py @@ -283,7 +283,7 @@ def _configure_ac_params(self): ac_conf = ConfigReader.convert_paths(ac_conf) ConfigReader._filter_launchers(ac_conf, filtering_params, mode=mode) for req_num in ["stat_requests_number", "eval_requests_number"]: - ac_conf[req_num] = self.engine[req_num] if req_num in self.engine else None + ac_conf[req_num] = self.engine.get(req_num, None) self["engine"] = ac_conf diff --git a/tests/post_training/data/wc_reference_data.yaml b/tests/post_training/data/wc_reference_data.yaml index cd27628038a..5235d155244 100644 --- a/tests/post_training/data/wc_reference_data.yaml +++ b/tests/post_training/data/wc_reference_data.yaml @@ -1,8 +1,16 @@ tinyllama_data_free_backend_OV: metric_value: 0.72057 + num_int4: 228 + num_int8: 84 tinyllama_data_aware_backend_OV: metric_value: 0.83084 + num_int4: 184 + num_int8: 128 tinyllama_data_aware_awq_backend_OV: metric_value: 0.81237 + num_int4: 184 + num_int8: 128 tinyllama_data_aware_awq_stateful_backend_OV: - metric_value: 0.81237 \ No newline at end of file + metric_value: 0.81237 + num_int4: 184 + num_int8: 128 \ No newline at end of file diff --git a/tests/post_training/pipelines/base.py b/tests/post_training/pipelines/base.py index 96b84e1cbd8..353e53930d4 100644 --- a/tests/post_training/pipelines/base.py +++ b/tests/post_training/pipelines/base.py @@ -71,6 +71,13 @@ def fill(self, stdout: str) -> None: """ +@dataclass +class NumCompressNodes: + num_fq_nodes: Optional[int] = None + num_int8: Optional[int] = None + num_int4: Optional[int] = None + + @dataclass class PTQTimeStats(StatsFromOutput): """ @@ -130,12 +137,12 @@ class RunInfo: metric_name: Optional[str] = None metric_value: Optional[float] = None metric_diff: Optional[float] = None - num_fq_nodes: Optional[float] = None compression_memory_usage: Optional[int] = None status: Optional[str] = None fps: Optional[float] = None time_total: Optional[float] = None time_compression: Optional[float] = None + num_compress_nodes: Optional[NumCompressNodes] = None stats_from_output = StatsFromOutput() @staticmethod @@ -157,7 +164,9 @@ def get_result_dict(self): "Metric name": self.metric_name, "Metric value": self.metric_value, "Metric diff": self.metric_diff, - "Num FQ": self.num_fq_nodes, + "Num FQ": self.num_compress_nodes.num_fq_nodes, + "Num int4": self.num_compress_nodes.num_int4, + "Num int8": self.num_compress_nodes.num_int8, "RAM MiB": self.format_memory_usage(self.compression_memory_usage), "Compr. time": self.format_time(self.time_compression), **self.stats_from_output.get_stats(), @@ -210,7 +219,7 @@ def __init__( self.dummy_tensor = None self.input_size = None - self.run_info = RunInfo(model=reported_name, backend=self.backend) + self.run_info = RunInfo(model=reported_name, backend=self.backend, num_compress_nodes=NumCompressNodes()) @abstractmethod def prepare_preprocessor(self) -> None: @@ -383,7 +392,7 @@ def get_num_compressed(self) -> None: if node_type == "FakeQuantize": num_fq += 1 - self.run_info.num_fq_nodes = num_fq + self.run_info.num_compress_nodes.num_fq_nodes = num_fq def run_bench(self) -> None: """ diff --git a/tests/post_training/pipelines/lm_weight_compression.py b/tests/post_training/pipelines/lm_weight_compression.py index b1a6e5853dc..84829e63288 100644 --- a/tests/post_training/pipelines/lm_weight_compression.py +++ b/tests/post_training/pipelines/lm_weight_compression.py @@ -161,7 +161,21 @@ def save_compressed_model(self) -> None: self.model_hf._save_config(self.output_model_dir) def get_num_compressed(self) -> None: - pass + """ + Get number of the i8, u8, i4, u4 ops in the compressed IR. + """ + num_int8 = 0 + num_int4 = 0 + + for node in self.model.get_ops(): + for i in range(node.get_output_size()): + if node.get_output_element_type(i).get_type_name() in ["i8", "u8"]: + num_int8 += 1 + if node.get_output_element_type(i).get_type_name() in ["i4", "u4"]: + num_int4 += 1 + + self.run_info.num_compress_nodes.num_int8 = num_int8 + self.run_info.num_compress_nodes.num_int4 = num_int4 def run_bench(self) -> None: pass @@ -219,3 +233,19 @@ def _validate(self): similarity = all_metrics["similarity"][0] self.run_info.metric_name = "Similarity" self.run_info.metric_value = round(similarity, 5) + + num_int4_reference = self.reference_data.get("num_int4") + num_int8_reference = self.reference_data.get("num_int8") + + num_int4_value = self.run_info.num_compress_nodes.num_int4 + num_int8_value = self.run_info.num_compress_nodes.num_int8 + + if num_int4_reference != num_int4_value: + status_msg = f"Regression: The number of int4 ops is different \ + than reference {num_int4_reference} != {num_int4_value}" + raise ValueError(status_msg) + + if num_int8_reference != num_int8_value: + status_msg = f"Regression: The number of int8 ops is different \ + than reference {num_int8_reference} != {num_int8_value}" + raise ValueError(status_msg) diff --git a/tests/post_training/requirements.txt b/tests/post_training/requirements.txt index 3b223aff441..24246bfb2ca 100644 --- a/tests/post_training/requirements.txt +++ b/tests/post_training/requirements.txt @@ -2,7 +2,7 @@ torch==2.2.1 --extra-index-url https://download.pytorch.org/whl/cpu torchvision==0.17.1 -transformers==4.36.0 +transformers==4.38.2 onnx==1.16.0 onnxruntime==1.17.1 pytest==8.0.2 diff --git a/tests/post_training/test_templates/test_ptq_params.py b/tests/post_training/test_templates/test_ptq_params.py index c7a0e45799c..f56db68f938 100644 --- a/tests/post_training/test_templates/test_ptq_params.py +++ b/tests/post_training/test_templates/test_ptq_params.py @@ -16,6 +16,7 @@ import pytest import nncf +from nncf.common.graph.graph import NNCFGraph from nncf.common.graph.operator_metatypes import InputNoopMetatype from nncf.common.graph.operator_metatypes import OperatorMetatype from nncf.common.graph.operator_metatypes import OutputNoopMetatype @@ -37,6 +38,7 @@ from nncf.quantization.passes import transform_to_inference_graph from nncf.quantization.range_estimator import RangeEstimatorParametersSet from nncf.scopes import IgnoredScope +from tests.common.quantization.metatypes import CatTestMetatype from tests.common.quantization.metatypes import Conv2dTestMetatype from tests.common.quantization.metatypes import IdentityTestMetatype from tests.common.quantization.metatypes import LinearTestMetatype @@ -91,6 +93,50 @@ def __init__(self, metatypes: Dict[TestMetatype, OperatorMetatype]): self.weight_quantization_target_point_names.append(node.node_name) +class ModelWithUnifiedScales: + # Input_1 + # / | \ + # Conv_1 Conv_2 Conv_3 + # \ | / + # Cat_1 + # | + # Output_1 + + def __init__(self, metatypes: Dict[TestMetatype, OperatorMetatype], nncf_graph_cls=NNCFGraph): + nodes = [ + NodeWithType("Input_1", InputNoopMetatype), + NodeWithType("Conv_1", metatypes[Conv2dTestMetatype]), + NodeWithType("Conv_2", metatypes[Conv2dTestMetatype]), + NodeWithType("Conv_3", metatypes[Conv2dTestMetatype]), + NodeWithType("Cat_1", metatypes[CatTestMetatype]), + NodeWithType("Output_1", OutputNoopMetatype), + ] + node_edges = [ + ("Input_1", "Conv_1"), + ("Input_1", "Conv_2"), + ("Input_1", "Conv_3"), + ("Conv_1", "Cat_1"), + ("Conv_2", "Cat_1"), + ("Conv_3", "Cat_1"), + ("Cat_1", "Output_1"), + ] + original_mock_graph = create_mock_graph(nodes, node_edges) + self.nncf_graph = get_nncf_graph_from_mock_nx_graph(original_mock_graph, nncf_graph_cls=nncf_graph_cls) + + +class DummyMinMaxTensorStatistic(MinMaxTensorStatistic): + def tensor_eq(self): + return True + + +class DummyMinMaxTensorCollector: + def __init__(self, min_val, max_val): + self._stat = DummyMinMaxTensorStatistic(min_values=min_val, max_values=max_val) + + def get_statistics(self): + return self._stat + + class TemplateTestPTQParams: @abstractmethod def get_algo_backend(self): @@ -112,6 +158,13 @@ def check_is_mean_min_max_statistic_collector(self, tensor_collector: TensorColl def check_quantize_outputs_fq_num(self, quantize_outputs, act_num_q, weight_num_q): pass + @abstractmethod + def check_unified_scale_layout(self, layout, unified_scales_group): + """ + Checks that given transfromation layout and unified_scales_group target points + are correspond to each other and to the test params + """ + @abstractmethod @pytest.fixture(scope="session") def test_params(self): @@ -131,6 +184,15 @@ def target_point(self, target_type: TargetType, target_node_name: str, port_id: def metatypes_mapping(self): pass + @property + @abstractmethod + def nncf_graph_cls(self): + pass + + @abstractmethod + def get_backend_tensor(self, value): + pass + @pytest.mark.parametrize( "range_estimator_params", [RangeEstimatorParametersSet.MINMAX, RangeEstimatorParametersSet.MEAN_MINMAX, None] ) @@ -282,6 +344,35 @@ def test_quantization_points_overflow_fix(self, overflow_fix, affected_target_po ) assert Counter([t_p.target_node_name for t_p in target_points_overflow_fix]) == Counter(affected_target_points) + def test_unified_scales_command_creation(self, mocker): + model = ModelWithUnifiedScales(self.metatypes_mapping, self.nncf_graph_cls) + algo = MinMaxQuantization() + algo._backend_entity = self.get_algo_backend() + # Imitating solver quantization setup building + q_tp_vs_qcf = {} + unified_scales_group = [] + for i in range(1, 4): + tp = self.target_point(TargetType.POST_LAYER_OPERATION, f"/Conv_{i}_0", port_id=0) + q_tp_vs_qcf[tp] = QuantizerConfig() + unified_scales_group.append(tp) + + algo._quantization_target_points_to_qconfig = q_tp_vs_qcf + algo._unified_scale_groups = [unified_scales_group] + + mock_transformer = mocker.MagicMock() + mocker.patch( + "nncf.quantization.algorithms.min_max.algorithm.ModelTransformerFactory.create", + return_value=mock_transformer, + ) + stats = StatisticPointsContainer() + for idx, tp in enumerate(unified_scales_group): + tc = DummyMinMaxTensorCollector(self.get_backend_tensor(idx - 1), self.get_backend_tensor(idx + 2)) + stats.add_statistic_point(StatisticPoint(tp, tc, algo._algorithm_key)) + algo.apply(model, model.nncf_graph, stats) + mock_transformer.transform.assert_called_once() + layout = mock_transformer.transform.call_args.args[0] + self.check_unified_scale_layout(layout, unified_scales_group) + @pytest.mark.parametrize("validate_scopes", (True, False)) def test_validate_scope(self, test_params, validate_scopes): nncf_graph = test_params["test_model_type_pass"]["nncf_graph"] @@ -308,20 +399,14 @@ def test_empty_statistics(self, mode, mocker): target_point = self.target_point(TargetType.PRE_LAYER_OPERATION, "A", 0) stat_points = StatisticPointsContainer() - class DummyMinMaxTensorStatistic(MinMaxTensorStatistic): - def tensor_eq(self): - return True - - class EmptyTensorCollector: - def get_statistics(self): - return DummyMinMaxTensorStatistic(None, None) - dummy_tp = {target_point: QuantizerConfig()} if mode == "target_point": dummy_tps = (dummy_tp, {}) else: dummy_tps = ({}, ((target_point,),)) - stat_points.add_statistic_point(StatisticPoint(target_point, EmptyTensorCollector(), algo._algorithm_key)) + stat_points.add_statistic_point( + StatisticPoint(target_point, DummyMinMaxTensorCollector(None, None), algo._algorithm_key) + ) mocker.patch("nncf.common.factory.ModelTransformerFactory.create", return_value=mocker.MagicMock()) mocker.patch( "nncf.quantization.algorithms.min_max.algorithm.MinMaxQuantization._get_quantization_target_points", diff --git a/tests/shared/test_templates/template_test_nncf_tensor.py b/tests/shared/test_templates/template_test_nncf_tensor.py index 2005e6c03bc..382ea2cbef9 100644 --- a/tests/shared/test_templates/template_test_nncf_tensor.py +++ b/tests/shared/test_templates/template_test_nncf_tensor.py @@ -354,11 +354,9 @@ def test_getitem(self): def test_iter(self): arr = [0, 1, 2] nncf_tensor = Tensor(self.to_tensor(arr)) - i = 0 - for x in nncf_tensor: + for i, x in enumerate(nncf_tensor): assert x == arr[i] assert isinstance(x, Tensor) - i += 1 # Math diff --git a/tests/torch/data/reference_graphs/quantized/ptq/symmetric/inception_v3.dot b/tests/torch/data/reference_graphs/quantized/ptq/symmetric/inception_v3.dot index 3eca81edfce..1082c9c5847 100644 --- a/tests/torch/data/reference_graphs/quantized/ptq/symmetric/inception_v3.dot +++ b/tests/torch/data/reference_graphs/quantized/ptq/symmetric/inception_v3.dot @@ -6,19 +6,19 @@ strict digraph { "4 Inception3/__mul___0" [id=4, type=__mul__]; "5 Inception3/NNCFNetworkInterface[_nncf]/ModuleDict[external_quantizers]/SymmetricQuantizer[Inception3/__mul___0|OUTPUT]/symmetric_quantize_0" [id=5, type=symmetric_quantize]; "6 Inception3/__add___0" [id=6, type=__add__]; -"7 Inception3/NNCFNetworkInterface[_nncf]/ModuleDict[external_quantizers]/SymmetricQuantizer[Inception3/__add___0|OUTPUT]/symmetric_quantize_0" [id=7, type=symmetric_quantize]; +"7 Inception3/NNCFNetworkInterface[_nncf]/ModuleDict[external_quantizers]/SymmetricQuantizer[Inception3/__add___0|OUTPUT;Inception3/__add___1|OUTPUT;Inception3/__add___2|OUTPUT]/symmetric_quantize_0" [id=7, type=symmetric_quantize]; "8 Inception3/__getitem___1" [id=8, type=__getitem__]; "9 Inception3/unsqueeze_1" [id=9, type=unsqueeze]; "10 Inception3/__mul___1" [id=10, type=__mul__]; "11 Inception3/NNCFNetworkInterface[_nncf]/ModuleDict[external_quantizers]/SymmetricQuantizer[Inception3/__mul___1|OUTPUT]/symmetric_quantize_0" [id=11, type=symmetric_quantize]; "12 Inception3/__add___1" [id=12, type=__add__]; -"13 Inception3/NNCFNetworkInterface[_nncf]/ModuleDict[external_quantizers]/SymmetricQuantizer[Inception3/__add___1|OUTPUT]/symmetric_quantize_0" [id=13, type=symmetric_quantize]; +"13 Inception3/NNCFNetworkInterface[_nncf]/ModuleDict[external_quantizers]/SymmetricQuantizer[Inception3/__add___0|OUTPUT;Inception3/__add___1|OUTPUT;Inception3/__add___2|OUTPUT]/symmetric_quantize_1" [id=13, type=symmetric_quantize]; "14 Inception3/__getitem___2" [id=14, type=__getitem__]; "15 Inception3/unsqueeze_2" [id=15, type=unsqueeze]; "16 Inception3/__mul___2" [id=16, type=__mul__]; "17 Inception3/NNCFNetworkInterface[_nncf]/ModuleDict[external_quantizers]/SymmetricQuantizer[Inception3/__mul___2|OUTPUT]/symmetric_quantize_0" [id=17, type=symmetric_quantize]; "18 Inception3/__add___2" [id=18, type=__add__]; -"19 Inception3/NNCFNetworkInterface[_nncf]/ModuleDict[external_quantizers]/SymmetricQuantizer[Inception3/__add___2|OUTPUT]/symmetric_quantize_0" [id=19, type=symmetric_quantize]; +"19 Inception3/NNCFNetworkInterface[_nncf]/ModuleDict[external_quantizers]/SymmetricQuantizer[Inception3/__add___0|OUTPUT;Inception3/__add___1|OUTPUT;Inception3/__add___2|OUTPUT]/symmetric_quantize_2" [id=19, type=symmetric_quantize]; "20 Inception3/cat_0" [id=20, type=cat]; "21 Inception3/BasicConv2d[Conv2d_1a_3x3]/NNCFConv2d[conv]/ModuleDict[pre_ops]/UpdateWeight[0]/SymmetricQuantizer[op]/symmetric_quantize_0" [id=21, type=symmetric_quantize]; "22 Inception3/BasicConv2d[Conv2d_1a_3x3]/NNCFConv2d[conv]/conv2d_0" [id=22, type=conv2d]; @@ -542,20 +542,20 @@ strict digraph { "3 Inception3/unsqueeze_0" -> "4 Inception3/__mul___0"; "4 Inception3/__mul___0" -> "5 Inception3/NNCFNetworkInterface[_nncf]/ModuleDict[external_quantizers]/SymmetricQuantizer[Inception3/__mul___0|OUTPUT]/symmetric_quantize_0"; "5 Inception3/NNCFNetworkInterface[_nncf]/ModuleDict[external_quantizers]/SymmetricQuantizer[Inception3/__mul___0|OUTPUT]/symmetric_quantize_0" -> "6 Inception3/__add___0"; -"6 Inception3/__add___0" -> "7 Inception3/NNCFNetworkInterface[_nncf]/ModuleDict[external_quantizers]/SymmetricQuantizer[Inception3/__add___0|OUTPUT]/symmetric_quantize_0"; -"7 Inception3/NNCFNetworkInterface[_nncf]/ModuleDict[external_quantizers]/SymmetricQuantizer[Inception3/__add___0|OUTPUT]/symmetric_quantize_0" -> "20 Inception3/cat_0"; +"6 Inception3/__add___0" -> "7 Inception3/NNCFNetworkInterface[_nncf]/ModuleDict[external_quantizers]/SymmetricQuantizer[Inception3/__add___0|OUTPUT;Inception3/__add___1|OUTPUT;Inception3/__add___2|OUTPUT]/symmetric_quantize_0"; +"7 Inception3/NNCFNetworkInterface[_nncf]/ModuleDict[external_quantizers]/SymmetricQuantizer[Inception3/__add___0|OUTPUT;Inception3/__add___1|OUTPUT;Inception3/__add___2|OUTPUT]/symmetric_quantize_0" -> "20 Inception3/cat_0"; "8 Inception3/__getitem___1" -> "9 Inception3/unsqueeze_1"; "9 Inception3/unsqueeze_1" -> "10 Inception3/__mul___1"; "10 Inception3/__mul___1" -> "11 Inception3/NNCFNetworkInterface[_nncf]/ModuleDict[external_quantizers]/SymmetricQuantizer[Inception3/__mul___1|OUTPUT]/symmetric_quantize_0"; "11 Inception3/NNCFNetworkInterface[_nncf]/ModuleDict[external_quantizers]/SymmetricQuantizer[Inception3/__mul___1|OUTPUT]/symmetric_quantize_0" -> "12 Inception3/__add___1"; -"12 Inception3/__add___1" -> "13 Inception3/NNCFNetworkInterface[_nncf]/ModuleDict[external_quantizers]/SymmetricQuantizer[Inception3/__add___1|OUTPUT]/symmetric_quantize_0"; -"13 Inception3/NNCFNetworkInterface[_nncf]/ModuleDict[external_quantizers]/SymmetricQuantizer[Inception3/__add___1|OUTPUT]/symmetric_quantize_0" -> "20 Inception3/cat_0"; +"12 Inception3/__add___1" -> "13 Inception3/NNCFNetworkInterface[_nncf]/ModuleDict[external_quantizers]/SymmetricQuantizer[Inception3/__add___0|OUTPUT;Inception3/__add___1|OUTPUT;Inception3/__add___2|OUTPUT]/symmetric_quantize_1"; +"13 Inception3/NNCFNetworkInterface[_nncf]/ModuleDict[external_quantizers]/SymmetricQuantizer[Inception3/__add___0|OUTPUT;Inception3/__add___1|OUTPUT;Inception3/__add___2|OUTPUT]/symmetric_quantize_1" -> "20 Inception3/cat_0"; "14 Inception3/__getitem___2" -> "15 Inception3/unsqueeze_2"; "15 Inception3/unsqueeze_2" -> "16 Inception3/__mul___2"; "16 Inception3/__mul___2" -> "17 Inception3/NNCFNetworkInterface[_nncf]/ModuleDict[external_quantizers]/SymmetricQuantizer[Inception3/__mul___2|OUTPUT]/symmetric_quantize_0"; "17 Inception3/NNCFNetworkInterface[_nncf]/ModuleDict[external_quantizers]/SymmetricQuantizer[Inception3/__mul___2|OUTPUT]/symmetric_quantize_0" -> "18 Inception3/__add___2"; -"18 Inception3/__add___2" -> "19 Inception3/NNCFNetworkInterface[_nncf]/ModuleDict[external_quantizers]/SymmetricQuantizer[Inception3/__add___2|OUTPUT]/symmetric_quantize_0"; -"19 Inception3/NNCFNetworkInterface[_nncf]/ModuleDict[external_quantizers]/SymmetricQuantizer[Inception3/__add___2|OUTPUT]/symmetric_quantize_0" -> "20 Inception3/cat_0"; +"18 Inception3/__add___2" -> "19 Inception3/NNCFNetworkInterface[_nncf]/ModuleDict[external_quantizers]/SymmetricQuantizer[Inception3/__add___0|OUTPUT;Inception3/__add___1|OUTPUT;Inception3/__add___2|OUTPUT]/symmetric_quantize_2"; +"19 Inception3/NNCFNetworkInterface[_nncf]/ModuleDict[external_quantizers]/SymmetricQuantizer[Inception3/__add___0|OUTPUT;Inception3/__add___1|OUTPUT;Inception3/__add___2|OUTPUT]/symmetric_quantize_2" -> "20 Inception3/cat_0"; "20 Inception3/cat_0" -> "22 Inception3/BasicConv2d[Conv2d_1a_3x3]/NNCFConv2d[conv]/conv2d_0"; "21 Inception3/BasicConv2d[Conv2d_1a_3x3]/NNCFConv2d[conv]/ModuleDict[pre_ops]/UpdateWeight[0]/SymmetricQuantizer[op]/symmetric_quantize_0" -> "22 Inception3/BasicConv2d[Conv2d_1a_3x3]/NNCFConv2d[conv]/conv2d_0"; "22 Inception3/BasicConv2d[Conv2d_1a_3x3]/NNCFConv2d[conv]/conv2d_0" -> "23 Inception3/BasicConv2d[Conv2d_1a_3x3]/NNCFBatchNorm2d[bn]/batch_norm_0"; diff --git a/tests/torch/models_hub_test/requirements.txt b/tests/torch/models_hub_test/requirements.txt index 6e5c5829757..4f1c41ddac4 100644 --- a/tests/torch/models_hub_test/requirements.txt +++ b/tests/torch/models_hub_test/requirements.txt @@ -2,7 +2,7 @@ torch==2.2.1 --extra-index-url https://download.pytorch.org/whl/cpu torchvision==0.17.1 -transformers==4.36.0 +transformers==4.38.2 pytest==8.0.2 timm==0.9.2 scikit-learn==1.2.2 diff --git a/tests/torch/nas/test_search.py b/tests/torch/nas/test_search.py index e717f3e4453..931dbe1ff79 100644 --- a/tests/torch/nas/test_search.py +++ b/tests/torch/nas/test_search.py @@ -273,7 +273,7 @@ def validate_model_fn(model, eval_datasets): ) max_subnetwork_acc = validate_model_fn(model, eval_datasets) - _, best_config, performance_metrics = search.run(validate_model_fn, eval_datasets, tmp_path) + _, _, performance_metrics = search.run(validate_model_fn, eval_datasets, tmp_path) assert max_subnetwork_acc == search_result_descriptors.expected_accuracy assert performance_metrics[1] == search_result_descriptors.subnet_expected_accuracy[search_algo_name] diff --git a/tests/torch/ptq/test_ptq_params.py b/tests/torch/ptq/test_ptq_params.py index 8ebdb1f1b2d..88a691c47fc 100644 --- a/tests/torch/ptq/test_ptq_params.py +++ b/tests/torch/ptq/test_ptq_params.py @@ -10,20 +10,26 @@ # limitations under the License. import pytest +import torch from torch import nn from nncf.common.graph.patterns import GraphPattern from nncf.common.graph.patterns.manager import PatternsManager from nncf.common.graph.transformations.commands import TargetType +from nncf.common.graph.transformations.commands import TransformationType from nncf.common.utils.backend import BackendType from nncf.parameters import TargetDevice from nncf.quantization.algorithms.min_max.algorithm import MinMaxQuantization from nncf.quantization.algorithms.min_max.torch_backend import PTMinMaxAlgoBackend from nncf.scopes import IgnoredScope +from nncf.torch.graph.graph import PTNNCFGraph from nncf.torch.graph.graph import PTTargetPoint +from nncf.torch.graph.operator_metatypes import PTCatMetatype from nncf.torch.graph.operator_metatypes import PTModuleConv2dMetatype from nncf.torch.graph.operator_metatypes import PTModuleLinearMetatype from nncf.torch.graph.operator_metatypes import PTSoftmaxMetatype +from nncf.torch.graph.transformations.commands import PTSharedFnInsertionCommand +from tests.common.quantization.metatypes import CatTestMetatype from tests.common.quantization.metatypes import Conv2dTestMetatype from tests.common.quantization.metatypes import LinearTestMetatype from tests.common.quantization.metatypes import SoftmaxTestMetatype @@ -97,17 +103,34 @@ def check_quantize_outputs_fq_num(self, quantize_outputs, act_num_q, weight_num_ assert act_num_q == 1 assert weight_num_q == 1 + def check_unified_scale_layout(self, layout, unified_scale_group): + assert len(layout.transformations) == 1 + command = layout.transformations[0] + assert isinstance(command, PTSharedFnInsertionCommand) + assert command.op_name == "/Conv_1_0|INPUT0;/Conv_2_0|INPUT0;/Conv_3_0|INPUT0" + assert command.target_points == unified_scale_group + assert torch.allclose(command.fn.scale, torch.tensor(4.0)) + assert command.type == TransformationType.INSERT + def target_point(self, target_type: TargetType, target_node_name: str, port_id: int) -> PTTargetPoint: return PTTargetPoint(target_type, target_node_name, input_port_id=port_id) + def get_backend_tensor(self, value): + return torch.tensor(value) + @property def metatypes_mapping(self): return { Conv2dTestMetatype: PTModuleConv2dMetatype, LinearTestMetatype: PTModuleLinearMetatype, SoftmaxTestMetatype: PTSoftmaxMetatype, + CatTestMetatype: PTCatMetatype, } + @property + def nncf_graph_cls(self): + return PTNNCFGraph + @pytest.fixture(scope="session") def test_params(self): linear_model = LinearTestModel().get_nncf_network() diff --git a/tests/torch/qat/helpers.py b/tests/torch/qat/helpers.py index 5d46cb9ad68..da6788a6a91 100644 --- a/tests/torch/qat/helpers.py +++ b/tests/torch/qat/helpers.py @@ -96,11 +96,9 @@ def get_quantization_preset(config_quantization_params: Dict[str, Any]) -> Optio def get_advanced_ptq_parameters(config_quantization_params: Dict[str, Any]) -> AdvancedQuantizationParameters: range_estimator_params = get_range_init_type(config_quantization_params) return AdvancedQuantizationParameters( - overflow_fix=convert_overflow_fix_param(config_quantization_params.get("overflow_fix", None)), - weights_quantization_params=convert_quantization_params(config_quantization_params.get("weights", None)), - activations_quantization_params=convert_quantization_params( - config_quantization_params.get("activations", None) - ), + overflow_fix=convert_overflow_fix_param(config_quantization_params.get("overflow_fix")), + weights_quantization_params=convert_quantization_params(config_quantization_params.get("weights")), + activations_quantization_params=convert_quantization_params(config_quantization_params.get("activations")), weights_range_estimator_params=range_estimator_params, activations_range_estimator_params=range_estimator_params, ) diff --git a/tests/torch/qat/test_qat_segmentation.py b/tests/torch/qat/test_qat_segmentation.py index 2f479faa7f7..f3dff7ef899 100644 --- a/tests/torch/qat/test_qat_segmentation.py +++ b/tests/torch/qat/test_qat_segmentation.py @@ -209,7 +209,7 @@ def train( datasets.train_data_loader.sampler.set_epoch(epoch) logger.info(">>>> [Epoch: {0:d}] Validation".format(epoch)) - loss, (iou, current_miou) = val_obj.run_epoch(config.print_step) + _, (_, current_miou) = val_obj.run_epoch(config.print_step) # best_metric = max(current_miou, best_metric) acc_drop = original_metric - current_miou best_miou = max(current_miou, best_miou) @@ -225,7 +225,7 @@ def train( return acc_drop logger.info(">>>> [Epoch: {0:d}] Training".format(epoch)) - epoch_loss, (iou, miou) = train_obj.run_epoch(config.print_step) + epoch_loss, (_, miou) = train_obj.run_epoch(config.print_step) logger.info(">>>> [Epoch: {0:d}] Avg. loss: {1:.4f} | Mean IoU: {2:.4f}".format(epoch, epoch_loss, miou)) diff --git a/tests/torch/quantization/quantization_helpers.py b/tests/torch/quantization/quantization_helpers.py index 16f2c83e7c7..45e0779e420 100644 --- a/tests/torch/quantization/quantization_helpers.py +++ b/tests/torch/quantization/quantization_helpers.py @@ -63,7 +63,7 @@ def get_squeezenet_quantization_config(image_size=32, batch_size=3): def distributed_init_test_default(gpu, ngpus_per_node, config): config.batch_size = 3 - config.workers = 0 # workaround for the pytorch multiprocessingdataloader issue/ + config.workers = 0 # workaround for the pytorch multiprocessingdataloader issue/ config.gpu = gpu config.ngpus_per_node = ngpus_per_node config.rank = gpu diff --git a/tests/torch/requirements.txt b/tests/torch/requirements.txt index 244e037e95e..d9a365f3e34 100644 --- a/tests/torch/requirements.txt +++ b/tests/torch/requirements.txt @@ -8,8 +8,8 @@ pytest-dependency>=0.5.1 virtualenv # Required for search_building_blocks tests -transformers[torch]~=4.36.0 -accelerate==0.24.1 +transformers[torch]~=4.38.2 +accelerate==0.28.0 # Required for movement_sparsity tests datasets~=2.14.0 diff --git a/tests/torch/sota_checkpoints_eval.json b/tests/torch/sota_checkpoints_eval.json index dd8f565f17b..c736f6c521d 100644 --- a/tests/torch/sota_checkpoints_eval.json +++ b/tests/torch/sota_checkpoints_eval.json @@ -12,7 +12,7 @@ "config": "examples/torch/classification/configs/quantization/resnet50_imagenet_int8.json", "reference": "resnet50_imagenet", "target_ov": 76.39, - "target_pt": 76.45, + "target_pt": 76.41, "metric_type": "Acc@1", "resume": "resnet50_imagenet_int8.pth", "model_description": "ResNet-50", @@ -38,7 +38,7 @@ "config": "examples/torch/classification/configs/mixed_precision/resnet50_imagenet_mixed_int_hawq.json", "reference": "resnet50_imagenet", "target_ov": 75.61, - "target_pt": 75.86, + "target_pt": 75.94, "metric_type": "Acc@1", "resume": "resnet50_imagenet_int4_int8.pth", "model_description": "ResNet-50", @@ -50,7 +50,7 @@ "config": "examples/torch/classification/configs/sparsity_quantization/resnet50_imagenet_rb_sparsity_int8.json", "reference": "resnet50_imagenet", "target_ov": 75.39, - "target_pt": 75.42, + "target_pt": 75.41, "metric_type": "Acc@1", "resume": "resnet50_imagenet_rb_sparsity_int8.pth", "model_description": "ResNet-50", @@ -62,7 +62,7 @@ "config": "examples/torch/classification/configs/sparsity_quantization/resnet50_imagenet_rb_sparsity50_int8.json", "reference": "resnet50_imagenet", "target_ov": 75.44, - "target_pt": 75.47, + "target_pt": 75.5, "metric_type": "Acc@1", "resume": "resnet50_imagenet_rb_sparsity50_int8.pth", "model_description": "ResNet-50", @@ -92,7 +92,7 @@ "config": "examples/torch/classification/configs/quantization/inception_v3_imagenet_int8.json", "reference": "inception_v3_imagenet", "target_ov": 77.49, - "target_pt": 77.43, + "target_pt": 77.46, "metric_type": "Acc@1", "resume": "inception_v3_imagenet_int8.pth", "model_description": "Inception V3", @@ -105,7 +105,7 @@ "config": "examples/torch/classification/configs/sparsity_quantization/inception_v3_imagenet_rb_sparsity_int8.json", "reference": "inception_v3_imagenet", "target_ov": 76.34, - "target_pt": 76.32, + "target_pt": 76.34, "metric_type": "Acc@1", "resume": "inception_v3_imagenet_rb_sparsity_int8.pth", "model_description": "Inception V3", @@ -125,7 +125,7 @@ "config": "examples/torch/classification/configs/quantization/mobilenet_v2_imagenet_int8.json", "reference": "mobilenet_v2_imagenet", "target_ov": 71.01, - "target_pt": 71.3, + "target_pt": 71.22, "metric_type": "Acc@1", "resume": "mobilenet_v2_imagenet_int8.pth", "model_description": "MobileNet V2", @@ -139,7 +139,7 @@ "config": "examples/torch/classification/configs/quantization/mobilenet_v2_imagenet_int8_per_tensor.json", "reference": "mobilenet_v2_imagenet", "target_ov": 71.17, - "target_pt": 71.28, + "target_pt": 71.26, "metric_type": "Acc@1", "resume": "mobilenet_v2_imagenet_int8_per_tensor.pth", "model_description": "MobileNet V2", @@ -153,7 +153,7 @@ "config": "examples/torch/classification/configs/mixed_precision/mobilenet_v2_imagenet_mixed_int_hawq.json", "reference": "mobilenet_v2_imagenet", "target_ov": 70.52, - "target_pt": 70.57, + "target_pt": 70.68, "metric_type": "Acc@1", "resume": "mobilenet_v2_imagenet_int4_int8.pth", "model_description": "MobileNet V2", @@ -167,7 +167,7 @@ "config": "examples/torch/classification/configs/sparsity_quantization/mobilenet_v2_imagenet_rb_sparsity_int8.json", "reference": "mobilenet_v2_imagenet", "target_ov": 71.07, - "target_pt": 71.02, + "target_pt": 71.06, "metric_type": "Acc@1", "resume": "mobilenet_v2_imagenet_rb_sparsity_int8.pth", "model_description": "MobileNet V2", @@ -237,7 +237,7 @@ "config": "examples/torch/classification/configs/mixed_precision/squeezenet1_1_imagenet_mixed_int_hawq_old_eval.json", "reference": "squeezenet1_1_imagenet", "target_ov": 57.53, - "target_pt": 57.63, + "target_pt": 57.66, "metric_type": "Acc@1", "resume": "squeezenet1_1_imagenet_int4_int8.pth", "model_description": "SqueezeNet V1.1", @@ -325,7 +325,7 @@ "config": "examples/torch/object_detection/configs/ssd300_mobilenet_voc_magnitude_int8.json", "reference": "ssd300_mobilenet_voc", "target_ov": 63.01, - "target_pt": 62.97, + "target_pt": 62.99, "metric_type": "Mean AP", "resume": "ssd300_mobilenet_voc_magnitude_sparsity_int8.pth", "model_description": "SSD300-MobileNet", @@ -360,7 +360,7 @@ "config": "examples/torch/object_detection/configs/ssd300_vgg_voc_magnitude_sparsity_int8.json", "reference": "ssd300_vgg_voc", "target_ov": 77.46, - "target_pt": 77.67, + "target_pt": 77.66, "metric_type": "Mean AP", "resume": "ssd300_vgg_voc_magnitude_sparsity_int8.pth", "model_description": "SSD300-VGG-BN", @@ -392,7 +392,7 @@ "config": "examples/torch/object_detection/configs/ssd512_vgg_voc_int8.json", "reference": "ssd512_vgg_voc", "target_ov": 80.19, - "target_pt": 80.14, + "target_pt": 80.11, "metric_type": "Mean AP", "resume": "ssd512_vgg_voc_int8.pth", "batch": 32, @@ -407,7 +407,7 @@ "config": "examples/torch/object_detection/configs/ssd512_vgg_voc_magnitude_sparsity_int8.json", "reference": "ssd512_vgg_voc", "target_ov": 79.98, - "target_pt": 79.76, + "target_pt": 79.7, "metric_type": "Mean AP", "resume": "ssd512_vgg_voc_magnitude_sparsity_int8.pth", "batch": 32, @@ -468,7 +468,7 @@ "config": "examples/torch/semantic_segmentation/configs/icnet_camvid_int8.json", "reference": "icnet_camvid", "target_ov": 67.89, - "target_pt": 67.86, + "target_pt": 67.87, "metric_type": "Mean IoU", "resume": "icnet_camvid_int8.pth", "model_description": "ICNet", @@ -481,7 +481,7 @@ "config": "examples/torch/semantic_segmentation/configs/icnet_camvid_magnitude_sparsity_int8.json", "reference": "icnet_camvid", "target_ov": 67.16, - "target_pt": 67.17, + "target_pt": 67.16, "metric_type": "Mean IoU", "resume": "icnet_camvid_magnitude_sparsity_int8.pth", "model_description": "ICNet", diff --git a/tests/torch/sparsity/magnitude/test_algo.py b/tests/torch/sparsity/magnitude/test_algo.py index 0dc2da03aa0..f6f16d979c3 100644 --- a/tests/torch/sparsity/magnitude/test_algo.py +++ b/tests/torch/sparsity/magnitude/test_algo.py @@ -46,18 +46,15 @@ def test_can_create_magnitude_sparse_algo__with_defaults(): _, sparse_model_conv = check_correct_nncf_modules_replacement(model, sparse_model) - i = 0 - nncf_stats = compression_ctrl.statistics() for layer_info in nncf_stats.magnitude_sparsity.thresholds: assert layer_info.threshold == approx(0.24, 0.1) assert isinstance(compression_ctrl._weight_importance_fn, type(normed_magnitude)) - for sparse_module in sparse_model_conv.values(): + for i, sparse_module in enumerate(sparse_model_conv.values()): store = [] ref_mask = torch.ones_like(sparse_module.weight) if i == 0 else ref_mask_2 - i += 1 for op in sparse_module.pre_ops.values(): if isinstance(op, UpdateWeight) and isinstance(op.operand, BinaryMask): assert torch.allclose(op.operand.binary_mask, ref_mask) diff --git a/tests/torch/test_model_transformer.py b/tests/torch/test_model_transformer.py index c6348644f68..c554a39ccb3 100644 --- a/tests/torch/test_model_transformer.py +++ b/tests/torch/test_model_transformer.py @@ -50,6 +50,7 @@ from nncf.torch.graph.operator_metatypes import PTOutputNoopMetatype from nncf.torch.graph.operator_metatypes import PTReshapeMetatype from nncf.torch.graph.transformations.command_creation import create_quantizer_insertion_command +from nncf.torch.graph.transformations.command_creation import create_shared_quantizer_insertion_command from nncf.torch.graph.transformations.commands import ExtraCompressionModuleType from nncf.torch.graph.transformations.commands import PTBiasCorrectionCommand from nncf.torch.graph.transformations.commands import PTInsertionCommand @@ -638,32 +639,26 @@ def to(self, device): self.to_device = device -@pytest.mark.parametrize( - "target_type, node_name, input_port_id, ref_name, compression_module_registered", - ( - ( - TargetType.OPERATOR_POST_HOOK, - "/nncf_model_input_0", - None, - "/nncf_model_input_0|OUTPUT", - True, - ), - ( - TargetType.OPERATOR_PRE_HOOK, - "InsertionPointTestModel/linear_0", - 0, - "InsertionPointTestModel/linear_0|INPUT0", - True, - ), - (TargetType.OPERATION_WITH_WEIGHTS, "InsertionPointTestModel/NNCFConv2d[conv1]/conv2d_0", None, None, False), +SHARED_FN_TARGET_POINTS = ( + PTTargetPoint( + TargetType.OPERATOR_POST_HOOK, + "/nncf_model_input_0", + ), + PTTargetPoint( + TargetType.OPERATOR_PRE_HOOK, + "InsertionPointTestModel/linear_0", + input_port_id=0, + ), + PTTargetPoint( + TargetType.OPERATION_WITH_WEIGHTS, + "InsertionPointTestModel/NNCFConv2d[conv1]/conv2d_0", ), ) -def test_quantizer_insertion_transformations( - target_type, node_name, input_port_id, ref_name, compression_module_registered -): - hook = Hook() - target_point = PTTargetPoint(target_type, node_name, input_port_id=input_port_id) + +@pytest.mark.parametrize("target_point", SHARED_FN_TARGET_POINTS) +def test_create_quantizer_insertion_command(target_point): + hook = Hook() command = create_quantizer_insertion_command(target_point, hook) assert command.fn is hook @@ -679,21 +674,21 @@ def test_quantizer_insertion_transformations( assert command.compression_module_type is ExtraCompressionModuleType.EXTERNAL_QUANTIZER -SHARED_FN_TARGET_POINTS = [ - PTTargetPoint( - TargetType.OPERATOR_POST_HOOK, - "/nncf_model_input_0", - ), - PTTargetPoint( - TargetType.OPERATOR_PRE_HOOK, - "InsertionPointTestModel/linear_0", - input_port_id=0, - ), - PTTargetPoint( - TargetType.OPERATION_WITH_WEIGHTS, - "InsertionPointTestModel/NNCFConv2d[conv1]/conv2d_0", - ), -] +def test_create_shared_quantizer_insertion_command(): + ref_storage_key = ( + "/nncf_model_input_0|OUTPUT;" + "InsertionPointTestModel/NNCFConv2d[conv1]/conv2d_0|OUTPUT;" + "InsertionPointTestModel/linear_0|INPUT0" + ) + hook = Hook() + + command = create_shared_quantizer_insertion_command(list(SHARED_FN_TARGET_POINTS), hook) + assert command.fn is hook + assert isinstance(command, PTSharedFnInsertionCommand) + assert command.target_points == list(SHARED_FN_TARGET_POINTS) + assert command.fn is hook + assert command.op_name == ref_storage_key + assert command.compression_module_type is ExtraCompressionModuleType.EXTERNAL_QUANTIZER @pytest.mark.parametrize("compression_module_type", ExtraCompressionModuleType) diff --git a/tests/torch/test_statistics_aggregator.py b/tests/torch/test_statistics_aggregator.py index 10dfc149c1c..a5c8a71788e 100644 --- a/tests/torch/test_statistics_aggregator.py +++ b/tests/torch/test_statistics_aggregator.py @@ -183,29 +183,29 @@ def test_successive_statistics_aggregation( if not is_standard_estimator and not is_backend_support_custom_estimators: pytest.skip("Custom estimators are not supported for this backend yet") - ### Register operations before statistic collection + # Register operations before statistic collection def fn(x): return x * 2 target_point = self.get_target_point(test_parameters.target_type) model = self.__add_fn_to_model(model, target_point, fn) - ### Check hook inserted correctly + # Check hook inserted correctly self.__check_successive_hooks(test_parameters, model, target_point, fn) - ### Register and collect statistics after inserted operations + # Register and collect statistics after inserted operations statistic_points = self.__get_statistic_points( test_parameters, model, quantizer_config, dataset_samples, inplace_statistics, mocker ) tensor_collector = self.__collect_statistics_get_collector(statistic_points, model, dataset_samples) - ### Check values are changed because of the inserted operation + # Check values are changed because of the inserted operation self.__check_collector( test_parameters, tensor_collector, is_stat_in_shape_of_scale, ) - ### Check the inserted operation is inside the model + # Check the inserted operation is inside the model self.__check_successive_hooks(test_parameters, model, target_point, fn) @pytest.mark.parametrize( @@ -270,7 +270,7 @@ def test_nested_statistics_aggregation( if not is_standard_estimator and not is_backend_support_custom_estimators: pytest.skip("Custom estimators are not supported for this backend yet") - ### Register operations before statistic collection + # Register operations before statistic collection @register_operator() def fn(x): return x * 2 @@ -280,22 +280,22 @@ def fn(x): nested_target_point = PTMinMaxAlgoBackend.target_point(nested_target_type, nested_target_node_name, 0) model = self.__add_fn_to_model(model, nested_target_point, fn) - ### Check hook inserted correctly + # Check hook inserted correctly self.__check_nested_hooks(test_parameters, model, target_point, nested_target_type, nested_target_node_name, fn) - ### Register and collect statistics after inserted operations + # Register and collect statistics after inserted operations statistic_points = self.__get_statistic_points( test_parameters, model, quantizer_config, dataset_samples, inplace_statistics, mocker ) tensor_collector = self.__collect_statistics_get_collector(statistic_points, model, dataset_samples) - ### Check values are changed because of the inserted operation + # Check values are changed because of the inserted operation self.__check_collector( test_parameters, tensor_collector, is_stat_in_shape_of_scale, ) - ### Check the inserted operation is inside the model + # Check the inserted operation is inside the model self.__check_nested_hooks(test_parameters, model, target_point, nested_target_type, nested_target_node_name, fn) @staticmethod diff --git a/tests/torch/test_tracing_context.py b/tests/torch/test_tracing_context.py index 75b127eb2d6..7c4a23a588a 100644 --- a/tests/torch/test_tracing_context.py +++ b/tests/torch/test_tracing_context.py @@ -110,10 +110,10 @@ def test_traced_tensors_are_stripped_on_context_exit(): assert isinstance(module.weight, TracedParameter) assert isinstance(module.conv2d.weight, TracedParameter) assert isinstance(result, TracedTensor) - assert type(module.cached_tensor) == torch.Tensor - assert type(result) == torch.Tensor - assert type(module.weight) == torch.nn.Parameter - assert type(module.conv2d.weight) == torch.nn.Parameter + assert isinstance(module.cached_tensor, torch.Tensor) + assert isinstance(result, torch.Tensor) + assert isinstance(module.weight, torch.nn.Parameter) + assert isinstance(module.conv2d.weight, torch.nn.Parameter) def test_no_cross_forward_run_dependency(): diff --git a/tools/extract_ov_subgraph.py b/tools/extract_ov_subgraph.py index 022ee777e8d..0739bfbf1eb 100644 --- a/tools/extract_ov_subgraph.py +++ b/tools/extract_ov_subgraph.py @@ -94,8 +94,8 @@ def get_nodes(xml_dict: Dict, edges: Dict): try: attributes = node["attributes"] data = node["data"]["attributes"] if "data" in node else None - inp = node["input"] if "input" in node else None - out = node["output"] if "output" in node else None + inp = node.get("input", None) + out = node.get("output", None) node_id = int(attributes["id"]) node_name = attributes["name"]