diff --git a/.azure-pipelines/docker/Dockerfile.devel b/.azure-pipelines/docker/Dockerfile.devel index 30e6bf3ec11..2f3aab3ce72 100644 --- a/.azure-pipelines/docker/Dockerfile.devel +++ b/.azure-pipelines/docker/Dockerfile.devel @@ -36,7 +36,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends --fix-missing \ RUN ln -sf $(which python3) /usr/bin/python -RUN python -m pip --no-cache-dir install --upgrade pip +RUN python -m pip install pip==24.0 RUN python -m pip install --no-cache-dir setuptools RUN pip list diff --git a/.azure-pipelines/model-test.yml b/.azure-pipelines/model-test.yml index cc9e431b608..b55d4f41ce3 100644 --- a/.azure-pipelines/model-test.yml +++ b/.azure-pipelines/model-test.yml @@ -57,7 +57,7 @@ parameters: - name: PyTorchModelList type: object default: - - resnet18 +# - resnet18 - resnet18_fx - name: ONNXModelList type: object diff --git a/.github/checkgroup.yml b/.github/checkgroup.yml index 63b228f3f7e..4cf4d63d13e 100644 --- a/.github/checkgroup.yml +++ b/.github/checkgroup.yml @@ -51,7 +51,6 @@ subprojects: - "Model-Test" - "Model-Test (Generate Report GenerateReport)" - "Model-Test (Run ONNX Model resnet50-v1-12)" - - "Model-Test (Run PyTorch Model resnet18)" - "Model-Test (Run PyTorch Model resnet18_fx)" - "Model-Test (Run TensorFlow Model resnet50v1.5)" - "Model-Test (Run TensorFlow Model ssd_resnet50_v1)" diff --git a/examples/onnxrt/nlp/huggingface_model/token_classification/layoutlmv2/quantization/ptq_dynamic/main.py b/examples/onnxrt/nlp/huggingface_model/token_classification/layoutlmv2/quantization/ptq_dynamic/main.py index 95a49ce37ab..c7cf936270d 100644 --- a/examples/onnxrt/nlp/huggingface_model/token_classification/layoutlmv2/quantization/ptq_dynamic/main.py +++ b/examples/onnxrt/nlp/huggingface_model/token_classification/layoutlmv2/quantization/ptq_dynamic/main.py @@ -216,8 +216,6 @@ def _process_dataset(self): self.label = [] self.onnx_inputs = [] for inputs in self.dataset: - # import pdb; - # pdb.set_trace() onnx_inputs = [] has_labels = all(inputs.get(k) is not None for k in self.label_names) if has_labels: @@ -237,8 +235,6 @@ def _process_dataset(self): } """ for key in self.onnx_input_names: - # import pdb; - # pdb.set_trace() if key in inputs: # onnx_inputs[key] = np.array([inputs[key]]) onnx_inputs.append(np.array(inputs[key])) diff --git a/examples/onnxrt/nlp/huggingface_model/token_classification/layoutlmv2/quantization/ptq_static/main.py b/examples/onnxrt/nlp/huggingface_model/token_classification/layoutlmv2/quantization/ptq_static/main.py index b3de22ac766..5540f4c002d 100644 --- a/examples/onnxrt/nlp/huggingface_model/token_classification/layoutlmv2/quantization/ptq_static/main.py +++ b/examples/onnxrt/nlp/huggingface_model/token_classification/layoutlmv2/quantization/ptq_static/main.py @@ -216,8 +216,6 @@ def _process_dataset(self): self.label = [] self.onnx_inputs = [] for inputs in self.dataset: - # import pdb; - # pdb.set_trace() onnx_inputs = [] has_labels = all(inputs.get(k) is not None for k in self.label_names) if has_labels: @@ -237,8 +235,6 @@ def _process_dataset(self): } """ for key in self.onnx_input_names: - # import pdb; - # pdb.set_trace() if key in inputs: # onnx_inputs[key] = np.array([inputs[key]]) onnx_inputs.append(np.array(inputs[key])) diff --git a/examples/pytorch/object_detection/ssd_resnet34/quantization/ptq/fx/python/models/utils.py b/examples/pytorch/object_detection/ssd_resnet34/quantization/ptq/fx/python/models/utils.py index 940722075ab..e4d5db6b8ee 100644 --- a/examples/pytorch/object_detection/ssd_resnet34/quantization/ptq/fx/python/models/utils.py +++ b/examples/pytorch/object_detection/ssd_resnet34/quantization/ptq/fx/python/models/utils.py @@ -59,7 +59,6 @@ def _compute_padding(self, input, dim): return additional_padding, total_padding def forward(self, input): - #import pdb; pdb.set_trace() if self.padding == "VALID": return F.conv2d( input, @@ -180,7 +179,6 @@ def decode_boxes(rel_codes, boxes, weights): dh = dh / wh pred_ctr_x = dx * widths + ctr_x - #import pdb; pdb.set_trace() pred_ctr_y = dy * heights + ctr_y pred_w = torch.exp(dw) * widths pred_h = torch.exp(dh) * heights @@ -194,5 +192,4 @@ def decode_boxes(rel_codes, boxes, weights): ], dim=2, ) - #import pdb; pdb.set_trace() return pred_boxes diff --git a/neural_coder/coders/tensorflow/amp.py b/neural_coder/coders/tensorflow/amp.py index 70302d78d4a..77f349ef084 100644 --- a/neural_coder/coders/tensorflow/amp.py +++ b/neural_coder/coders/tensorflow/amp.py @@ -22,8 +22,6 @@ def __init__(self, file) -> None: self.keras_edited_flag = False def transform(self): - # import pdb - # pdb.set_trace() lines = self.file.split("\n") for line in lines: if self.is_modify(line): diff --git a/neural_coder/coders/tensorflow/inc.py b/neural_coder/coders/tensorflow/inc.py index 837dff143fb..30455bc27c8 100644 --- a/neural_coder/coders/tensorflow/inc.py +++ b/neural_coder/coders/tensorflow/inc.py @@ -21,8 +21,6 @@ def __init__(self, file) -> None: self.result = [] def transform(self): - # import pdb - # pdb.set_trace() lines = self.file.split("\n") for line in lines: if self.is_modify(line): diff --git a/neural_compressor/adaptor/keras.py b/neural_compressor/adaptor/keras.py index e6fbfb963c5..3def459852a 100644 --- a/neural_compressor/adaptor/keras.py +++ b/neural_compressor/adaptor/keras.py @@ -24,7 +24,6 @@ import numpy as np import yaml -from ..conf.dotdict import deep_get from ..data.dataloaders.base_dataloader import BaseDataLoader from ..utils import logger from ..utils.utility import ( @@ -34,6 +33,7 @@ Dequantize, LazyImport, Statistics, + deep_get, dump_elapsed_time, singleton, version1_lt_version2, diff --git a/neural_compressor/adaptor/mxnet.py b/neural_compressor/adaptor/mxnet.py index e53bf5871a2..57f28bbcbe6 100644 --- a/neural_compressor/adaptor/mxnet.py +++ b/neural_compressor/adaptor/mxnet.py @@ -446,13 +446,6 @@ def _one_shot_query(self): raise ValueError( "Please check if the format of {} follows Neural Compressor yaml schema.".format(self.cfg) ) - self._update_cfg_with_usr_definition() - - def _update_cfg_with_usr_definition(self): - from neural_compressor.conf.pythonic_config import mxnet_config - - if mxnet_config.precisions is not None: - self.cur_config["precisions"]["names"] = ",".join(mxnet_config.precisions) def _get_specified_version_cfg(self, data): """Get the configuration for the current runtime. diff --git a/neural_compressor/adaptor/onnxrt.py b/neural_compressor/adaptor/onnxrt.py index be92b6ee100..6cc17942577 100644 --- a/neural_compressor/adaptor/onnxrt.py +++ b/neural_compressor/adaptor/onnxrt.py @@ -2242,15 +2242,6 @@ def _one_shot_query(self): raise ValueError( "Please check if the format of {} follows Neural Compressor yaml schema.".format(self.cfg) ) - self._update_cfg_with_usr_definition() - - def _update_cfg_with_usr_definition(self): - from neural_compressor.conf.pythonic_config import onnxruntime_config - - if onnxruntime_config.graph_optimization_level is not None: - self.cur_config["graph_optimization"]["level"] = onnxruntime_config.graph_optimization_level - if onnxruntime_config.precisions is not None: - self.cur_config["precisions"]["names"] = ",".join(onnxruntime_config.precisions) def _get_specified_version_cfg(self, data): # pragma: no cover """Get the configuration for the current runtime. diff --git a/neural_compressor/adaptor/pytorch.py b/neural_compressor/adaptor/pytorch.py index 2ac0af30efe..e34aae63e59 100644 --- a/neural_compressor/adaptor/pytorch.py +++ b/neural_compressor/adaptor/pytorch.py @@ -5102,13 +5102,6 @@ def _one_shot_query(self): self.cur_config = self.cur_config[self.device] elif "cpu" in self.cur_config: self.cur_config = self.cur_config["cpu"] - self._update_cfg_with_usr_definition() - - def _update_cfg_with_usr_definition(self): - from neural_compressor.conf.pythonic_config import pytorch_config - - if pytorch_config.precisions is not None: - self.cur_config["precisions"]["names"] = ",".join(pytorch_config.precisions) def get_quantization_capability(self, datatype="int8"): """Get the supported op types' quantization capability. diff --git a/neural_compressor/adaptor/tensorflow.py b/neural_compressor/adaptor/tensorflow.py index 212c233a530..41b4c148192 100644 --- a/neural_compressor/adaptor/tensorflow.py +++ b/neural_compressor/adaptor/tensorflow.py @@ -24,7 +24,6 @@ import numpy as np import yaml -from ..conf.dotdict import deep_get from ..data.dataloaders.base_dataloader import BaseDataLoader from ..utils import logger from ..utils.utility import ( @@ -34,6 +33,7 @@ Dequantize, LazyImport, Statistics, + deep_get, dump_elapsed_time, singleton, version1_eq_version2, @@ -2204,14 +2204,6 @@ def _one_shot_query(self): raise ValueError( "Please check if the format of {} follows Neural Compressor yaml schema.".format(self.cfg) ) - self._update_cfg_with_usr_definition() - - def _update_cfg_with_usr_definition(self): - """Add user defined precision configuration.""" - from neural_compressor.conf.pythonic_config import tensorflow_config - - if tensorflow_config.precisions is not None: - self.cur_config["precisions"]["names"] = ",".join(tensorflow_config.precisions) def get_version(self): """Get the current backend version information. diff --git a/neural_compressor/adaptor/tf_utils/graph_converter.py b/neural_compressor/adaptor/tf_utils/graph_converter.py index 67c205a646d..5703914a9fd 100644 --- a/neural_compressor/adaptor/tf_utils/graph_converter.py +++ b/neural_compressor/adaptor/tf_utils/graph_converter.py @@ -28,13 +28,13 @@ from tensorflow.python.platform import gfile from neural_compressor.adaptor.tf_utils.graph_rewriter.generic.insert_print_node import InsertPrintMinMaxNode -from neural_compressor.conf.dotdict import deep_get from neural_compressor.model import Model from neural_compressor.model.tensorflow_model import TensorflowSavedModelModel from neural_compressor.utils.utility import ( CaptureOutputToFile, CpuInfo, combine_histogram, + deep_get, get_all_fp32_data, get_tensor_histogram, ) diff --git a/neural_compressor/adaptor/tf_utils/graph_converter_without_calib.py b/neural_compressor/adaptor/tf_utils/graph_converter_without_calib.py index 1aaf15feaa8..aaeba9fdf93 100644 --- a/neural_compressor/adaptor/tf_utils/graph_converter_without_calib.py +++ b/neural_compressor/adaptor/tf_utils/graph_converter_without_calib.py @@ -24,8 +24,8 @@ import tensorflow as tf from tensorflow.python.platform import gfile -from neural_compressor.conf.dotdict import deep_get from neural_compressor.model import Model +from neural_compressor.utils.utility import deep_get from .graph_rewriter.bf16.bf16_convert import BF16Convert from .graph_rewriter.generic.fold_batch_norm import FoldBatchNormNodesOptimizer diff --git a/neural_compressor/compression/pruner/utils.py b/neural_compressor/compression/pruner/utils.py index 661cd7008f2..87baabd907e 100644 --- a/neural_compressor/compression/pruner/utils.py +++ b/neural_compressor/compression/pruner/utils.py @@ -18,90 +18,20 @@ # limitations under the License. import re -from collections import UserDict, defaultdict +from collections import UserDict import numpy as np -import yaml -from ...config import WeightPruningConfig as WeightPruningConf - -try: - from neural_compressor.conf.config import Pruner - from neural_compressor.conf.dotdict import DotDict - from neural_compressor.utils import logger - - from ...conf.config import PrunerV2 - from ...conf.pythonic_config import WeightPruningConfig - from ...utils.utility import LazyImport - - torch = LazyImport("torch") - nn = LazyImport("torch.nn") - F = LazyImport("torch.nn.functional") - tf = LazyImport("tensorflow") -except: - import logging - - import tensorflow as tf - import torch - import torch.nn as nn - import torch.nn.functional as F - - from .dot_dict import DotDict # #TODO - - logger = logging.getLogger(__name__) - from .schema_check import PrunerV2 - - class WeightPruningConfig: - """Similar to torch optimizer's interface.""" - - def __init__( - self, - pruning_configs=[{}], ##empty dict will use global values - target_sparsity=0.9, - pruning_type="snip_momentum", - pattern="4x1", - op_names=[], - excluded_op_names=[], - start_step=0, - end_step=0, - pruning_scope="global", - pruning_frequency=1, - min_sparsity_ratio_per_op=0.0, - max_sparsity_ratio_per_op=0.98, - sparsity_decay_type="exp", - pruning_op_types=["Conv", "Linear"], - **kwargs, - ): - """Init a WeightPruningConfig object.""" - self.pruning_configs = pruning_configs - self._weight_compression = DotDict( - { - "target_sparsity": target_sparsity, - "pruning_type": pruning_type, - "pattern": pattern, - "op_names": op_names, - "excluded_op_names": excluded_op_names, ##global only - "start_step": start_step, - "end_step": end_step, - "pruning_scope": pruning_scope, - "pruning_frequency": pruning_frequency, - "min_sparsity_ratio_per_op": min_sparsity_ratio_per_op, - "max_sparsity_ratio_per_op": max_sparsity_ratio_per_op, - "sparsity_decay_type": sparsity_decay_type, - "pruning_op_types": pruning_op_types, - } - ) - self._weight_compression.update(kwargs) +from neural_compressor.utils import logger +from neural_compressor.utils.utility import DotDict - @property - def weight_compression(self): - """Get weight_compression.""" - return self._weight_compression +from ...config import WeightPruningConfig as WeightPruningConf +from ...utils.utility import LazyImport - @weight_compression.setter - def weight_compression(self, weight_compression): - """Set weight_compression.""" - self._weight_compression = weight_compression +torch = LazyImport("torch") +nn = LazyImport("torch.nn") +F = LazyImport("torch.nn.functional") +tf = LazyImport("tensorflow") def get_sparsity_ratio(pruners, model): @@ -423,14 +353,10 @@ def check_key_validity_prunerv2(template_config, usr_cfg_dict): for obj in user_config: if isinstance(obj, dict): check_key_validity_dict(template_config, obj) - elif isinstance(obj, PrunerV2): - check_key_validity_prunerv2(template_config, obj) # single pruner, weightconfig or yaml elif isinstance(user_config, dict): check_key_validity_dict(template_config, user_config) - elif isinstance(user_config, PrunerV2): - check_key_validity_prunerv2(template_config, user_config) return @@ -470,7 +396,7 @@ def process_and_check_config(val): default_config.update(default_global_config) default_config.update(default_local_config) default_config.update(params_default_config) - if isinstance(val, WeightPruningConfig) or isinstance(val, WeightPruningConf): + if isinstance(val, WeightPruningConf): global_configs = val.weight_compression pruning_configs = val.pruning_configs check_key_validity(default_config, pruning_configs) @@ -494,21 +420,7 @@ def process_config(config): Returns: A config dict object. """ - if isinstance(config, str): - try: - with open(config, "r") as f: - content = f.read() - val = yaml.safe_load(content) - ##schema.validate(val) - return process_and_check_config(val) - except FileNotFoundError as f: - logger.error("{}.".format(f)) - raise RuntimeError("The yaml file is not exist. Please check the file name or path.") - except Exception as e: - logger.error("{}.".format(e)) - raise RuntimeError("The yaml file format is not correct. Please refer to document.") - - if isinstance(config, WeightPruningConfig) or isinstance(config, WeightPruningConf): + if isinstance(config, WeightPruningConf): return process_and_check_config(config) else: assert False, f"not supported type {config}" @@ -618,25 +530,6 @@ def parse_to_prune_tf(config, model): return new_modules -def generate_pruner_config(info): - """Generate pruner config object from prune information. - - Args: - info: A dotdict that saves prune information. - - Returns: - pruner: A pruner config object. - """ - return Pruner( - initial_sparsity=0, - method=info.method, - target_sparsity=info.target_sparsity, - start_epoch=info.start_step, - end_epoch=info.end_step, - update_frequency=info.pruning_frequency, - ) - - def get_layers(model): """Get each layer's name and its module. diff --git a/neural_compressor/conf/README.md b/neural_compressor/conf/README.md deleted file mode 100644 index 72d456555d7..00000000000 --- a/neural_compressor/conf/README.md +++ /dev/null @@ -1,133 +0,0 @@ -Introduction -========================================= - -User can specify yaml configuration file to control the entire tuning behavior. - -Take peleenet model as an example, you will see many repeated but similar items in quantization.op_wise. - -```yaml -quantization: - calibration: - sampling_size: 256 - dataloader: - batch_size: 256 - dataset: - ImageFolder: - root: /path/to/calibration/dataset - transform: - RandomResizedCrop: - size: 224 - RandomHorizontalFlip: - ToTensor: - Normalize: - mean: [0.485, 0.456, 0.406] - std: [0.229, 0.224, 0.225] - op_wise: { - 'module.features.stemblock.f_cat': { - 'weight': {'dtype': ['fp32']}, - }, - 'module.features.denseblock1.denselayer1.f_cat': { - 'weight': {'dtype': ['fp32']}, - }, - 'module.features.denseblock1.denselayer2.f_cat': { - 'weight': {'dtype': ['fp32']}, - }, - 'module.features.denseblock1.denselayer3.f_cat': { - 'weight': {'dtype': ['fp32']}, - }, - 'module.features.denseblock2.denselayer1.f_cat': { - 'weight': {'dtype': ['fp32']}, - }, - 'module.features.denseblock2.denselayer2.f_cat': { - 'weight': {'dtype': ['fp32']}, - }, - 'module.features.denseblock2.denselayer3.f_cat': { - 'weight': {'dtype': ['fp32']}, - }, - 'module.features.denseblock2.denselayer4.f_cat': { - 'weight': {'dtype': ['fp32']}, - }, - 'module.features.denseblock3.denselayer1.f_cat': { - 'weight': {'dtype': ['fp32']}, - }, - 'module.features.denseblock3.denselayer2.f_cat': { - 'weight': {'dtype': ['fp32']}, - }, - 'module.features.denseblock3.denselayer3.f_cat': { - 'weight': {'dtype': ['fp32']}, - }, - 'module.features.denseblock3.denselayer4.f_cat': { - 'weight': {'dtype': ['fp32']}, - }, - 'module.features.denseblock3.denselayer5.f_cat': { - 'weight': {'dtype': ['fp32']}, - }, - 'module.features.denseblock3.denselayer6.f_cat': { - 'weight': {'dtype': ['fp32']}, - }, - 'module.features.denseblock3.denselayer7.f_cat': { - 'weight': {'dtype': ['fp32']}, - }, - 'module.features.denseblock3.denselayer8.f_cat': { - 'weight': {'dtype': ['fp32']}, - }, - 'module.features.denseblock4.denselayer1.f_cat': { - 'weight': {'dtype': ['fp32']}, - }, - 'module.features.denseblock4.denselayer2.f_cat': { - 'weight': {'dtype': ['fp32']}, - }, - 'module.features.denseblock4.denselayer3.f_cat': { - 'weight': {'dtype': ['fp32']}, - }, - 'module.features.denseblock4.denselayer4.f_cat': { - 'weight': {'dtype': ['fp32']}, - }, - 'module.features.denseblock4.denselayer5.f_cat': { - 'weight': {'dtype': ['fp32']}, - }, - 'module.features.denseblock4.denselayer6.f_cat': { - 'weight': {'dtype': ['fp32']}, - }, - } -``` - -Because config module supports parsing regular expression, so the above content can be simplified to: - -```yaml -quantization: - calibration: - sampling_size: 256 - dataloader: - batch_size: 256 - dataset: - ImageFolder: - root: /path/to/calibration/dataset - transform: - RandomResizedCrop: - size: 224 - RandomHorizontalFlip: - ToTensor: - Normalize: - mean: [0.485, 0.456, 0.406] - std: [0.229, 0.224, 0.225] - op_wise: { - 'module.features.stemblock.f_cat': { - 'weight': {'dtype': ['fp32']}, - }, - 'module.features.denseblock1.denselayer[1-3].f_cat': { - 'weight': {'dtype': ['fp32']}, - }, - 'module.features.denseblock1.denselayer[1-4].f_cat': { - 'weight': {'dtype': ['fp32']}, - }, - 'module.features.denseblock1.denselayer[1-8].f_cat': { - 'weight': {'dtype': ['fp32']}, - }, - 'module.features.denseblock1.denselayer[1-6].f_cat': { - 'weight': {'dtype': ['fp32']}, - } - } -``` - -> Note that you can use other standard regular expression. diff --git a/neural_compressor/conf/__init__.py b/neural_compressor/conf/__init__.py deleted file mode 100644 index 5193c828c62..00000000000 --- a/neural_compressor/conf/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/neural_compressor/conf/config.py b/neural_compressor/conf/config.py deleted file mode 100644 index f7b224028d6..00000000000 --- a/neural_compressor/conf/config.py +++ /dev/null @@ -1,1733 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import copy -import datetime -import itertools -import os -import re -from collections import OrderedDict - -import yaml -from schema import And, Hook, Optional, Or, Schema, Use - -from ..adaptor import FRAMEWORKS -from ..objective import OBJECTIVES -from ..utils import logger -from ..version import __version__ -from .dotdict import DotDict, deep_set - -# TODO WA for avoid circular import -# from ..experimental.strategy import EXP_STRATEGIES -EXP_STRATEGIES = ['basic', 'auto_mixed_precision', 'bayesian', 'conservative',\ - 'exhaustive', 'hawq_v2', 'mse', 'mse_v2', 'random', 'sigopt', 'tpe', 'fake'] - -def constructor_register(cls): - yaml_key = "!{}".format(cls.__name__) - - def constructor(loader, node): - instance = cls.__new__(cls) - yield instance - - state = loader.construct_mapping(node, deep=True) - instance.__init__(**state) - - yaml.add_constructor( - yaml_key, - constructor, - yaml.SafeLoader, - ) - return cls - -@constructor_register -class Pruner(): - def __init__(self, start_epoch=None, end_epoch=None, initial_sparsity=None, - target_sparsity=None, update_frequency=1, - method='per_tensor', - prune_type='basic_magnitude',##for pytorch pruning, these values should be None - start_step=None, end_step=None, update_frequency_on_step=None, prune_domain=None, - sparsity_decay_type=None, pattern="tile_pattern_1x1", names=None, - extra_excluded_names=None, parameters=None): - self.start_epoch = start_epoch - self.end_epoch = end_epoch - self.update_frequency = update_frequency - self.target_sparsity = target_sparsity - self.initial_sparsity = initial_sparsity - self.update_frequency = update_frequency - self.start_step = start_step - self.end_step = end_step - self.update_frequency_on_step = update_frequency_on_step - self.prune_domain = prune_domain - self.sparsity_decay_type = sparsity_decay_type - self.extra_excluded_names = extra_excluded_names - self.pattern = pattern - ## move this to experimental/pruning to support dynamic pruning - # assert prune_type.replace('_', '') in [i.lower() for i in PRUNERS], \ - # 'now only support {}'.format(PRUNERS.keys()) - self.prune_type = prune_type - self.method = method - self.names= names - self.parameters = parameters - - -@constructor_register -class PrunerV2: - """Similar to torch optimizer's interface.""" - - def __init__(self, - target_sparsity=None, pruning_type=None, pattern=None, op_names=None, - excluded_op_names=None, - start_step=None, end_step=None, pruning_scope=None, pruning_frequency=None, - min_sparsity_ratio_per_op=None, max_sparsity_ratio_per_op=None, - sparsity_decay_type=None, pruning_op_types=None, reg_type=None, - criterion_reduce_type=None, parameters=None, resume_from_pruned_checkpoint=None): - self.pruner_config = DotDict({ - 'target_sparsity': target_sparsity, - 'pruning_type': pruning_type, - 'pattern': pattern, - 'op_names': op_names, - 'excluded_op_names': excluded_op_names, ##global only - 'start_step': start_step, - 'end_step': end_step, - 'pruning_scope': pruning_scope, - 'pruning_frequency': pruning_frequency, - 'min_sparsity_ratio_per_op': min_sparsity_ratio_per_op, - 'max_sparsity_ratio_per_op': max_sparsity_ratio_per_op, - 'sparsity_decay_type': sparsity_decay_type, - 'pruning_op_types': pruning_op_types, - 'reg_type': reg_type, - 'criterion_reduce_type': criterion_reduce_type, - 'parameters': parameters, - 'resume_from_pruned_checkpoint': resume_from_pruned_checkpoint - }) - - -# Schema library has different loading sequence priorities for different -# value types. -# To make sure the fields under dataloader.transform field of yaml file -# get loaded with written sequence, this workaround is used to convert -# None to {} in yaml load(). -yaml.SafeLoader.add_constructor('tag:yaml.org,2002:null', lambda loader, node: {}) -# Add python tuple support because best_configure.yaml may contain tuple -yaml.SafeLoader.add_constructor('tag:yaml.org,2002:python/tuple', - lambda loader, node: tuple(loader.construct_sequence(node))) - -def _valid_accuracy_field(key, scope, error): - assert bool( - 'relative' in scope['accuracy_criterion']) != bool( - 'absolute' in scope['accuracy_criterion']) - -def _valid_prune_epoch(key, scope, error): - if "start_epoch" in scope[key] and "end_epoch" in scope[key]: - assert scope[key]["start_epoch"] <= scope[key]["end_epoch"] - -def _valid_prune_sparsity(key, scope, error): - if "initial_sparsity" in scope[key] and "target_sparsity" in scope[key]: - assert scope[key]["initial_sparsity"] <= scope[key]["target_sparsity"] - elif "initial_sparsity" in scope[key]: - assert scope[key]["initial_sparsity"] >= 0 - elif "target_sparsity" in scope[key]: - assert scope[key]["target_sparsity"] < 1 - -def _valid_multi_objectives(key, scope, error): - if 'weight' in scope[key] and scope[key]['weight'] is not None: - assert len(scope[key]['objective']) == len(scope[key]['weight']) - -def _valid_multi_metrics(key, scope, error): - if 'metric' in scope and 'multi_metrics' in scope: - assert False - -def _valid_metric_length(key, scope, error): - metrics = [i for i in scope[key] if i != 'weight' and i != 'higher_is_better'] - if 'weight' in scope[key] and scope[key]['weight'] is not None: - assert len(input_to_list_float(scope[key]['weight'])) == len(metrics) - if 'higher_is_better' in scope[key] and scope[key]['higher_is_better'] is not None: - assert len(input_to_list_bool(scope[key]['higher_is_better'])) == len(metrics) - -# used for '123.68 116.78 103.94' style to float list -def input_to_list_float(data): - if isinstance(data, str): - return [float(s.strip()) for s in data.split()] - - if isinstance(data, float): - return [data] - - assert isinstance(data, list) - return [float(d) for d in data] - -def input_to_list_bool(data): - if isinstance(data, str): - if ',' in data: - return [s.strip() == 'True' for s in data.split(',')] - else: - return [s.strip() == 'True' for s in data.split()] - - if isinstance(data, bool): - return [data] - - assert isinstance(data, list) and all([isinstance(i, bool) for i in data]) - return data - -def input_int_to_float(data): - if isinstance(data, str): - # used for '123.68, 116.78, 103.94' style - if ',' in data: - data = data.split(',') - # used for '123.68 116.78 103.94' style - else: - data = data.split() - - if len(data) == 1: - return float(data[0].strip()) - else: - return [float(s.strip()) for s in data] - elif isinstance(data, list): - return [float(s) for s in data] - elif isinstance(data, int): - return float(data) - -def input_to_list_int(data): - if isinstance(data, str): - return [int(s.strip()) for s in data.split(',')] - - if isinstance(data, int): - return [data] - - assert isinstance(data, list) - return [int(d) for d in data] - -def input_to_list(data): - if isinstance(data, str): - if ',' in data: - return [s.strip() for s in data.split(',')] - - return [s.strip() for s in data.split()] - - if isinstance(data, int): - return [data] - - assert isinstance(data, list) - return data - -def list_to_tuple(data): - if isinstance(data, str): - return tuple([int(s.strip()) for s in data.split(',')]) - - elif isinstance(data, list): - if isinstance(data[0], list): - result = [] - for item in data: - result.append(tuple([int(s) for s in item])) - return result - else: - return tuple([int(s) for s in data]) - -def percent_to_float(data): - if isinstance(data, str) and re.match(r'-?\d+(\.\d+)?%', data): - data = float(data.strip('%')) / 100 - if isinstance(data, int): - data = float(data) - else: - assert isinstance(data, float), 'This field should be float, int or percent string' - return data - -ops_schema = Schema({ - Optional('weight', default=None): { - Optional('granularity'): And( - list, - lambda s: all(i in ['per_channel', 'per_tensor'] for i in s)), - Optional('scheme'): And( - list, - # asym_float are only for PyTorch framework - lambda s: all(i in ['asym', 'sym', 'asym_float'] for i in s)), - Optional('dtype'): And( - list, - lambda s: all(i in ['int8', 'uint8', 'fp32', 'bf16', 'fp16'] for i in s)), - Optional('algorithm'): And( - list, - lambda s: all(i in ['minmax'] for i in s)), - Optional('bit'): And( - Or(float, list), - Use(input_to_list_float), - lambda s: all(0.0 < i <= 7.0 for i in s)) - }, - Optional('activation', default=None): { - Optional('granularity'): And( - list, - lambda s: all(i in ['per_channel', 'per_tensor'] for i in s)), - Optional('scheme'): And( - list, - lambda s: all(i in ['asym', 'sym'] for i in s)), - Optional('dtype'): And( - list, - lambda s: all(i in ['int8', 'uint8', 'fp32', 'bf16', 'fp16'] for i in s)), - # compute_dtypeis only for PyTorch framework - Optional('compute_dtype', default=['uint8']): And( - list, - lambda s: all(i in ['int8', 'uint8', 'fp32', 'bf16', 'None'] for i in s)), - # placeholder are only for PyTorch framework - Optional('algorithm'): And( - list, - lambda s: all(i in ['minmax', 'kl', 'placeholder', 'percentile'] for i in s)) - } -}) - -graph_optimization_schema = Schema({ - - Optional('precisions', default={'precisions': ['fp32']}): And( - Or(str, list), - Use(input_to_list), - lambda s: all(i in [ 'fp32', 'bf16'] for i in s)), - - Optional('op_wise', default={'weight': {}, 'activation': {}}): { - Optional('weight', default=None): { - Optional('dtype', default=None): And( - Or(str, list), - Use(input_to_list), - lambda s: all(i in ['fp32', 'bf16', 'fp16'] for i in s)), - }, - Optional('activation', default=None): { - Optional('dtype', default=None): And( - Or(str, list), - Use(input_to_list), - lambda s: all(i in ['fp32', 'bf16', 'fp16'] for i in s)), - } - } -}) - -mixed_precision_schema = Schema({ - - Optional('precisions', default={'precisions': ['fp32']}): And( - Or(str, list), - Use(input_to_list), - lambda s: all(i in [ 'fp32', 'bf16', 'fp16'] for i in s)), - - Optional('op_wise', default={'weight': {}, 'activation': {}}): { - Optional('weight', default=None): { - Optional('dtype', default=None): And( - Or(str, list), - Use(input_to_list), - lambda s: all(i in ['fp32', 'bf16', 'fp16'] for i in s)), - }, - Optional('activation', default=None): { - Optional('dtype', default=None): And( - Or(str, list), - Use(input_to_list), - lambda s: all(i in ['fp32', 'bf16', 'fp16'] for i in s)), - } - } -}) - -model_conversion_schema = Schema({ - 'source': And(str, lambda s: s.lower() == 'qat'), - 'destination': And(str, lambda s: s.lower() == 'default') -}) - -filter_schema = Schema({ - Optional('LabelBalance'): { - 'size': And(int, lambda s: s > 0) - }, -}) - -transform_schema = Schema({ - Optional('ResizeWithRatio'):{ - Optional('min_dim'): int, - Optional('max_dim'): int, - Optional('padding'): bool, - Optional('constant_value'): int - }, - Optional('CropToBoundingBox'): { - 'offset_height': int, - 'offset_width': int, - 'target_height': int, - 'target_width': int - }, - Optional('Cast'): { - Optional('dtype'): str - }, - Optional('RandomResizedCrop'): { - 'size': Or(And(list, lambda s: all(isinstance(i, int) for i in s)), - And(int, lambda s: s > 0)), - Optional('scale'): And(list, lambda s: all(isinstance(i, float) for i in s)), - Optional('ratio'): And(list, lambda s: all(isinstance(i, float) for i in s)), - Optional('interpolation'): And( - str, - lambda s: s in ['nearest', 'bilinear', 'bicubic']), - }, - Optional('AlignImageChannel'): { - Optional('dim'): int - }, - Optional('ToNDArray'): Or({}, None), - Optional('CropResize'): { - 'x': int, - 'y': int, - 'width': int, - 'height': int, - 'size': Or(And(list, lambda s: all(isinstance(i, int) for i in s)), - And(int, lambda s: s > 0)), - Optional('interpolation'): And( - str, - lambda s: s in ['nearest', 'bilinear', 'bicubic']), - }, - Optional('RandomHorizontalFlip'): Or({}, None), - Optional('RandomVerticalFlip'): Or({}, None), - Optional('ToTensor'): Or({}, None), - Optional('ToPILImage'): Or({}, None), - Optional('Normalize'): { - Optional('mean'): And(list, lambda s: all(isinstance(i, float) for i in s)), - Optional('std'): And(list, lambda s: all(isinstance(i, float) for i in s)), - Optional('rescale'): list - }, - Optional('Resize'): { - 'size': Or(And(list, lambda s: all(isinstance(i, int) for i in s)), - And(int, lambda s: s > 0)), - Optional('interpolation'): And( - str, - lambda s: s in ['nearest', 'bilinear', 'bicubic']), - }, - Optional('RandomCrop'): { - 'size': Or(And(list, lambda s: all(isinstance(i, int) for i in s)), - And(int, lambda s: s > 0)) - }, - Optional('Rescale'): Or({}, None), - Optional('CenterCrop'): { - 'size': Or(And(list, lambda s: all(isinstance(i, int) for i in s)), - And(int, lambda s: s > 0)) - }, - Optional('PaddedCenterCrop'): { - 'size': Or(And(list, lambda s: all(isinstance(i, int) for i in s)), - And(int, lambda s: s > 0)), - Optional('crop_padding'): And(int, lambda s: s > 0), - }, - Optional('ToArray'): Or({}, None), - Optional('QuantizedInput'): { - Optional('dtype', default='int8'): And(str, lambda s: s in ['int8', 'uint8']), - Optional('scale'): And(float, lambda s: s > 0), - }, - Optional('Transpose'): { - 'perm': And(list, lambda s: all(isinstance(i, int) for i in s)), - }, - # THIS API IS TO BE DEPRECATED! - Optional('ParseDecodeImagenet'): Or({}, None), - Optional('ParseDecodeCoco'): Or({}, None), - Optional('ParseDecodeVoc'): Or({}, None), - Optional('BilinearImagenet'): { - 'height': And(int, lambda s: s > 0), - 'width': And(int, lambda s: s > 0), - Optional('central_fraction'): float, - Optional('mean_value'): And(Or(str, list), Use(input_to_list_float)), - Optional('scale'): float, - }, - Optional('ResizeCropImagenet'): { - 'height': And(int, lambda s: s > 0), - 'width': And(int, lambda s: s > 0), - Optional('random_crop'): bool, - Optional('slice_crop'): bool, - Optional('resize_side'): And(int, lambda s: s > 0), - Optional('resize_method', default='bilinear'): \ - And(str, lambda s: s in ['bilinear', 'lanczos3', 'lanczos5', - 'bicubic', 'gaussian', 'nearest', - 'area', 'mitchellcubic']), - Optional('random_flip_left_right'): bool, - Optional('data_format', default='channels_last'): \ - And(str, lambda s: s in ['channels_first', 'channels_last']), - Optional('subpixels', default='RGB'): \ - And(str, lambda s: s in ['BGR', 'RGB']), - Optional('mean_value'): And(Or(str, list), Use(input_to_list_float)), - Optional('scale'): float, - }, - Optional('ResizeWithAspectRatio'):{ - 'height': And(int, lambda s: s > 0), - 'width': And(int, lambda s: s > 0), - }, - Optional('ParseDecodeImagenet'): Or({}, None), - Optional('ToArray'): Or({}, None), - Optional('QuantizedInput'): { - Optional('dtype', default='int8'): And(str, lambda s: s in ['int8', 'uint8']), - Optional('scale'): And(float, lambda s: s > 0), - }, - Optional('Transpose'): { - 'perm': And(list, lambda s: all(isinstance(i, int) for i in s)), - }, -}) - -postprocess_schema = Schema({ - Optional('LabelShift'): int, - Optional('Collect'): { - 'length': int - }, - Optional('SquadV1'): { - 'label_file': str, - 'vocab_file': str, - Optional('do_lower_case', default='True'): bool, - Optional('max_seq_length', default=384): int, - }, - Optional('SquadV1ModelZoo'): { - 'label_file': str, - 'vocab_file': str, - Optional('do_lower_case', default='True'): bool, - Optional('max_seq_length', default=384): int, - }, -}) - -dataset_schema = Schema({ - Optional('CIFAR10'): { - 'root': str, - Optional('train'): bool, - Optional('download'): bool, - }, - Optional('CIFAR100'): { - 'root': str, - Optional('train'): bool, - Optional('download'): bool, - }, - Optional('MNIST'): { - 'root': str, - Optional('train'): bool, - Optional('download'): bool, - }, - Optional('FashionMNIST'): { - 'root': str, - Optional('train'): bool, - Optional('download'): bool, - }, - Optional('ImageFolder'): { - 'root': str, - }, - Optional('TFRecordDataset'): { - 'root': str, - }, - Optional('ImageRecord'): { - 'root': str, - }, - Optional('dummy_v2'): { - 'input_shape': And(Or(str, list), Use(list_to_tuple)), - Optional('label_shape', default=[1]): And(Or(str, list), Use(list_to_tuple)), - Optional('low'): Or( - float, - And(int, Use(input_int_to_float)), - And(list, Use(input_int_to_float)), - And(str, Use(input_int_to_float))), - Optional('high'): Or( - float, - And(int, Use(input_int_to_float)), - And(list, Use(input_int_to_float)), - And(str, Use(input_int_to_float))), - Optional('dtype'): And(Or(str, list), Use(input_to_list)), - }, - Optional('sparse_dummy_v2'): { - 'dense_shape': And(Or(str, list), Use(list_to_tuple)), - Optional('label_shape', default=[1]): And(Or(str, list), Use(list_to_tuple)), - Optional('sparse_ratio'): Or( - float, - And(list, Use(input_int_to_float)), - And(int, Use(input_int_to_float))), - Optional('low'): Or( - float, - And(int, Use(input_int_to_float)), - And(list, Use(input_int_to_float)), - And(str, Use(input_int_to_float))), - Optional('high'): Or( - float, - And(int, Use(input_int_to_float)), - And(list, Use(input_int_to_float)), - And(str, Use(input_int_to_float))), - Optional('dtype'): And(Or(str, list), Use(input_to_list)), - }, - - Optional('dummy'): { - 'shape': And(Or(str, list), Use(list_to_tuple)), - Optional('low'): Or( - float, - And(int, Use(input_int_to_float)), - And(list, Use(input_int_to_float)), - And(str, Use(input_int_to_float))), - Optional('high'): Or( - float, - And(int, Use(input_int_to_float)), - And(list, Use(input_int_to_float)), - And(str, Use(input_int_to_float))), - Optional('dtype'): And(Or(str, list), Use(input_to_list)), - Optional('label'): bool, - }, - Optional('bert'): { - 'root': str, - 'label_file': str, - Optional('task'): And(str, lambda s: s in ["classifier", "squad"]), - Optional('model_type'): And(str, lambda s: s in ['bert', 'xlnet', 'xlm']), - }, - Optional('mzbert'): { - 'root': str, - 'label_file': str, - Optional('task'): And(str, lambda s: s in ["classifier", "squad"]), - Optional('model_type'): And(str, lambda s: s in ['bert', 'xlnet', 'xlm']), - }, - Optional('VOCRecord'): { - 'root': str, - }, - Optional('COCORecord'): { - 'root': str, - Optional('num_cores'): int, - }, - Optional('COCORaw'): { - 'root': str, - Optional('img_dir'): str, - Optional('anno_dir'): str, - Optional('num_cores'): int, - }, - Optional('COCONpy'): { - 'root': str, - 'npy_dir': str, - Optional('anno_dir'): str, - Optional('num_cores'): int, - }, - Optional('ImagenetRaw'): { - 'data_path': str, - Optional('image_list'): str, - }, - Optional('style_transfer'): { - 'content_folder': str, - 'style_folder': str, - Optional('crop_ratio'): float, - Optional('resize_shape'): And(Or(str, list), Use(input_to_list_int)), - Optional('image_format'): str, - }, - Optional('GLUE'): { - 'data_dir': str, - 'model_name_or_path': str, - Optional('max_seq_length'): int, - Optional('do_lower_case'): bool, - Optional('task'): str, - Optional('model_type'): str, - Optional('dynamic_length'): bool, - Optional('evaluate'): bool - }, - # TO BE DEPRECATED! - Optional('Imagenet'): { - 'root': str, - }, -}) - -dataloader_schema = Schema({ - Optional('last_batch', default='rollover'): And(str, lambda s: s in ['rollover', 'discard']), - Optional('batch_size', default=None): And(int, lambda s: s > 0), - 'dataset': dataset_schema, - Optional('filter'): filter_schema, - Optional('transform'): transform_schema, - Optional('shuffle', default = False): And(bool, lambda s: s in [True, False]), - Optional('distributed', default = False): And(bool, lambda s: s in [True, False]), -}) - -configs_schema = Schema({ - Optional('cores_per_instance'): And(int, lambda s: s > 0), - Optional('num_of_instance', default=1): And(int, lambda s: s > 0), - Optional('inter_num_of_threads'): And(int, lambda s: s > 0), - Optional('intra_num_of_threads'): And(int, lambda s: s > 0), - Optional('kmp_blocktime'): And(int, lambda s: s >= 0), - Optional('kmp_affinity', default='granularity=fine,verbose,compact,1,0'): str, -}) - -optimizer_schema = Schema({ - Optional('SGD'): { - 'learning_rate': Use(float), - Optional('momentum'): Use(float), - Optional('nesterov'): bool, - Optional('weight_decay'): Use(float) - }, - Optional('AdamW'): { - 'weight_decay': Use(float), - Optional('learning_rate', default=0.001): Use(float), - Optional('beta_1', default=0.9): Use(float), - Optional('beta_2', default=0.999): Use(float), - Optional('epsilon', default=1e-07): Use(float), - Optional('amsgrad', default=False): bool - }, - Optional('Adam'): { - Optional('learning_rate', default=0.001): Use(float), - Optional('beta_1', default=0.9): Use(float), - Optional('beta_2', default=0.999): Use(float), - Optional('epsilon', default=1e-07): Use(float), - Optional('amsgrad', default=False): bool - }, -}) - -criterion_schema = Schema({ - Optional('CrossEntropyLoss'): { - Optional('reduction', default='mean'): \ - And(str, lambda s: s in ['none', 'sum', 'mean', 'auto', 'sum_over_batch_size']), - Optional('from_logits', default=False): - And(bool, lambda s: s in [True, False]), - }, - Optional('SparseCategoricalCrossentropy'): { - Optional('reduction', default='mean'): \ - And(str, lambda s: s in ['none', 'sum', 'mean', 'auto', 'sum_over_batch_size']), - Optional('from_logits', default=False): - And(bool, lambda s: s in [True, False]), - }, - Optional('KnowledgeDistillationLoss'): { - Optional('temperature'): And(float, lambda s: s > 0), - Optional('loss_types'): And(list, lambda s: all(i in ['CE', 'KL', 'MSE'] for i in s)), - Optional('loss_weights'): And(list, lambda s: all(i >= 0 for i in s) and sum(s) == 1.0), - }, - Optional('IntermediateLayersKnowledgeDistillationLoss'): { - 'layer_mappings': - And(Or(tuple, list), lambda s: all(len(i) in [1, 2] for i in s)), - Optional('loss_types'): - And(Or(tuple, list), lambda s: all(i in ['MSE', 'KL', 'L1'] for i in s)), - Optional('loss_weights'): - And(Or(tuple, list), lambda s: all(i >= 0 for i in s)), - Optional('add_origin_loss'): bool, - }, - Optional('SelfKnowledgeDistillationLoss'): { - 'layer_mappings': - And(Or(tuple, list), lambda s: all(len(i) >= 1 for i in s)), - Optional('loss_types'): - And(Or(tuple, list), lambda s: all(i in ['L2', 'CE', 'KL'] for i in s)), - Optional('loss_weights'): - And(Or(tuple, list), lambda s: all(i >= 0.0 and i < 1.0 for i in s)), - Optional('add_origin_loss'): bool, - Optional('temperature'): And(float, lambda s: s > 0), - } -}) - -train_schema = Schema({ - 'criterion': criterion_schema, - Optional('optimizer', default={'SGD': {'learning_rate': 0.001}}): optimizer_schema, - Optional('dataloader'): dataloader_schema, - Optional('epoch', default=1): int, - Optional('start_epoch', default=0): int, - Optional('end_epoch'): int, - Optional('iteration'): int, - Optional('frequency'): int, - Optional('execution_mode', default='eager'): And(str, lambda s: s in ['eager', 'graph']), - Optional('postprocess'): { - Optional('transform'): postprocess_schema - }, - # TODO reserve for multinode training support - Optional('hostfile'): str -}) - -weight_compression_schema = Schema({ - Optional('initial_sparsity', default=0): And(float, lambda s: s < 1.0 and s >= 0.0), - Optional('target_sparsity', default=0.97): float, - Optional('max_sparsity_ratio_per_layer', default=0.98):float, - Optional('prune_type', default="basic_magnitude"): str, - Optional('start_epoch', default=0): int, - Optional('end_epoch', default=4): int, - Optional('start_step', default=0): int, - Optional('end_step', default=0): int, - Optional('update_frequency', default=1.0): float, - Optional('update_frequency_on_step', default=1):int, - Optional('excluded_names', default=[]):list, - Optional('prune_domain', default="global"): str, - Optional('names', default=[]): list, - Optional('extra_excluded_names', default=None): list, - Optional('prune_layer_type', default=None): list, - Optional('sparsity_decay_type', default="exp"): str, - Optional('pattern', default="tile_pattern_1x1"): str, - - Optional('pruners'): And(list, \ - lambda s: all(isinstance(i, Pruner) for i in s)) -}) - -weight_compression_v2_schema = Schema({ - Optional('target_sparsity', default=0.9): float, - Optional('pruning_type', default="snip_momentum"): str, - Optional('pattern', default="4x1"): str, - Optional('op_names', default=[]): list, - Optional('excluded_op_names', default=[]): list, - Optional('start_step', default=0): int, - Optional('end_step', default=0): int, - Optional('pruning_scope', default="global"): str, - Optional('pruning_frequency', default=1): int, - Optional('min_sparsity_ratio_per_op', default=0.0): float, - Optional('max_sparsity_ratio_per_op', default=0.98): float, - Optional('sparsity_decay_type', default="exp"): str, - Optional('pruning_op_types', default=['Conv', 'Linear']): list, - Optional('reg_type', default=None): str, - Optional('criterion_reduce_type', default="mean"): str, - Optional('parameters', default={"reg_coeff": 0.0}): dict, - Optional('resume_from_pruned_checkpoint', default=False): str, - Optional('pruners'): And(list, \ - lambda s: all(isinstance(i, Pruner) for i in s)) -}) - - -# weight_compression_pytorch_schema = Schema({},ignore_extra_keys=True) - -approach_schema = Schema({ - Hook('weight_compression', handler=_valid_prune_sparsity): object, - Hook('weight_compression_pytorch', handler=_valid_prune_sparsity): object, - Optional('weight_compression'): weight_compression_schema, - Optional('weight_compression_v2'): weight_compression_v2_schema, - Optional('weight_compression_pytorch'): weight_compression_schema, -}) - -default_workspace = './nc_workspace/{}/'.format( - datetime.datetime.now().strftime('%Y-%m-%d_%H-%M-%S')) - -COCOmAP_input_order_schema = Schema({ - Optional('num_detections'): int, - 'boxes': int, - 'scores': int, - 'classes': int -}) - -schema = Schema({ - 'model': { - 'name': str, - 'framework': And(str, lambda s: s in list(FRAMEWORKS.keys()) + ['NA']), - Optional('inputs', default=[]): And(Or(str, list), Use(input_to_list)), - Optional('outputs', default=[]): And(Or(str, list), Use(input_to_list)), - }, - Optional('version', default=float(__version__.split('.')[0])): Or(float, - And(int, Use(input_int_to_float)), - And(str, Use(input_int_to_float))), - Optional('device', default='cpu'): And(str, lambda s: s in ['cpu', 'gpu']), - Optional('quantization', default={'approach': 'post_training_static_quant', \ - 'calibration': {'sampling_size': [100]}, \ - 'recipes': {'scale_propagation_max_pooling': True, - 'scale_propagation_concat': True, - 'first_conv_or_matmul_quantization': True, - 'last_conv_or_matmul_quantization': True, - 'pre_post_process_quantization': True}, - 'model_wise': {'weight': {'bit': [7.0]}, - 'activation': {}}, - 'quant_level': "auto", - }): { - Optional('approach', default='post_training_static_quant'): And( - str, - # TODO check if framework support dynamic quantize - # Now only onnruntime and pytorch support - lambda s: s in ['post_training_static_quant', - 'post_training_dynamic_quant', - 'post_training_auto_quant', - 'quant_aware_training']), - Optional('train', default=None): train_schema, - Optional('advance', default=None): { - Optional('bias_correction'): And(str, lambda s: s in ['weight_empirical']), - }, - Optional('calibration', default={'sampling_size': [100]}): { - Optional('sampling_size', default=[100]): And(Or(str, int, list), Use(input_to_list)), - Optional('dataloader', default=None): dataloader_schema - }, - Optional('recipes', default={'scale_propagation_max_pooling': True, - 'scale_propagation_concat': True, - 'first_conv_or_matmul_quantization': True, - 'last_conv_or_matmul_quantization': True, - 'pre_post_process_quantization': True}): { - Optional('scale_propagation_max_pooling', default=True): - And(bool, lambda s: s in [True, False]), - Optional('scale_propagation_concat', default=True): - And(bool, lambda s: s in [True, False]), - Optional('first_conv_or_matmul_quantization', default=True): - And(bool, lambda s: s in [True, False]), - Optional('last_conv_or_matmul_quantization', default=True): - And(bool, lambda s: s in [True, False]), - Optional('pre_post_process_quantization', default=True): - And(bool, lambda s: s in [True, False]), - Optional('fast_bias_correction', default=False): - And(bool, lambda s: s in [True, False]), - Optional('weight_correction', default=False): - And(bool, lambda s: s in [True, False]), - }, - Optional('model_wise', default={'weight': {'bit': [7.0]}, 'activation': {}}): { - Optional('weight', default= {'bit': [7.0]}): { - Optional('granularity', default=None): And( - Or(str, list), - Use(input_to_list), - lambda s: all(i in ['per_channel', 'per_tensor'] for i in s)), - # asym_float are only for PyTorch framework - Optional('scheme', default=None): And( - Or(str, list), - Use(input_to_list), - lambda s: all(i in ['asym', 'sym', 'asym_float'] for i in s)), - Optional('dtype', default=None): And( - Or(str, list), - Use(input_to_list), - lambda s: all(i in ['int8', 'uint8', 'fp32', 'bf16', 'fp16'] for i in s)), - Optional('algorithm', default=None): And( - Or(str, list), - Use(input_to_list), - lambda s: all(i in ['minmax'] for i in s)), - Optional('bit', default=[7.0]): And( - Or(float, list), - Use(input_to_list_float), - lambda s: all(0.0 < i <= 7.0 for i in s)) - - }, - Optional('activation', default=None): { - Optional('granularity', default=None): And( - Or(str, list), - Use(input_to_list), - lambda s: all(i in ['per_channel', 'per_tensor'] for i in s)), - Optional('scheme', default=None): And( - Or(str, list), - Use(input_to_list), - lambda s: all(i in ['asym', 'sym'] for i in s)), - Optional('dtype', default=None): And( - Or(str, list), - Use(input_to_list), - lambda s: all(i in ['int8', 'uint8', 'fp32', 'bf16', 'fp16'] for i in s)), - # compute_dtypeis only for PyTorch framework - Optional('compute_dtype', default=['uint8']): And( - Or(str, list), - Use(input_to_list), - lambda s: all(i in ['int8', 'uint8', 'fp32', 'bf16', 'None'] for i in s)), - # placeholder are only for PyTorch framework - Optional('algorithm', default=None): And( - Or(str, list), - Use(input_to_list), - lambda s: all(i in ['minmax', 'kl', 'placeholder', 'percentile'] for i in s)), - } - }, - Optional('optype_wise', default=None): { - str: ops_schema - }, - Optional('op_wise', default=None): { - str: ops_schema - }, - Optional('quant_level', default="auto"): And(Or(str, int), lambda level: level in ["auto", 0, 1]), - }, - Optional('use_bf16', default=True): bool, - Optional('graph_optimization'): graph_optimization_schema, - Optional('mixed_precision'): mixed_precision_schema, - - Optional('model_conversion'): model_conversion_schema, - - Optional('tuning', default={ - 'strategy': {'name': 'basic'}, - 'accuracy_criterion': {'relative': 0.01, 'higher_is_better': True}, - 'objective': 'performance', - 'exit_policy': {'timeout': 0, 'max_trials': 100, 'performance_only': False}, - 'random_seed': 1978, 'tensorboard': False, - 'workspace': {'path': default_workspace}, - 'diagnosis': False, - }): { - Optional('strategy', default={'name': 'basic'}): { - 'name': And(str, lambda s: s in EXP_STRATEGIES), - Optional('sigopt_api_token'): str, - Optional('sigopt_project_id'): str, - Optional('sigopt_experiment_name', default='nc-tune'): str, - Optional('accuracy_weight', default=1.0): float, - Optional('latency_weight', default=1.0): float, - Optional('confidence_batches', default=2): int, - Optional('hawq_v2_loss', default=None): object, - } , - Hook('accuracy_criterion', handler=_valid_accuracy_field): object, - Optional('accuracy_criterion', default={'relative': 0.01}): { - Optional('relative'): And(Or(str, float), Use(percent_to_float)), - Optional('absolute'): And(Or(str, int, float), Use(percent_to_float)), - Optional('higher_is_better', default=True): bool, - }, - Optional('objective', default='performance'): And(str, lambda s: s in OBJECTIVES), - Hook('multi_objectives', handler=_valid_multi_objectives): object, - Optional('multi_objectives'):{ - Optional('objective'): And( - Or(str, list), Use(input_to_list), lambda s: all(i in OBJECTIVES for i in s)), - Optional('weight'): And(Or(str, list), Use(input_to_list_float)), - Optional('higher_is_better'): And( - Or(str, bool, list), Use(input_to_list_bool)), - }, - Optional('exit_policy', default={'timeout': 0, - 'max_trials': 100, - 'performance_only': False}): { - Optional('timeout', default=0): int, - Optional('max_trials', default=100): int, - Optional('performance_only', default=False): bool, - }, - Optional('random_seed', default=1978): int, - Optional('tensorboard', default=False): And(bool, lambda s: s in [True, False]), - Optional('workspace', default={'path': default_workspace}): { - Optional('path', default=None): str, - Optional('resume'): str - }, - Optional('diagnosis', default=False): And(bool, lambda s: s in [True, False]), - }, - Optional('evaluation'): { - Hook('accuracy', handler=_valid_multi_metrics): object, - Optional('accuracy'): { - Hook('multi_metrics', handler=_valid_metric_length): object, - Optional('multi_metrics', default=None): { - Optional('weight'): And(Or(str, list), Use(input_to_list_float)), - Optional('higher_is_better'): And( - Or(str, list), Use(input_to_list_bool)), - Optional('topk'): And(int, lambda s: s in [1, 5]), - Optional('mAP'): { - Optional('anno_path'): str, - Optional('iou_thrs', default=0.5): - Or(And(str, lambda s: s in ['0.5:0.05:0.95']), - And(float, lambda s: s <= 1.0 and s >= 0.0)), - Optional('map_points', default=0): And(int, lambda s: s in [0, 11, 101]) - }, - Optional('COCOmAP'): { - Optional('anno_path'): str, - Optional('map_key', default='DetectionBoxes_Precision/mAP'): str - }, - Optional('COCOmAPv2'): { - Optional('anno_path'): str, - Optional('map_key', default='DetectionBoxes_Precision/mAP'): str, - Optional('output_index_mapping', default={'num_detections': -1, - 'boxes': 0, - 'scores': 1, - 'classes': 2}): COCOmAP_input_order_schema - }, - Optional('VOCmAP'): { - Optional('anno_path'): str - }, - Optional('SquadF1'): Or({}, None), - Optional('MSE'): { - Optional('compare_label'): bool - }, - Optional('RMSE'): { - Optional('compare_label'): bool - }, - Optional('MAE'): { - Optional('compare_label'): bool - }, - Optional('Accuracy'): Or({}, None), - Optional('Loss'): Or({}, None), - Optional('BLEU'): Or({}, None), - Optional('SquadF1'): Or({}, None), - Optional('F1'): Or({}, None), - Optional('mIOU'): { - Optional('num_classes'): int - }, - Optional('GLUE'): { - Optional('task'): str - }, - Optional('ROC'): { - Optional('task'): str - }, - }, - Optional('metric', default=None): { - Optional('topk'): And(int, lambda s: s in [1, 5]), - Optional('mAP'): { - Optional('anno_path'): str, - Optional('iou_thrs', default=0.5): - Or(And(str, lambda s: s in ['0.5:0.05:0.95']), - And(float, lambda s: s <= 1.0 and s >= 0.0)), - Optional('map_points', default=0): And(int, lambda s: s in [0, 11, 101]) - }, - Optional('COCOmAP'): { - Optional('anno_path'): str, - Optional('map_key', default='DetectionBoxes_Precision/mAP'): str - }, - Optional('COCOmAPv2'): { - Optional('anno_path'): str, - Optional('map_key', default='DetectionBoxes_Precision/mAP'): str, - Optional('output_index_mapping', default={'num_detections': -1, - 'boxes': 0, - 'scores': 1, - 'classes': 2}): COCOmAP_input_order_schema - }, - Optional('VOCmAP'): { - Optional('anno_path'): str - }, - Optional('SquadF1'): Or({}, None), - Optional('MSE'): { - Optional('compare_label'): bool - }, - Optional('RMSE'): { - Optional('compare_label'): bool - }, - Optional('MAE'): { - Optional('compare_label'): bool - }, - Optional('Accuracy'): Or({}, None), - Optional('Loss'): Or({}, None), - Optional('BLEU'): Or({}, None), - Optional('SquadF1'): Or({}, None), - Optional('F1'): Or({}, None), - Optional('mIOU'): { - Optional('num_classes'): int - }, - Optional('GLUE'): { - Optional('task'): str - }, - Optional('ROC'): { - Optional('task'): str - }, - }, - Optional('configs'): configs_schema, - Optional('iteration', default=-1): int, - Optional('dataloader'): dataloader_schema, - Optional('postprocess'): { - Optional('transform'): postprocess_schema - }, - }, - Optional('performance'): { - Optional('warmup', default=5): int, - Optional('iteration', default=-1): int, - Optional('configs'): configs_schema, - Optional('dataloader'): dataloader_schema, - Optional('postprocess'): { - Optional('transform'): postprocess_schema - } - }, - Optional('diagnosis', default=False): And(bool, lambda s: s in [True, False]), - }, - Optional('pruning'): { - Hook('train', handler=_valid_prune_epoch): object, - Optional("train"): train_schema, - Optional("approach"): approach_schema - }, - - Optional('distillation'): { - Optional("train"): train_schema - }, - - Optional('auto_distillation'): { - "search": { - "search_space": dict, - Optional("search_algorithm", default=""): str, - Optional("metrics", default=[]): list, - Optional("higher_is_better", default=[]): list, - Optional("max_trials", default=1): int, - Optional("seed", default=42): int, - }, - Optional("flash_distillation"): { - Optional("knowledge_transfer"): { - Optional("block_names", default=[]): list, - "layer_mappings_for_knowledge_transfer": list, - Optional("loss_types", default=[]): list, - Optional("loss_weights", default=[]): list, - Optional("add_origin_loss", default=[]): list, - Optional("train_steps", default=[]): list, - }, - Optional("regular_distillation"): { - Optional("block_names", default=[]): list, - "layer_mappings_for_knowledge_transfer": list, - Optional("loss_types", default=[]): list, - Optional("loss_weights", default=[]): list, - Optional("add_origin_loss", default=[]): list, - Optional("train_steps", default=[]): list, - }, - }, - }, - - Optional('nas'): { - Optional("approach", default=None): Or(str, None), - Optional("search"): { - Optional("search_space", default=None): Or(dict, None), - Optional("search_algorithm", default=None): Or(str, None), - Optional("metrics", default=None): list, - Optional("higher_is_better", default=None): list, - Optional("max_trials", default=None): int, - Optional("seed", default=42): int, - }, - Optional("dynas"): { - Optional("supernet", default=None): str, - Optional("metrics", default=None): list, - Optional("population", default=50): int, - Optional("num_evals", default=100000): int, - Optional("results_csv_path", default=None): str, - Optional("dataset_path", default=None): str, - Optional("supernet_ckpt_path", default=None): str, - Optional("batch_size", default=64): int, - Optional("eval_batch_size", default=64): int, - Optional("num_workers", default=20): int, - Optional("distributed", default=False): bool, - Optional("test_fraction", default=1.0): float, - }, - }, - - Optional("train"): train_schema -}) - -quantization_default_schema = Schema({ - Optional('model', default={'name': 'default_model_name', \ - 'framework': 'NA', \ - 'inputs': [], 'outputs': []}): dict, - - Optional('version', default=float(__version__.split('.')[0])): str, - - Optional('device', default='cpu'): str, - - Optional('quantization', default={'approach': 'post_training_static_quant', \ - 'calibration': {'sampling_size': [100]}, - 'recipes': {'scale_propagation_max_pooling': True, - 'scale_propagation_concat': True, - 'first_conv_or_matmul_quantization': True, - 'last_conv_or_matmul_quantization': True, - 'pre_post_process_quantization': True}, - 'model_wise': {'weight': {'bit': [7.0]}, - 'activation': {}}, - 'quant_level': "auto", - }): dict, - Optional('use_bf16', default=False): bool, - Optional('tuning', default={ - 'strategy': {'name': 'basic'}, - 'accuracy_criterion': {'relative': 0.01, 'higher_is_better': True}, - 'objective': 'performance', - 'exit_policy': {'timeout': 0, 'max_trials': 100, 'performance_only': False}, - 'random_seed': 1978, 'tensorboard': False, - 'workspace': {'path': default_workspace}}): dict, - - Optional('evaluation', default={'accuracy': {'metric': {'topk': 1}}}): dict -}) - -pruning_default_schema = Schema({ - Optional('model', default={'name': 'default_model_name', \ - 'framework': 'NA', \ - 'inputs': [], 'outputs': []}): dict, - - Optional('version', default=float(__version__.split('.')[0])): str, - - Optional('device', default='cpu'): str, - - Optional('use_bf16', default=False): bool, - - Optional('tuning', default={ - 'random_seed': 1978, 'tensorboard': False, - 'workspace': {'path': default_workspace}}): dict, - - Optional('pruning', default={'approach': {'weight_compression':{'initial_sparsity': 0.0, \ - 'target_sparsity': 0.97, 'start_epoch': 0, \ - 'end_epoch': 4}}}): dict, - - Optional('evaluation', default={'accuracy': {'metric': {'topk': 1}}}): dict -}) - -graph_optimization_default_schema = Schema({ - Optional('model', default={'name': 'resnet50', \ - 'framework': 'NA', \ - 'inputs': [], 'outputs': []}): dict, - - Optional('version', default=float(__version__.split('.')[0])): str, - - Optional('device', default='cpu'): str, - - Optional('quantization', default={'approach': 'post_training_static_quant', - 'calibration': {'sampling_size': [100]}, - 'recipes': {'scale_propagation_max_pooling': True, - 'scale_propagation_concat': True, - 'first_conv_or_matmul_quantization': True, - 'last_conv_or_matmul_quantization': True, - 'pre_post_process_quantization': True}, - 'model_wise': {'weight': {'bit': [7.0]}, - 'activation': {}}}): dict, - - Optional('use_bf16', default=False): bool, - - Optional('tuning', default={ - 'strategy': {'name': 'basic'}, - 'accuracy_criterion': {'relative': 0.01, 'higher_is_better': True}, - 'objective': 'performance', - 'exit_policy': {'timeout': 0, 'max_trials': 100, 'performance_only': False}, - 'random_seed': 1978, 'tensorboard': False, - 'workspace': {'path': default_workspace}}): dict, - - Optional('evaluation', default={'accuracy': {'metric': {'topk': 1}}}): dict, - - Optional('graph_optimization', default={'precisions': ['bf16, fp32']}): dict -}) - -mixed_precision_default_schema = Schema({ - Optional('model', default={'name': 'resnet50', \ - 'framework': 'NA', \ - 'inputs': [], 'outputs': []}): dict, - - Optional('version', default=float(__version__.split('.')[0])): str, - - Optional('device', default='cpu'): str, - - Optional('quantization', default={'approach': 'post_training_static_quant', - 'calibration': {'sampling_size': [100]}, - 'recipes': {'scale_propagation_max_pooling': True, - 'scale_propagation_concat': True, - 'first_conv_or_matmul_quantization': True, - 'last_conv_or_matmul_quantization': True, - 'pre_post_process_quantization': True}, - 'model_wise': {'weight': {'bit': [7.0]}, - 'activation': {}}}): dict, - - Optional('use_bf16', default=False): bool, - - Optional('tuning', default={ - 'strategy': {'name': 'basic'}, - 'accuracy_criterion': {'relative': 0.01, 'higher_is_better': True}, - 'objective': 'performance', - 'exit_policy': {'timeout': 0, 'max_trials': 100, 'performance_only': False}, - 'random_seed': 1978, 'tensorboard': False, - 'workspace': {'path': default_workspace}}): dict, - - Optional('evaluation', default={'accuracy': {'metric': {'topk': 1}}}): dict, - - Optional('mixed_precision', default={'precisions': ['bf16, fp32']}): dict -}) - -benchmark_default_schema = Schema({ - Optional('model', default={'name': 'resnet50', \ - 'framework': 'NA', \ - 'inputs': [], 'outputs': []}): dict, - - Optional('version', default=float(__version__.split('.')[0])): str, - - Optional('device', default='cpu'): str, - - Optional('use_bf16', default=False): bool, - - Optional('quantization', default={'approach': 'post_training_static_quant', - 'calibration': {'sampling_size': [100]}, - 'recipes': {'scale_propagation_max_pooling': True, - 'scale_propagation_concat': True, - 'first_conv_or_matmul_quantization': True, - 'last_conv_or_matmul_quantization': True, - 'pre_post_process_quantization': True}, - 'model_wise': {'weight': {'bit': [7.0]}, - 'activation': {}}}): dict, - - Optional('tuning', default={ - 'strategy': {'name': 'basic'}, - 'accuracy_criterion': {'relative': 0.01, 'higher_is_better': True}, - 'objective': 'performance', - 'exit_policy': {'timeout': 0, 'max_trials': 100, 'performance_only': False}, - 'random_seed': 1978, 'tensorboard': False, - 'workspace': {'path': default_workspace}}): dict, - - Optional('evaluation', default={'accuracy': {'metric': {'topk': 1}}}): dict -}) - -distillation_default_schema = Schema({ - Optional('model', default={'name': 'default_model_name', \ - 'framework': 'NA', \ - 'inputs': [], 'outputs': []}): dict, - - Optional('version', default=float(__version__.split('.')[0])): str, - - Optional('device', default='cpu'): str, - - Optional('use_bf16', default=False): bool, - - Optional('tuning', default={ - 'random_seed': 1978, 'tensorboard': False, - 'workspace': {'path': default_workspace}}): dict, - - Optional('distillation', default={ - 'train': {'start_epoch': 0, 'end_epoch': 10, - 'iteration': 1000, 'frequency': 1, - 'optimizer': {'SGD': {'learning_rate': 0.001}}, - 'criterion': {'KnowledgeDistillationLoss': - {'temperature': 1.0, - 'loss_types': ['CE', 'KL'], - 'loss_weights': [0.5, 0.5]}}}}): dict, - - Optional('evaluation', default={'accuracy': {'metric': {'topk': 1}}}): dict - -}) - -class Conf(object): - """Config parser. - - Args: - cfg_fname (string): The path to the configuration file. - """ - def __init__(self, cfg_fname): - assert cfg_fname is not None - self.usr_cfg = DotDict(self._read_cfg(cfg_fname)) - - def _read_cfg(self, cfg_fname): - """Load a config file following yaml syntax. - - Args: - cfg_fname(string): The name of configuration yaml file - """ - try: - with open(cfg_fname, 'r') as f: - content = f.read() - cfg = yaml.safe_load(content) - validated_cfg = schema.validate(cfg) - - # if user yaml doesn't include version field, neural_compressor will write a supported version - # into it. - if 'version' not in cfg: - leading_whitespace = re.search(r"[ \t]*model\s*:", - content).group().split("model")[0] - content = re.sub(r'model\s*:', - 'version: {}\n\n{}model:'.format( - float(__version__.split('.')[0]), - leading_whitespace - ), - content) - with open(cfg_fname, 'w') as f: - f.write(content) - - return validated_cfg - except FileNotFoundError as f: - logger.error("{}.".format(f)) - raise RuntimeError( - "The yaml file is not exist. Please check the file name or path." - ) - except Exception as e: - logger.error("{}.".format(e)) - raise RuntimeError( - "The yaml file format is not correct. Please refer to document." - ) - - def map_pyconfig_to_cfg(self, pythonic_config): - mapping = {} - if pythonic_config.quantization is not None: - mapping.update({ - 'device': pythonic_config.quantization.device, - 'model.inputs': pythonic_config.quantization.inputs, - 'model.outputs': pythonic_config.quantization.outputs, - 'model.backend': pythonic_config.quantization.backend, - 'model.quant_format': pythonic_config.quantization.quant_format, - 'model.domain': pythonic_config.quantization.domain, - 'quantization.recipes': pythonic_config.quantization.recipes, - 'quantization.approach': pythonic_config.quantization.approach, - 'quantization.example_inputs': pythonic_config.quantization.example_inputs, - 'quantization.calibration.sampling_size': - pythonic_config.quantization.calibration_sampling_size, - 'quantization.optype_wise': pythonic_config.quantization.op_type_dict, - 'quantization.op_wise': pythonic_config.quantization.op_name_dict, - 'tuning.strategy.name': pythonic_config.quantization.strategy, - 'tuning.accuracy_criterion.relative': - pythonic_config.quantization.accuracy_criterion.relative, - 'tuning.accuracy_criterion.absolute': - pythonic_config.quantization.accuracy_criterion.absolute, - 'tuning.accuracy_criterion.higher_is_better': - pythonic_config.quantization.accuracy_criterion.higher_is_better, - 'tuning.objective': pythonic_config.quantization.objective, - 'tuning.exit_policy.timeout': pythonic_config.quantization.timeout, - 'tuning.exit_policy.max_trials': pythonic_config.quantization.max_trials, - 'tuning.exit_policy.performance_only': pythonic_config.quantization.performance_only, - 'use_bf16': pythonic_config.quantization.use_bf16, - 'quantization.quant_level': pythonic_config.quantization.quant_level, - 'reduce_range': pythonic_config.quantization.reduce_range - }) - - if pythonic_config.quantization.diagnosis: - mapping.update({ - 'tuning.diagnosis': True, - 'tuning.exit_policy.max_trials': 1, - }) - - if pythonic_config.quantization.strategy_kwargs: - st_kwargs = pythonic_config.quantization.strategy_kwargs - for st_key in ['sigopt_api_token', 'sigopt_project_id', 'sigopt_experiment_name', \ - 'accuracy_weight', 'latency_weight', 'hawq_v2_loss', 'confidence_batches']: - if st_key in st_kwargs: - st_val = st_kwargs[st_key] - mapping.update({'tuning.strategy.' + st_key: st_val}) - - if pythonic_config.distillation is not None: - mapping.update({ - 'distillation.train.criterion': pythonic_config.distillation.criterion, - 'distillation.train.optimizer': pythonic_config.distillation.optimizer, - }) - if pythonic_config.pruning is not None: - mapping.update({ - 'pruning.approach.weight_compression': pythonic_config.pruning.weight_compression, - }) - if pythonic_config.nas is not None: - mapping.update({ - 'nas.approach': pythonic_config.nas.approach, - 'nas.search': pythonic_config.nas.search, - 'nas.dynas': pythonic_config.nas.dynas, - }) - if pythonic_config.options is not None: - mapping.update({ - 'tuning.random_seed': pythonic_config.options.random_seed, - 'tuning.workspace.path': pythonic_config.options.workspace, - 'tuning.workspace.resume': pythonic_config.options.resume_from, - 'tuning.tensorboard': pythonic_config.options.tensorboard, - }) - if pythonic_config.benchmark is not None: - if pythonic_config.benchmark.inputs != []: - mapping.update({'model.inputs': pythonic_config.benchmark.inputs}) - if pythonic_config.benchmark.outputs != []: - mapping.update({'model.outputs': pythonic_config.benchmark.outputs}) - mapping.update({ - 'evaluation.performance.warmup': pythonic_config.benchmark.warmup, - 'evaluation.performance.iteration': pythonic_config.benchmark.iteration, - 'evaluation.performance.configs.cores_per_instance': - pythonic_config.benchmark.cores_per_instance, - 'evaluation.performance.configs.num_of_instance': - pythonic_config.benchmark.num_of_instance, - 'evaluation.performance.configs.inter_num_of_threads': - pythonic_config.benchmark.inter_num_of_threads, - 'evaluation.performance.configs.intra_num_of_threads': - pythonic_config.benchmark.intra_num_of_threads, - 'evaluation.accuracy.configs.cores_per_instance': - pythonic_config.benchmark.cores_per_instance, - 'evaluation.accuracy.configs.num_of_instance': - pythonic_config.benchmark.num_of_instance, - 'evaluation.accuracy.configs.inter_num_of_threads': - pythonic_config.benchmark.inter_num_of_threads, - 'evaluation.accuracy.configs.intra_num_of_threads': - pythonic_config.benchmark.intra_num_of_threads, - }) - if pythonic_config.benchmark.diagnosis: - mapping.update({'evaluation.diagnosis': pythonic_config.benchmark.diagnosis}) - - if "model.backend" not in mapping: - mapping.update({ - 'model.backend': pythonic_config.benchmark.backend, - }) - else: - if mapping['model.backend'] == 'default' and \ - pythonic_config.benchmark.backend != 'default': - mapping.update({ - 'model.backend': pythonic_config.benchmark.backend, - }) - - if "model.backend" not in mapping: - mapping.update({ - 'model.backend': "default", - }) - - for k, v in mapping.items(): - if k in ['tuning.accuracy_criterion.relative', 'tuning.accuracy_criterion.absolute']: - target_key = str(pythonic_config.quantization.accuracy_criterion) - if target_key not in k and 'accuracy_criterion' in self.usr_cfg.tuning: - if target_key in self.usr_cfg.tuning.accuracy_criterion and \ - k.split('.')[-1] in self.usr_cfg.tuning.accuracy_criterion: - self.usr_cfg.tuning.accuracy_criterion.pop(k.split('.')[-1]) - continue - if v is not None: - deep_set(self.usr_cfg, k, v) - - def _convert_cfg(self, src, dst): - """Helper function to merge user defined dict into default dict. - - If the key in src doesn't exist in dst, then add this key and value - pair to dst. - If the key in src is in dst, then override the value in dst with the - value in src. - - Args: - src (dict): The source dict merged from - dst (dict): The source dict merged to - - Returns: - dict: The merged dict from src to dst - """ - for key in src: - if key in dst: - if isinstance(dst[key], dict) and isinstance(src[key], dict): - if key in ['accuracy_criterion', 'metric', 'dataset', - 'criterion', 'optimizer']: - # accuracy_criterion can only have one of absolute and relative - # others can only have one item - inter_key = src[key].keys() & dst[key].keys()-{'higher_is_better'} - if len(inter_key) == 0: - dst[key] = {} - if key == 'accuracy' and src[key].get('multi_metrics', None): - dst[key].pop('metric', None) - self._convert_cfg(src[key], dst[key]) - elif dst[key] == src[key]: - pass # same leaf value - else: - dst[key] = src[key] - elif isinstance(src[key], dict): - dst[key] = DotDict(self._convert_cfg(src[key], {})) - else: - dst[key] = src[key] - return dst - -class Quantization_Conf(Conf): - """Config parser. - - Args: - cfg: The path to the configuration file or DotDict object or None. - """ - - def __init__(self, cfg=None): - if isinstance(cfg, str): - self.usr_cfg = DotDict(self._read_cfg(cfg)) - elif isinstance(cfg, DotDict): - self.usr_cfg = DotDict(schema.validate(self._convert_cfg( - cfg, copy.deepcopy(quantization_default_schema.validate(dict()))))) - else: - self.usr_cfg = DotDict(quantization_default_schema.validate(dict())) - self._model_wise_tune_space = None - self._opwise_tune_space = None - - def _merge_dicts(self, src, dst): - """Helper function to merge src dict into dst dict. - - If the key in src doesn't exist in dst, then skip - If the key in src is in dst and the value intersects with the one in - dst, then override the value in dst with the intersect value. - - Args: - src (dict): The source dict merged from - dst (dict): The source dict merged to - - Returns: - dict: The merged dict from src to dst - """ - for key in src: - if key in dst: - if isinstance(dst[key], dict) and isinstance(src[key], dict): - self._merge_dicts(src[key], dst[key]) - elif dst[key] == src[key] or src[key] is None: - pass # same leaf value - else: - value = [value for value in src[key] - if value in dst[key] or isinstance(value, float)] - if value != []: - dst[key] = value - - return dst - - def modelwise_tune_space(self, model_wise_quant): - cfg = self.usr_cfg - - self._model_wise_tune_space = OrderedDict() - for optype in model_wise_quant.keys(): - if cfg.quantization.optype_wise and optype in cfg.quantization.optype_wise: - self._model_wise_tune_space[optype] = self._merge_dicts( - cfg.quantization.optype_wise[optype], - model_wise_quant[optype]) - else: - self._model_wise_tune_space[optype] = self._merge_dicts( - cfg.quantization.model_wise, - model_wise_quant[optype]) - - return self._model_wise_tune_space - -class Pruning_Conf(Conf): - """Config parser. - - Args: - cfg: The path to the configuration file or DotDict object or None. - """ - - def __init__(self, cfg=None): - if isinstance(cfg, str): - self._read_cfg(cfg) - self.usr_cfg = DotDict(self._read_cfg(cfg)) - elif isinstance(cfg, DotDict): - self.usr_cfg = DotDict(schema.validate(self._convert_cfg( - cfg, copy.deepcopy(pruning_default_schema.validate(dict()))))) - else: - self.usr_cfg = DotDict(pruning_default_schema.validate(dict())) - -class Graph_Optimization_Conf(Quantization_Conf): - """Config parser. - - Args: - cfg: The path to the configuration file or DotDict object or None. - """ - - def __init__(self, cfg=None): - if isinstance(cfg, str): - self.usr_cfg = DotDict(self._read_cfg(cfg)) - elif isinstance(cfg, DotDict): - self.usr_cfg = DotDict(schema.validate(self._convert_cfg( - cfg, copy.deepcopy(graph_optimization_default_schema.validate(dict()))))) - else: - self.usr_cfg = DotDict(graph_optimization_default_schema.validate(dict())) - -class MixedPrecision_Conf(Quantization_Conf): - """Config parser. - - Args: - cfg: The path to the configuration file or DotDict object or None. - """ - - def __init__(self, cfg=None): - if isinstance(cfg, str): - self.usr_cfg = DotDict(self._read_cfg(cfg)) - elif isinstance(cfg, DotDict): - self.usr_cfg = DotDict(self._convert_cfg( - cfg, copy.deepcopy(mixed_precision_default_schema.validate(dict())))) - else: - self.usr_cfg = DotDict(mixed_precision_default_schema.validate(dict())) - -class Benchmark_Conf(Conf): - """Config parser. - - Args: - cfg: The path to the configuration file or DotDict object or None. - """ - - def __init__(self, cfg=None): - if isinstance(cfg, str): - self.usr_cfg = DotDict(self._read_cfg(cfg)) - elif isinstance(cfg, DotDict): - self.usr_cfg = DotDict(schema.validate(self._convert_cfg( - cfg, copy.deepcopy(benchmark_default_schema.validate(dict()))))) - else: - self.usr_cfg = DotDict(benchmark_default_schema.validate(dict())) - -class Distillation_Conf(Conf): - """Config parser. - - Args: - cfg: The path to the configuration file or DotDict object or None. - """ - - def __init__(self, cfg=None): - if isinstance(cfg, str): - self.usr_cfg = DotDict(self._read_cfg(cfg)) - elif isinstance(cfg, DotDict): - self.usr_cfg = DotDict(schema.validate(self._convert_cfg( - cfg, copy.deepcopy(distillation_default_schema.validate(dict()))))) - else: - self.usr_cfg = DotDict(distillation_default_schema.validate(dict())) - -class NASConfig(Conf): - """Config parser. - - Args: - approach: The approach of the NAS. - search_algorithm: The search algorithm for NAS procedure. - """ - - def __init__(self, approach=None, search_space=None, search_algorithm=None): - assert approach is None or (isinstance(approach, str) and approach), \ - "Should set approach with a string." - usr_cfg = { - 'model': {'name': 'nas', 'framework': 'NA'}, - 'nas': {'approach': approach, - 'search': { - 'search_space': search_space, - 'search_algorithm': search_algorithm} - }, - } - self.usr_cfg = DotDict(usr_cfg) - if approach and approach != 'basic': - self.usr_cfg.nas[approach] = DotDict({}) - self.__setattr__(approach, self.usr_cfg.nas[approach]) - - def validate(self): - self.usr_cfg = schema.validate(self.usr_cfg) - - @property - def nas(self): - return self.usr_cfg.nas - -class DefaultConf(DotDict): - def __getitem__(self, key): - if key not in self: - self[key] = DefaultConf({}) - value = self.get(key, None) - return value - - __getattr__ = __getitem__ - -conf = DefaultConf({}) -QuantConf = Quantization_Conf -PruningConf = Pruning_Conf -GraphOptConf = Graph_Optimization_Conf -BenchmarkConf = Benchmark_Conf -DistillationConf = Distillation_Conf -MixedPrecisionConf = MixedPrecision_Conf diff --git a/neural_compressor/conf/dotdict.py b/neural_compressor/conf/dotdict.py deleted file mode 100644 index 6d474dab694..00000000000 --- a/neural_compressor/conf/dotdict.py +++ /dev/null @@ -1,88 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from functools import reduce - - -def deep_get(dictionary, keys, default=None): - """Get the dot key's item in nested dict - eg person = {'person':{'name':{'first':'John'}}} - deep_get(person, "person.name.first") will output 'John'. - - Args: - dictionary (dict): The dict object to get keys - keys (dict): The deep keys - default (object): The return item if key not exists - Returns: - item: the item of the deep dot keys - """ - return reduce(lambda d, key: d.get(key, default) if isinstance(d, dict) else default, keys.split("."), dictionary) - - -def deep_set(dictionary, keys, value): - """Set the dot key's item in nested dict - eg person = {'person':{'name':{'first':'John'}}} - deep_set(person, "person.sex", 'male') will output - {'person': {'name': {'first': 'John'}, 'sex': 'male'}} - - Args: - dictionary (dict): The dict object to get keys - keys (dict): The deep keys - value (object): The value of the setting key - """ - keys = keys.split(".") - for key in keys[:-1]: - dictionary = dictionary.setdefault(key, DotDict()) - dictionary[keys[-1]] = value - - -class DotDict(dict): - """Access yaml using attributes instead of using the dictionary notation. - - Args: - value (dict): The dict object to access. - """ - - def __init__(self, value=None): - if value is None: - pass - elif isinstance(value, dict): - for key in value: - self.__setitem__(key, value[key]) - else: - raise TypeError("expected dict") - - def __getitem__(self, key): - value = self.get(key, None) - return value - - def __setitem__(self, key, value): - if isinstance(value, dict) and not isinstance(value, DotDict): - value = DotDict(value) - if isinstance(value, list) and len(value) == 1 and isinstance(value[0], dict): - value = DotDict(value[0]) - if isinstance(value, list) and len(value) > 1 and all(isinstance(v, dict) for v in value): - value = DotDict({k: v for d in value for k, v in d.items()}) - super(DotDict, self).__setitem__(key, value) - - def __getstate__(self): - return self.__dict__ - - def __setstate__(self, d): - self.__dict__.update(d) - - __setattr__, __getattr__ = __setitem__, __getitem__ diff --git a/neural_compressor/conf/pythonic_config.py b/neural_compressor/conf/pythonic_config.py deleted file mode 100644 index d61cc65b0d2..00000000000 --- a/neural_compressor/conf/pythonic_config.py +++ /dev/null @@ -1,1460 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Configs for Neural Compressor 1.x.""" -import datetime -import logging - -from schema import And, Optional, Schema - -from .dotdict import DotDict - -logger = logging.getLogger("neural_compressor") -default_workspace = './nc_workspace/{}/'.format( - datetime.datetime.now().strftime('%Y-%m-%d_%H-%M-%S')) - - -ops_schema = Schema({ - Optional('weight', default=None): { - Optional('granularity'): And( - list, - lambda s: all(i in ['per_channel', 'per_tensor'] for i in s)), - Optional('scheme'): And( - list, - lambda s: all(i in ['asym', 'sym', 'asym_float'] for i in s)), - Optional('dtype'): And( - list, - lambda s: all(i in ['int8', 'uint8', 'fp32', 'bf16', 'fp16'] for i in s)), - Optional('algorithm'): And( - list, - lambda s: all(i in ['minmax'] for i in s))}, - Optional('activation', default=None): { - Optional('granularity'): And( - list, - lambda s: all(i in ['per_channel', 'per_tensor'] for i in s)), - Optional('scheme'): And( - list, - lambda s: all(i in ['asym', 'sym'] for i in s)), - Optional('dtype'): And( - list, - lambda s: all(i in ['int8', 'uint8', 'fp32', 'bf16', 'fp16', 'None'] for i in s)), - Optional('algorithm'): And( - list, - lambda s: all(i in ['minmax', 'kl', 'placeholder'] for i in s))}}) - - -def _check_value(name, src, supported_type, supported_value=[]): - """Check if the given object is the given supported type and in the given supported value. - - Example:: - - from neural_compressor.config import _check_value - - def datatype(self, datatype): - if _check_value('datatype', datatype, list, ['fp32', 'bf16', 'uint8', 'int8']): - self._datatype = datatype - """ - if isinstance(src, list) and any([not isinstance(i, supported_type) for i in src]): - assert False, ("Type of {} items should be {} but not {}".format( - name, str(supported_type), [type(i) for i in src])) - elif not isinstance(src, list) and not isinstance(src, supported_type): - assert False, ("Type of {} should be {} but not {}".format( - name, str(supported_type), type(src))) - - if len(supported_value) > 0: - if isinstance(src, str) and src not in supported_value: - assert False, ("{} is not in supported {}: {}. Skip setting it.".format( - src, name, str(supported_value))) - elif isinstance(src, list) and all([isinstance(i, str) for i in src]) and \ - any([i not in supported_value for i in src]): - assert False, ("{} is not in supported {}: {}. Skip setting it.".format( - src, name, str(supported_value))) - - return True - - -class Options: - """Option Class for configs. - - This class is used for configuring global variables. The global variable options is created with this class. - If you want to change global variables, you should use functions from utils.utility.py: - set_random_seed(seed: int) - set_workspace(workspace: str) - set_resume_from(resume_from: str) - set_tensorboard(tensorboard: bool) - - Args: - random_seed(int): Random seed used in neural compressor. - Default value is 1978. - workspace(str): The directory where intermediate files and tuning history file are stored. - Default value is: - './nc_workspace/{}/'.format(datetime.datetime.now().strftime('%Y-%m-%d_%H-%M-%S')). - resume_from(str): The directory you want to resume tuning history file from. - The tuning history was automatically saved in the workspace directory - during the last tune process. - Default value is None. - tensorboard(bool): This flag indicates whether to save the weights of the model and the inputs of each layer - for visual display. - Default value is False. - - Example:: - - from neural_compressor import set_random_seed, set_workspace, set_resume_from, set_tensorboard - set_random_seed(2022) - set_workspace("workspace_path") - set_resume_from("workspace_path") - set_tensorboard(True) - """ - def __init__(self, random_seed=1978, workspace=default_workspace, - resume_from=None, tensorboard=False): - """Init an Option object.""" - self.random_seed = random_seed - self.workspace = workspace - self.resume_from = resume_from - self.tensorboard = tensorboard - - @property - def random_seed(self): - """Get random seed.""" - return self._random_seed - - @random_seed.setter - def random_seed(self, random_seed): - """Set random seed.""" - if _check_value('random_seed', random_seed, int): - self._random_seed = random_seed - - @property - def workspace(self): - """Get workspace.""" - return self._workspace - - @workspace.setter - def workspace(self, workspace): - """Set workspace.""" - if _check_value('workspace', workspace, str): - self._workspace = workspace - - @property - def resume_from(self): - """Get resume_from.""" - return self._resume_from - - @resume_from.setter - def resume_from(self, resume_from): - """Set resume_from.""" - if resume_from is None or _check_value('resume_from', resume_from, str): - self._resume_from = resume_from - - @property - def tensorboard(self): - """Get tensorboard.""" - return self._tensorboard - - @tensorboard.setter - def tensorboard(self, tensorboard): - """Set tensorboard.""" - if _check_value('tensorboard', tensorboard, bool): - self._tensorboard = tensorboard - - -class AccuracyCriterion: - """Class of Accuracy Criterion. - - Args: - higher_is_better(bool, optional): This flag indicates whether the metric higher is the better. - Default value is True. - criterion:(str, optional): This flag indicates whether the metric loss is 'relative' or 'absolute'. - Default value is 'relative'. - tolerable_loss(float, optional): This float indicates how much metric loss we can accept. - Default value is 0.01. - - Example:: - - from neural_compressor.config import AccuracyCriterion - - accuracy_criterion = AccuracyCriterion( - higher_is_better=True, # optional. - criterion='relative', # optional. Available values are 'relative' and 'absolute'. - tolerable_loss=0.01, # optional. - ) - """ - def __init__(self, higher_is_better=True, criterion='relative', tolerable_loss=0.01): - """Init an AccuracyCriterion object.""" - self.higher_is_better = higher_is_better - self.criterion = criterion - self.tolerable_loss = tolerable_loss - - @property - def higher_is_better(self): - """Get higher_is_better.""" - return self._higher_is_better - - @higher_is_better.setter - def higher_is_better(self, higher_is_better): - """Set higher_is_better.""" - if _check_value('higher_is_better', higher_is_better, bool): - self._higher_is_better = higher_is_better - - @property - def relative(self): - """Get tolerable_loss when criterion is relative.""" - if self.criterion != 'relative': - return None - return self.tolerable_loss - - @relative.setter - def relative(self, relative): - """Set tolerable_loss and criterion to relative.""" - self.criterion = 'relative' - self.tolerable_loss = relative - - @property - def absolute(self): - """Get tolerable_loss when criterion is absolute.""" - if self.criterion != 'absolute': - return None - return self.tolerable_loss - - @absolute.setter - def absolute(self, absolute): - """Set tolerable_loss and criterion to absolute.""" - self.criterion = 'absolute' - self.tolerable_loss = absolute - - @property - def criterion(self): - """Get criterion.""" - return self._criterion - - @criterion.setter - def criterion(self, criterion): - """Set criterion.""" - if _check_value('criterion', criterion, str, ['relative', 'absolute']): - self._criterion = criterion - - @property - def tolerable_loss(self): - """Get tolerable_loss.""" - return self._tolerable_loss - - @tolerable_loss.setter - def tolerable_loss(self, tolerable_loss): - """Set tolerable_loss.""" - if _check_value('tolerable_loss', tolerable_loss, float): - self._tolerable_loss = tolerable_loss - - def __str__(self): - """Get criterion.""" - return self.criterion - - def keys(self): - """Returns keys of the dict.""" - return ('higher_is_better', 'criterion', 'tolerable_loss') - - def __getitem__(self, item): - """Get the dict.""" - return getattr(self, item) - - -accuracy_criterion = AccuracyCriterion() - - -class _BaseQuantizationConfig: - """Basic class for quantization config. Inherited by PostTrainingQuantConfig and QuantizationAwareTrainingConfig. - - Args: - inputs: Inputs of model, only required in tensorflow. - outputs: Outputs of model, only required in tensorflow. - backend: Backend for model execution. Support 'default', 'itex', 'ipex', 'onnxrt_trt_ep', 'onnxrt_cuda_ep' - domain: Model domain. Support 'auto', 'cv', 'object_detection', 'nlp' and 'recommendation_system'. - Adaptor will use specific quantization settings for different domains automatically, and - explicitly specified quantization settings will override the automatic setting. - If users set domain as auto, automatic detection for domain will be executed. - recipes: Recipes for quantiztaion, support list is as below. - 'smooth_quant': whether do smooth quant - 'smooth_quant_args': parameters for smooth_quant - 'fast_bias_correction': whether do fast bias correction - 'weight_correction': whether do weight correction - 'gemm_to_matmul': whether convert gemm to matmul and add, only valid for onnx models - 'graph_optimization_level': support 'DISABLE_ALL', 'ENABLE_BASIC', 'ENABLE_EXTENDED', 'ENABLE_ALL' - only valid for onnx models - 'first_conv_or_matmul_quantization': whether quantize the first conv or matmul - 'last_conv_or_matmul_quantization': whether quantize the last conv or matmul - 'pre_post_process_quantization': whether quantize the ops in preprocess and postprocess - 'add_qdq_pair_to_weight': whether add QDQ pair for weights, only valid for onnxrt_trt_ep - 'optypes_to_exclude_output_quant': don't quantize output of specified optypes - 'dedicated_qdq_pair': whether dedicate QDQ pair, only valid for onnxrt_trt_ep - quant_format: Support 'default', 'QDQ' and 'QOperator', only required in ONNXRuntime. - device: Support 'cpu' and 'gpu'. - calibration_sampling_size: Number of calibration sample. - op_type_dict: Tuning constraints on optype-wise for advance user to reduce tuning space. - User can specify the quantization config by op type: - example: - { - 'Conv': { - 'weight': { - 'dtype': ['fp32'] - }, - 'activation': { - 'dtype': ['fp32'] - } - } - } - op_name_dict: Tuning constraints on op-wise for advance user to reduce tuning space. - User can specify the quantization config by op name: - example: - { - "layer1.0.conv1": { - "activation": { - "dtype": ["fp32"] - }, - "weight": { - "dtype": ["fp32"] - } - }, - } - strategy: Strategy name used in tuning, Please refer to docs/source/tuning_strategies.md. - strategy_kwargs: Parameters for strategy, Please refer to docs/source/tuning_strategies.md. - objective: Objective with accuracy constraint guaranteed, support 'performance', 'modelsize', 'footprint'. - Please refer to docs/source/objective.md. - Default value is 'performance'. - timeout: Tuning timeout (seconds). default value is 0 which means early stop - max_trials: Max tune times. default value is 100. Combine with timeout field to decide when to exit - performance_only: Whether do evaluation - reduce_range: Whether use 7 bit to quantization. - example_inputs: Used to trace PyTorch model with torch.jit/torch.fx. - excluded_precisions: Precisions to be excluded, Default value is empty list. - Neural compressor enable the mixed precision with fp32 + bf16 + int8 by default. - If you want to disable bf16 data type, you can specify excluded_precisions = ['bf16]. - quant_level: Support auto, 0 and 1, 0 is conservative strategy, 1 is basic or user-specified - strategy, auto (default) is the combination of 0 and 1. - accuracy_criterion: Accuracy constraint settings. - use_distributed_tuning: Whether use distributed tuning or not. - """ - def __init__(self, - inputs=[], - outputs=[], - backend="default", - domain="auto", - recipes={}, - quant_format="default", - device="cpu", - calibration_sampling_size=[100], - op_type_dict=None, - op_name_dict=None, - strategy="basic", - strategy_kwargs=None, - objective="performance", - timeout=0, - max_trials=100, - performance_only=False, - reduce_range=None, - example_inputs=None, - excluded_precisions=[], - quant_level="auto", - accuracy_criterion=accuracy_criterion, - use_distributed_tuning=False, - diagnosis=False): - """Initialize _BaseQuantizationConfig class.""" - self.inputs = inputs - self.outputs = outputs - self.backend = backend - self.domain = domain - self.recipes = recipes - self.quant_format = quant_format - self.device = device - self.op_type_dict = op_type_dict - self.op_name_dict = op_name_dict - self.strategy = strategy - self.strategy_kwargs = strategy_kwargs - self.objective = objective - self.timeout = timeout - self.max_trials = max_trials - self.performance_only = performance_only - self.reduce_range = reduce_range - self.excluded_precisions = excluded_precisions - self.use_bf16 = "bf16" not in self.excluded_precisions - self.accuracy_criterion = accuracy_criterion - self.calibration_sampling_size = calibration_sampling_size - self.quant_level = quant_level - self.use_distributed_tuning = use_distributed_tuning - self.diagnosis = diagnosis - self._example_inputs = example_inputs - - @property - def domain(self): - """Get domain.""" - return self._domain - - @domain.setter - def domain(self, domain): - """Set domain.""" - if _check_value("domain", domain, str, - ["auto", "cv", "object_detection", "nlp", "recommendation_system"]): - self._domain = domain - - @property - def recipes(self): - """Get recipes.""" - return self._recipes - - @recipes.setter - def recipes(self, recipes): - """Set recipes.""" - if recipes is not None and not isinstance(recipes, dict): - raise ValueError("recipes should be a dict.") - - def smooth_quant(val=None): - if val is not None: - return _check_value("smooth_quant", val, bool) - else: - return False - - def smooth_quant_args(val=None): - if val is not None: - _check_value("smooth_quant_args", val, dict) - for k, v in val.items(): - if k == "alpha": - if isinstance(v, str): - assert v == "auto", "the alpha of sq only supports float and 'auto'" - else: - _check_value("alpha", v, float) - - return True - else: - return {} - - def fast_bias_correction(val=None): - if val is not None: - return _check_value("fast_bias_correction", val, bool) - else: - return False - - def weight_correction(val=None): - if val is not None: - return _check_value("weight_correction", val, bool) - else: - return False - - def gemm_to_matmul(val=None): - if val is not None: - return _check_value("gemm_to_matmul", val, bool) - else: - return True - - def graph_optimization_level(val=None): - if val is not None: - return _check_value("graph_optimization_level", val, str, - ["DISABLE_ALL", "ENABLE_BASIC", "ENABLE_EXTENDED", "ENABLE_ALL"]) - else: - return None - - def first_conv_or_matmul_quantization(val=None): - if val is not None: - return _check_value("first_conv_or_matmul_quantization", val, bool) - else: - return True - - def last_conv_or_matmul_quantization(val=None): - if val is not None: - return _check_value("last_conv_or_matmul_quantization", val, bool) - else: - return True - - def pre_post_process_quantization(val=None): - if val is not None: - return _check_value("pre_post_process_quantization", val, bool) - else: - return True - - def add_qdq_pair_to_weight(val=None): - if val is not None: - return _check_value("add_qdq_pair_to_weight", val, bool) - else: - return False - - def optypes_to_exclude_output_quant(val=None): - if val is not None: - return isinstance(val, list) - else: - return [] - - def dedicated_qdq_pair(val=None): - if val is not None: - return _check_value("dedicated_qdq_pair", val, bool) - else: - return False - - RECIPES = {"smooth_quant": smooth_quant, - "smooth_quant_args": smooth_quant_args, - "fast_bias_correction": fast_bias_correction, - "weight_correction": weight_correction, - "gemm_to_matmul": gemm_to_matmul, - "graph_optimization_level": graph_optimization_level, - "first_conv_or_matmul_quantization": first_conv_or_matmul_quantization, - "last_conv_or_matmul_quantization": last_conv_or_matmul_quantization, - "pre_post_process_quantization": pre_post_process_quantization, - "add_qdq_pair_to_weight": add_qdq_pair_to_weight, - "optypes_to_exclude_output_quant": optypes_to_exclude_output_quant, - "dedicated_qdq_pair": dedicated_qdq_pair - } - self._recipes = {} - for k in RECIPES.keys(): - if k in recipes and RECIPES[k](recipes[k]): - self._recipes.update({k: recipes[k]}) - else: - self._recipes.update({k: RECIPES[k]()}) - - @property - def accuracy_criterion(self): - return self._accuracy_criterion - - @accuracy_criterion.setter - def accuracy_criterion(self, accuracy_criterion): - if _check_value("accuracy_criterion", accuracy_criterion, AccuracyCriterion): - self._accuracy_criterion = accuracy_criterion - - @property - def excluded_precisions(self): - return self._excluded_precisions - - @excluded_precisions.setter - def excluded_precisions(self, excluded_precisions): - if _check_value("excluded_precisions", excluded_precisions, str, ["bf16", "fp16"]): - self._excluded_precisions = excluded_precisions - self._use_bf16 = "bf16" not in excluded_precisions - - @property - def quant_level(self): - return self._quant_level - - @quant_level.setter - def quant_level(self, quant_level): - self._quant_level = quant_level - - @property - def use_distributed_tuning(self): - return self._use_distributed_tuning - - @use_distributed_tuning.setter - def use_distributed_tuning(self, use_distributed_tuning): - if _check_value('use_distributed_tuning', use_distributed_tuning, bool): - self._use_distributed_tuning = use_distributed_tuning - - @property - def reduce_range(self): - return self._reduce_range - - @reduce_range.setter - def reduce_range(self, reduce_range): - if reduce_range is None or _check_value('reduce_range', reduce_range, bool): - self._reduce_range = reduce_range - - @property - def performance_only(self): - return self._performance_only - - @performance_only.setter - def performance_only(self, performance_only): - if _check_value('performance_only', performance_only, bool): - self._performance_only = performance_only - - @property - def max_trials(self): - return self._max_trials - - @max_trials.setter - def max_trials(self, max_trials): - if _check_value('max_trials', max_trials, int): - self._max_trials = max_trials - - @property - def timeout(self): - return self._timeout - - @timeout.setter - def timeout(self, timeout): - if _check_value('timeout', timeout, int): - self._timeout = timeout - - @property - def objective(self): - return self._objective - - @objective.setter - def objective(self, objective): - if _check_value('objective', objective, str, - ['performance', 'accuracy', 'modelsize', 'footprint']): - self._objective = objective - - @property - def strategy(self): - return self._strategy - - @strategy.setter - def strategy(self, strategy): - if _check_value('strategy', strategy, str, - ['basic', 'mse', 'bayesian', 'random', 'exhaustive', 'sigopt', 'tpe', 'mse_v2', 'hawq_v2']): - self._strategy = strategy - - @property - def strategy_kwargs(self): - return self._strategy_kwargs - - @strategy_kwargs.setter - def strategy_kwargs(self, strategy_kwargs): - self._strategy_kwargs = strategy_kwargs - - @property - def op_name_dict(self): - return self._op_name_dict - - @op_name_dict.setter - def op_name_dict(self, op_name_dict): - if op_name_dict is None: - self._op_name_dict = op_name_dict - elif isinstance(op_name_dict, dict): - for k, v in op_name_dict.items(): - ops_schema.validate(v) - self._op_name_dict = op_name_dict - else: - assert False, ("Type of op_name_dict should be dict but not {}, ".format( - type(op_name_dict))) - - @property - def op_type_dict(self): - return self._op_type_dict - - @op_type_dict.setter - def op_type_dict(self, op_type_dict): - if op_type_dict is None: - self._op_type_dict = op_type_dict - elif isinstance(op_type_dict, dict): - for k, v in op_type_dict.items(): - ops_schema.validate(v) - self._op_type_dict = op_type_dict - else: - assert False, ("Type of op_type_dict should be dict but not {}".format( - type(op_type_dict))) - - @property - def calibration_sampling_size(self): - return self._calibration_sampling_size - - @calibration_sampling_size.setter - def calibration_sampling_size(self, sampling_size): - if _check_value('calibration_sampling_size', sampling_size, int): - if isinstance(sampling_size, int): - sampling_size = [sampling_size] - self._calibration_sampling_size = sampling_size - - @property - def device(self): - return self._device - - @device.setter - def device(self, device): - if _check_value('device', device, str, ['cpu', 'gpu']): - self._device = device - - @property - def quant_format(self): - return self._quant_format - - @quant_format.setter - def quant_format(self, quant_format): - if _check_value('quant_format', quant_format, str, - ['default', 'QDQ', 'QOperator']): - self._quant_format = quant_format - - @property - def backend(self): - return self._backend - - @backend.setter - def backend(self, backend): - if _check_value('backend', backend, str, [ - 'default', 'itex', 'ipex', 'onnxrt_trt_ep', 'onnxrt_cuda_ep']): - self._backend = backend - - @property - def outputs(self): - return self._outputs - - @outputs.setter - def outputs(self, outputs): - if _check_value('outputs', outputs, str): - self._outputs = outputs - - @property - def inputs(self): - return self._inputs - - @inputs.setter - def inputs(self, inputs): - if _check_value('inputs', inputs, str): - self._inputs = inputs - - @property - def example_inputs(self): - """Get strategy_kwargs.""" - return self._example_inputs - - @example_inputs.setter - def example_inputs(self, example_inputs): - """Set example_inputs.""" - self._example_inputs = example_inputs - - -class BenchmarkConfig: - """Config Class for Benchmark. - - Args: - inputs (list, optional): A list of strings containing the inputs of model. Default is an empty list. - outputs (list, optional): A list of strings containing the outputs of model. Default is an empty list. - backend (str, optional): Backend name for model execution. Supported values include: 'default', 'itex', - 'ipex', 'onnxrt_trt_ep', 'onnxrt_cuda_ep'. Default value is 'default'. - warmup (int, optional): The number of iterations to perform warmup before running performance tests. - Default value is 5. - iteration (int, optional): The number of iterations to run performance tests. Default is -1. - cores_per_instance (int, optional): The number of CPU cores to use per instance. Default value is None. - num_of_instance (int, optional): The number of instances to use for performance testing. - Default value is None. - inter_num_of_threads (int, optional): The number of threads to use for inter-thread operations. - Default value is None. - intra_num_of_threads (int, optional): The number of threads to use for intra-thread operations. - Default value is None. - - Example:: - - # Run benchmark according to config - from neural_compressor.benchmark import fit - - conf = BenchmarkConfig(iteration=100, cores_per_instance=4, num_of_instance=7) - fit(model='./int8.pb', config=conf, b_dataloader=eval_dataloader) - """ - def __init__(self, - inputs=[], - outputs=[], - backend='default', - device='cpu', - warmup=5, - iteration=-1, - model=None, - model_name='', - cores_per_instance=None, - num_of_instance=None, - inter_num_of_threads=None, - intra_num_of_threads=None, - diagnosis=False): - """Init a BenchmarkConfig object.""" - self.inputs = inputs - self.outputs = outputs - self.backend = backend - self.device=device - self.warmup = warmup - self.iteration = iteration - self.model = model - self.model_name = model_name - self.cores_per_instance = cores_per_instance - self.num_of_instance = num_of_instance - self.inter_num_of_threads = inter_num_of_threads - self.intra_num_of_threads = intra_num_of_threads - self.diagnosis = diagnosis - self._framework = None - - def keys(self): - """Returns keys of the dict.""" - return ('inputs', 'outputs', 'backend', 'device', 'warmup', 'iteration', 'model', \ - 'model_name', 'cores_per_instance', 'num_of_instance', 'framework', \ - 'inter_num_of_threads','intra_num_of_threads') - - def __getitem__(self, item): - """Get the dict.""" - return getattr(self, item) - - @property - def backend(self): - """Get backend.""" - return self._backend - - @backend.setter - def backend(self, backend): - """Set backend.""" - if _check_value('backend', backend, str, [ - 'default', 'itex', 'ipex', 'onnxrt_trt_ep', 'onnxrt_cuda_ep']): - self._backend = backend - - @property - def device(self): - """Get device name.""" - return self._device - - @device.setter - def device(self, device): - if _check_value('device', device, str, ['cpu', 'gpu']): - self._device = device - - @property - def outputs(self): - """Get outputs.""" - return self._outputs - - @outputs.setter - def outputs(self, outputs): - """Set outputs.""" - if _check_value('outputs', outputs, str): - self._outputs = outputs - - @property - def inputs(self): - """Get inputs.""" - return self._inputs - - @inputs.setter - def inputs(self, inputs): - """Set inputs.""" - if _check_value('inputs', inputs, str): - self._inputs = inputs - - @property - def warmup(self): - """Get warmup.""" - return self._warmup - - @warmup.setter - def warmup(self, warmup): - """Set warmup.""" - if _check_value('warmup', warmup, int): - self._warmup = warmup - - @property - def iteration(self): - """Get iteration.""" - return self._iteration - - @iteration.setter - def iteration(self, iteration): - """Set iteration.""" - if _check_value('iteration', iteration, int): - self._iteration = iteration - - @property - def cores_per_instance(self): - """Get cores_per_instance.""" - return self._cores_per_instance - - @cores_per_instance.setter - def cores_per_instance(self, cores_per_instance): - """Set cores_per_instance.""" - if cores_per_instance is None or _check_value('cores_per_instance', cores_per_instance, - int): - self._cores_per_instance = cores_per_instance - - @property - def num_of_instance(self): - """Get num_of_instance.""" - return self._num_of_instance - - @num_of_instance.setter - def num_of_instance(self, num_of_instance): - """Set num_of_instance.""" - if num_of_instance is None or _check_value('num_of_instance', num_of_instance, int): - self._num_of_instance = num_of_instance - - @property - def inter_num_of_threads(self): - """Get inter_num_of_threads.""" - return self._inter_num_of_threads - - @inter_num_of_threads.setter - def inter_num_of_threads(self, inter_num_of_threads): - """Set inter_num_of_threads.""" - if inter_num_of_threads is None or _check_value('inter_num_of_threads', - inter_num_of_threads, int): - self._inter_num_of_threads = inter_num_of_threads - - @property - def intra_num_of_threads(self): - """Get intra_num_of_threads.""" - return self._intra_num_of_threads - - @intra_num_of_threads.setter - def intra_num_of_threads(self, intra_num_of_threads): - """Get intra_num_of_threads.""" - if intra_num_of_threads is None or _check_value('intra_num_of_threads', - intra_num_of_threads, int): - self._intra_num_of_threads = intra_num_of_threads - - @property - def model(self): - """Get model.""" - return self._model - - @model.setter - def model(self, model): - """Set model.""" - self._model = model - - @property - def model_name(self): - """Get model name.""" - return self._model_name - - @model_name.setter - def model_name(self, model_name): - """Set model name.""" - if _check_value("model_name", model_name, str): - self._model_name = model_name - - @property - def framework(self): - """Set framework.""" - return self._framework - - @framework.setter - def framework(self, framework): - """Get framework.""" - self._framework = framework - - -class QuantizationConfig(_BaseQuantizationConfig): - def __init__(self, - inputs=[], - outputs=[], - backend='default', - device='cpu', - approach='post_training_static_quant', - calibration_sampling_size=[100], - op_type_dict=None, - op_name_dict=None, - strategy='basic', - strategy_kwargs=None, - objective='performance', - timeout=0, - max_trials=100, - performance_only=False, - reduce_range=None, - use_bf16=True, - quant_level="auto", - accuracy_criterion=accuracy_criterion, - diagnosis=False): - excluded_precisions = ["bf16"] if not use_bf16 else [] - super().__init__( - inputs=inputs, - outputs=outputs, - backend=backend, - device=device, - calibration_sampling_size=calibration_sampling_size, - op_type_dict=op_type_dict, - op_name_dict=op_name_dict, - strategy=strategy, - strategy_kwargs=strategy_kwargs, - objective=objective, - timeout=timeout, - max_trials=max_trials, - performance_only=performance_only, - reduce_range=reduce_range, - excluded_precisions=excluded_precisions, - accuracy_criterion=accuracy_criterion, - quant_level=quant_level, - diagnosis=diagnosis - ) - self.approach = approach - - @property - def approach(self): - return self._approach - - @approach.setter - def approach(self, approach): - if _check_value( - 'approach', approach, str, - ['post_training_static_quant', 'post_training_dynamic_quant', 'quant_aware_training'] - ): - self._approach = approach - - -class WeightPruningConfig: - """Config Class for Pruning. Define a single or a sequence of pruning configs. - - Args: - pruning_configs (list of dicts, optional): Local pruning configs only valid to linked layers. - Parameters defined out of pruning_configs are valid for all layers. - By defining dicts in pruning_config, users can set different pruning strategies for corresponding layers. - Defaults to [{}]. - target_sparsity (float, optional): Sparsity ratio the model can reach after pruning. - Supports a float between 0 and 1. - Default to 0.90. - pruning_type (str, optional): A string define the criteria for pruning. - Supports "magnitude", "snip", "snip_momentum", - "magnitude_progressive", "snip_progressive", "snip_momentum_progressive", "pattern_lock" - Default to "snip_momentum", which is the most feasible pruning criteria under most situations. - pattern (str, optional): Sparsity's structure (or unstructure) types. - Supports "NxM" (e.g "4x1", "8x1"), "channelx1" & "1xchannel"(channel-wise), "N:M" (e.g "2:4"). - Default to "4x1", which can be directly processed by our kernels in ITREX. - op_names (list of str, optional): Layers contains some specific names to be included for pruning. - Defaults to []. - excluded_op_names: Layers contains some specific names to be excluded for pruning. - Defaults to []. - start_step (int, optional): The step to start pruning. - Supports an integer. - Default to 0. - end_step: (int, optional): The step to end pruning. - Supports an integer. - Default to 0. - pruning_scope (str, optional): Determine layers' scores should be gather together to sort - Supports "global" and "local". - Default: "global", since this leads to less accuracy loss. - pruning_frequency: the frequency of pruning operation. - Supports an integer. - Default to 1. - min_sparsity_ratio_per_op (float, optional): Minimum restriction for every layer's sparsity. - Supports a float between 0 and 1. - Default to 0.0. - max_sparsity_ratio_per_op (float, optional): Maximum restriction for every layer's sparsity. - Supports a float between 0 and 1. - Default to 0.98. - sparsity_decay_type (str, optional): how to schedule the sparsity increasing methods. - Supports "exp", "cube", "cube", "linear". - Default to "exp". - pruning_op_types (list of str): Operator types currently support for pruning. - Supports ['Conv', 'Linear']. - Default to ['Conv', 'Linear']. - - Example:: - - from neural_compressor.config import WeightPruningConfig - local_configs = [ - { - "pruning_scope": "local", - "target_sparsity": 0.6, - "op_names": ["query", "key", "value"], - "pattern": "channelx1", - }, - { - "pruning_type": "snip_momentum_progressive", - "target_sparsity": 0.5, - "op_names": ["self.attention.dense"], - } - ] - config = WeightPruningConfig( - pruning_configs = local_configs, - target_sparsity=0.8 - ) - prune = Pruning(config) - prune.update_config(start_step=1, end_step=10) - prune.model = self.model - """ - - def __init__(self, pruning_configs=[{}], ##empty dict will use global values - target_sparsity=0.9, pruning_type="snip_momentum", pattern="4x1", op_names=[], - excluded_op_names=[], - start_step=0, end_step=0, pruning_scope="global", pruning_frequency=1, - min_sparsity_ratio_per_op=0.0, max_sparsity_ratio_per_op=0.98, - sparsity_decay_type="exp", pruning_op_types=['Conv', 'Linear'], - **kwargs): - """Init a WeightPruningConfig object.""" - self.pruning_configs = pruning_configs - self._weight_compression = DotDict({ - 'target_sparsity': target_sparsity, - 'pruning_type': pruning_type, - 'pattern': pattern, - 'op_names': op_names, - 'excluded_op_names': excluded_op_names, ##global only - 'start_step': start_step, - 'end_step': end_step, - 'pruning_scope': pruning_scope, - 'pruning_frequency': pruning_frequency, - 'min_sparsity_ratio_per_op': min_sparsity_ratio_per_op, - 'max_sparsity_ratio_per_op': max_sparsity_ratio_per_op, - 'sparsity_decay_type': sparsity_decay_type, - 'pruning_op_types': pruning_op_types, - }) - self._weight_compression.update(kwargs) - - @property - def weight_compression(self): - """Get weight_compression.""" - return self._weight_compression - - @weight_compression.setter - def weight_compression(self, weight_compression): - """Set weight_compression.""" - self._weight_compression = weight_compression - - -class WeightConf: - def __init__(self, datatype=None, scheme=None, granularity=None, algorithm=None): - self._datatype = datatype - self._scheme = scheme - self._granularity = granularity - self._algorithm = algorithm - - @property - def datatype(self): - return self._datatype - - @datatype.setter - def datatype(self, datatype): - if _check_value('datatype', datatype, str, ['fp32', 'bf16', 'uint8', 'int8']): - self._datatype = datatype if isinstance(datatype, list) else [datatype] - - @property - def scheme(self): - return self._scheme - - @scheme.setter - def scheme(self, scheme): - if _check_value('scheme', scheme, str, ['sym', 'asym']): - self._scheme = scheme if isinstance(scheme, list) else [scheme] - - @property - def granularity(self): - return self._granularity - - @granularity.setter - def granularity(self, granularity): - if _check_value('granularity', granularity, str, ['per_channel', 'per_tensor']): - self._granularity = granularity if isinstance(granularity, list) else [granularity] - - @property - def algorithm(self): - return self._algorithm - - @algorithm.setter - def algorithm(self, algorithm): - if _check_value('algorithm', algorithm, str, ['minmax', 'kl']): - self._algorithm = algorithm if isinstance(algorithm, list) else [algorithm] - - -class KnowledgeDistillationLossConfig: - """Config Class for Knowledge Distillation Loss. - - Args: - temperature (float, optional): Hyperparameters that control the entropy - of probability distributions. Defaults to 1.0. - loss_types (list[str], optional): loss types, should be a list of length 2. - First item is the loss type for student model output and groundtruth label, - second item is the loss type for student model output and teacher model output. - Supported types for first item are "CE", "MSE". - Supported types for second item are "CE", "MSE", "KL". - Defaults to ['CE', 'CE']. - loss_weights (list[float], optional): loss weights, should be a list of length 2 and sum to 1.0. - First item is the weight multiplied to the loss of student model output and groundtruth label, - second item is the weight multiplied to the loss of student model output and teacher model output. - Defaults to [0.5, 0.5]. - - Example:: - - from neural_compressor.config import DistillationConfig, KnowledgeDistillationLossConfig - from neural_compressor.training import prepare_compression - - criterion_conf = KnowledgeDistillationLossConfig() - d_conf = DistillationConfig(teacher_model=teacher_model, criterion=criterion_conf) - compression_manager = prepare_compression(model, d_conf) - model = compression_manager.model - """ - def __init__(self, temperature=1.0, loss_types=['CE', 'CE'], loss_weights=[0.5, 0.5]): - """Init a KnowledgeDistillationLossConfig object.""" - self.config = DotDict({ - 'KnowledgeDistillationLoss': { - 'temperature': temperature, - 'loss_types': loss_types, - 'loss_weights': loss_weights - } - }) - - -criterion = KnowledgeDistillationLossConfig() - - -class DistillationConfig: - """Config of distillation. - - Args: - teacher_model (Callable): Teacher model for distillation. Defaults to None. - features (optional): Teacher features for distillation, features and teacher_model are alternative. - Defaults to None. - criterion (Callable, optional): Distillation loss configure. - optimizer (dictionary, optional): Optimizer configure. - - Example:: - - from neural_compressor.training import prepare_compression - from neural_compressor.config import DistillationConfig, KnowledgeDistillationLossConfig - - distil_loss = KnowledgeDistillationLossConfig() - conf = DistillationConfig(teacher_model=model, criterion=distil_loss) - criterion = nn.CrossEntropyLoss() - optimizer = torch.optim.SGD(model.parameters(), lr=0.0001) - compression_manager = prepare_compression(model, conf) - model = compression_manager.model - """ - def __init__(self, - teacher_model=None, - criterion=criterion, - optimizer={'SGD': { - 'learning_rate': 0.0001 - }}): - """Init a DistillationConfig object.""" - self.criterion = criterion - self.optimizer = optimizer - self.teacher_model = teacher_model - - @property - def criterion(self): - """Get criterion.""" - return self._criterion - - @criterion.setter - def criterion(self, criterion): - """Set criterion.""" - self._criterion = criterion - - @property - def optimizer(self): - """Get optimizer.""" - return self._optimizer - - @optimizer.setter - def optimizer(self, optimizer): - """Set optimizer.""" - self._optimizer = optimizer - - @property - def teacher_model(self): - """Get teacher_model.""" - return self._teacher_model - - @teacher_model.setter - def teacher_model(self, teacher_model): - """Set teacher_model.""" - self._teacher_model = teacher_model - - -class ActivationConf(WeightConf): - def __init__(self, datatype=None, scheme=None, granularity=None, algorithm=None): - super().__init__(datatype, scheme, granularity, algorithm) - - -weight = WeightConf() -activation = ActivationConf() - - -class OpQuantConf: - def __init__(self, op_type=None, weight=weight, activation=activation): - self._op_type = op_type - self._weight = weight - self._activation = activation - - @property - def op_type(self): - return self._op_type - - @op_type.setter - def op_type(self, op_type): - if _check_value('op_type', op_type, str): - self._op_type = op_type - - @property - def weight(self): - return self._weight - - @property - def activation(self): - return self._activation - - -class MXNet: - def __init__(self, precisions=None): - self._precisions = precisions - - @property - def precisions(self): - return self._precisions - - @precisions.setter - def precisions(self, precisions): - if not isinstance(precisions, list): - precisions = [precisions] - if _check_value('precisions', precisions, str, ['int8', 'uint8', 'fp32', 'bf16', 'fp16']): - self._precisions = precisions - - -class ONNX(MXNet): - def __init__(self, graph_optimization_level=None, precisions=None): - super().__init__(precisions) - self._graph_optimization_level = graph_optimization_level - - @property - def graph_optimization_level(self): - return self._graph_optimization_level - - @graph_optimization_level.setter - def graph_optimization_level(self, graph_optimization_level): - if _check_value('graph_optimization_level', graph_optimization_level, str, - ['DISABLE_ALL', 'ENABLE_BASIC', 'ENABLE_EXTENDED', 'ENABLE_ALL']): - self._graph_optimization_level = graph_optimization_level - - -class TensorFlow(MXNet): - def __init__(self, precisions=None): - super().__init__(precisions) - - -class Keras(MXNet): - def __init__(self, precisions=None): - super().__init__(precisions) - - -class PyTorch(MXNet): - def __init__(self, precisions=None): - super().__init__(precisions) - - -class DyNASConfig: - def __init__(self, supernet=None, metrics=None, population=50, num_evals=100000, - results_csv_path=None, dataset_path=None, batch_size=64): - self.config = { - 'supernet': supernet, - 'metrics': metrics, - 'population': population, - 'num_evals': num_evals, - 'results_csv_path': results_csv_path, - 'dataset_path': dataset_path, - 'batch_size': batch_size, - } - - -class NASConfig: - def __init__(self, approach=None, search_space=None, search_algorithm=None, - metrics=[], higher_is_better=[], max_trials=3, seed=42, dynas=None): - self._approach = approach - self._search = DotDict({ - 'search_space': search_space, - 'search_algorithm': search_algorithm, - 'metrics': metrics, - 'higher_is_better': higher_is_better, - 'max_trials': max_trials, - 'seed': seed - }) - self.dynas = None - if approach == 'dynas' and dynas: - self.dynas = dynas.config - - @property - def approach(self): - return self._approach - - @approach.setter - def approach(self, approach): - self._approach = approach - - @property - def search(self): - return self._search - - @search.setter - def search(self, search): - self._search = search - - -quantization = QuantizationConfig() -benchmark = BenchmarkConfig() -options = Options() -pruning = WeightPruningConfig() -distillation = DistillationConfig(teacher_model=None) -nas = NASConfig() -onnxruntime_config = ONNX() -tensorflow_config = TensorFlow() -keras_config = Keras() -pytorch_config = PyTorch() -mxnet_config = MXNet() - - -class Config: - def __init__(self, - quantization=quantization, - benchmark=benchmark, - options=options, - pruning=pruning, - distillation=distillation, - nas=nas, - onnxruntime=onnxruntime_config, - tensorflow=tensorflow_config, - pytorch=pytorch_config, - mxnet=mxnet_config, - keras=keras_config): - self._quantization = quantization - self._benchmark = benchmark - self._options = options - self._onnxruntime = onnxruntime - self._pruning = pruning - self._distillation = distillation - self._nas = nas - self._tensorflow = tensorflow - self._pytorch = pytorch - self._mxnet = mxnet - self._keras = keras - - @property - def distillation(self): - return self._distillation - - @property - def nas(self): - return self._nas - - @property - def tensorflow(self): - return self._tensorflow - - @property - def keras(self): - return self._keras - - @property - def pytorch(self): - return self._pytorch - - @property - def mxnet(self): - return self._mxnet - - @property - def pruning(self): - return self._pruning - - @property - def quantization(self): - return self._quantization - - @property - def benchmark(self): - return self._benchmark - - @property - def options(self): - return self._options - - @property - def onnxruntime(self): - return self._onnxruntime - - -config = Config() diff --git a/neural_compressor/experimental/__init__.py b/neural_compressor/experimental/__init__.py deleted file mode 100644 index fbf460df2a9..00000000000 --- a/neural_compressor/experimental/__init__.py +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Intel® Neural Compressor: An open-source Python library supporting popular model compression techniques.""" - -from .component import Component -from .quantization import Quantization -from .pruning import Pruning -from .benchmark import Benchmark -from .graph_optimization import Graph_Optimization, GraphOptimization -from .mixed_precision import MixedPrecision -from .model_conversion import ModelConversion -from .distillation import Distillation -from .nas import NAS -from . import export -from .contrib import * - -__all__ = [ - "Component", - "Quantization", - "Pruning", - "Benchmark", - "Graph_Optimization", - "GraphOptimization", - "ModelConversion", - "Distillation", - "NAS", - "MixedPrecision", - "export", -] diff --git a/neural_compressor/experimental/benchmark.py b/neural_compressor/experimental/benchmark.py deleted file mode 100644 index b84a9fdff47..00000000000 --- a/neural_compressor/experimental/benchmark.py +++ /dev/null @@ -1,658 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Benchmarking: measure the model performance with the objective settings.""" - -import os -import re -import signal -import subprocess -import sys -from threading import Thread - -import numpy as np -import psutil -from deprecated import deprecated - -from ..adaptor import FRAMEWORKS -from ..conf.config import BenchmarkConf -from ..conf.dotdict import DotDict, deep_get, deep_set -from ..conf.pythonic_config import Config -from ..model import BaseModel -from ..model.model import get_model_fwk_name -from ..objective import MultiObjective -from ..utils import logger -from ..utils.create_obj_from_config import create_dataloader, create_eval_func -from ..utils.utility import GLOBAL_STATE, MODE -from .common import Metric as NCMetric -from .common import Model as NCModel -from .common import Postprocess as NCPostprocess -from .common import _generate_common_dataloader -from .metric import METRICS - - -@deprecated(version="2.0") -def set_env_var(env_var, value, overwrite_existing=False): - """Set the specified environment variable. - - Only set new env in two cases: - 1. env not exists - 2. env already exists but overwrite_existing params set True - """ - if overwrite_existing or not os.environ.get(env_var): - os.environ[env_var] = str(value) - - -@deprecated(version="2.0") -def set_all_env_var(conf, overwrite_existing=False): - """Set all the environment variables with the configuration dict. - - Neural Compressor only uses physical cores - """ - cpu_counts = psutil.cpu_count(logical=False) - if not conf: - conf = {} - conf["num_of_instance"] = 1 - conf["cores_per_instance"] = cpu_counts - if "cores_per_instance" in conf: - assert ( - conf["cores_per_instance"] * conf["num_of_instance"] <= cpu_counts - ), "num_of_instance * cores_per_instance should <= cpu physical cores" - else: - assert conf["num_of_instance"] <= cpu_counts, "num_of_instance should <= cpu counts" - conf["cores_per_instance"] = int(cpu_counts / conf["num_of_instance"]) - - for var, value in conf.items(): - set_env_var(var.upper(), value, overwrite_existing) - - -@deprecated(version="2.0") -def get_architecture(): - """Get the architecture name of the system.""" - p1 = subprocess.Popen("lscpu", stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - p2 = subprocess.Popen(["grep", "Architecture"], stdin=p1.stdout, stdout=subprocess.PIPE) - p3 = subprocess.Popen(["cut", "-d", ":", "-f2"], stdin=p2.stdout, stdout=subprocess.PIPE) - res = None - for line in iter(p3.stdout.readline, b""): - res = line.decode("utf-8").strip() - return res - - -@deprecated(version="2.0") -def get_threads_per_core(): - """Get the threads per core.""" - p1 = subprocess.Popen("lscpu", stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - p2 = subprocess.Popen(["grep", "Thread(s) per core"], stdin=p1.stdout, stdout=subprocess.PIPE) - p3 = subprocess.Popen(["cut", "-d", ":", "-f2"], stdin=p2.stdout, stdout=subprocess.PIPE) - res = None - for line in iter(p3.stdout.readline, b""): - res = line.decode("utf-8").strip() - return res - - -@deprecated(version="2.0") -def get_threads(): - """Get the list of threads.""" - p1 = subprocess.Popen(["cat", "/proc/cpuinfo"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - p2 = subprocess.Popen(["grep", "processor"], stdin=p1.stdout, stdout=subprocess.PIPE) - p3 = subprocess.Popen(["cut", "-d", ":", "-f2"], stdin=p2.stdout, stdout=subprocess.PIPE) - res = [] - for line in iter(p3.stdout.readline, b""): - res.append(line.decode("utf-8").strip()) - return res - - -@deprecated(version="2.0") -def get_physical_ids(): - """Get the list of sockets.""" - p1 = subprocess.Popen(["cat", "/proc/cpuinfo"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - p2 = subprocess.Popen(["grep", "physical id"], stdin=p1.stdout, stdout=subprocess.PIPE) - p3 = subprocess.Popen(["cut", "-d", ":", "-f2"], stdin=p2.stdout, stdout=subprocess.PIPE) - res = [] - for line in iter(p3.stdout.readline, b""): - res.append(line.decode("utf-8").strip()) - return res - - -@deprecated(version="2.0") -def get_core_ids(): - """Get the ids list of the cores.""" - p1 = subprocess.Popen(["cat", "/proc/cpuinfo"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - p2 = subprocess.Popen(["grep", "core id"], stdin=p1.stdout, stdout=subprocess.PIPE) - p3 = subprocess.Popen(["cut", "-d", ":", "-f2"], stdin=p2.stdout, stdout=subprocess.PIPE) - res = [] - for line in iter(p3.stdout.readline, b""): - res.append(line.decode("utf-8").strip()) - return res - - -@deprecated(version="2.0") -def get_bounded_threads(core_ids, threads, sockets): - """Return the threads id list that we will bind instances to.""" - res = [] - existing_socket_core_list = [] - for idx, x in enumerate(core_ids): - socket_core = sockets[idx] + ":" + x - if socket_core not in existing_socket_core_list: - res.append(int(threads[idx])) - existing_socket_core_list.append(socket_core) - return res - - -@deprecated(version="2.0") -class Benchmark(object): - """Benchmark class is used to evaluate the model performance with the objective settings. - - Users can use the data that they configured in YAML - NOTICE: neural_compressor Benchmark will use the original command to run sub-process, which - depends on the user's code and has the possibility to run unnecessary code - """ - - def __init__(self, conf_fname_or_obj=None): - """Init a Benchmark object. - - Args: - conf_fname_or_obj (string or obj): The path to the YAML configuration file or - BenchmarkConf class containing accuracy goal, tuning objective, and preferred - calibration & quantization tuning space etc. - """ - self.framework = None - self._model = None - self._b_dataloader = None - self._b_func = None - self._custom_b_func = False - self._metric = None - self._results = {} - if isinstance(conf_fname_or_obj, BenchmarkConf): - self.conf = conf_fname_or_obj - elif isinstance(conf_fname_or_obj, Config): - self.conf = BenchmarkConf() - self.conf.map_pyconfig_to_cfg(conf_fname_or_obj) - else: - self.conf = BenchmarkConf(conf_fname_or_obj) - if self.conf.usr_cfg.model.framework != "NA": - self.framework = self.conf.usr_cfg.model.framework.lower() - - def __call__(self, mode="performance"): - """Directly call a Benchmark object. - - Args: - mode: 'performance' or 'accuracy' - 'performance' mode runs benchmarking with numactl on specific cores and instances set - by user config and returns model performance - 'accuracy' mode runs benchmarking with full cores and returns model accuracy - """ - cfg = self.conf.usr_cfg - assert cfg.evaluation is not None, "benchmark evaluation filed should not be None..." - assert sys.platform in ["linux", "win32"], "only support platform windows and linux..." - set_all_env_var(deep_get(cfg, "evaluation.{}.configs".format(mode))) - # disable multi-instance for accuracy mode or running benchmark on GPU device - if mode == "accuracy" or cfg.device == "gpu": - set_env_var("NC_ENV_CONF", True, overwrite_existing=True) - - logger.info("Start to run Benchmark.") - if os.environ.get("NC_ENV_CONF") == "True": - return self.run_instance(mode) - else: - self.config_instance() - self.summary_benchmark() - return None - - fit = __call__ - - def summary_benchmark(self): - """Get the summary of the benchmark.""" - num_of_instance = int(os.environ.get("NUM_OF_INSTANCE")) - cores_per_instance = int(os.environ.get("CORES_PER_INSTANCE")) - latency_l = [] - throughput_l = [] - for i in range(0, num_of_instance): - log = "{}_{}_{}.log".format(num_of_instance, cores_per_instance, i) - with open(log, "r") as f: - for line in f: - latency = re.search(r"[L,l]atency:\s+(\d+(\.\d+)?)", line) - latency_l.append(float(latency.group(1))) if latency and latency.group(1) else None - throughput = re.search(r"[T,t]hroughput:\s+(\d+(\.\d+)?)", line) - throughput_l.append(float(throughput.group(1))) if throughput and throughput.group(1) else None - if throughput_l and latency_l: - assert ( - len(latency_l) == len(throughput_l) == num_of_instance - ), "Multiple instance benchmark failed with some instance!" - logger.info("\n\nMultiple instance benchmark summary: ") - logger.info("Latency average: {:.3f} ms".format(sum(latency_l) / len(latency_l))) - logger.info("Throughput sum: {:.3f} images/sec".format(sum(throughput_l))) - - def call_one(self, cmd, log_file): - """Execute one command for one instance in one thread and dump the log (for Windows).""" - proc = subprocess.Popen( - cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True - ) # nosec - with open(log_file, "w", 1, encoding="utf-8") as log_file: - log_file.write(f"[ COMMAND ] {cmd} \n") - for line in proc.stdout: - decoded_line = line.decode("utf-8", errors="ignore").strip() - logger.info(decoded_line) # redirect to terminal - log_file.write(decoded_line + "\n") - - def config_instance(self): - """Configure the multi-instance commands and trigger benchmark with sub process.""" - raw_cmd = sys.executable + " " + " ".join(sys.argv) - multi_instance_cmd = "" - num_of_instance = int(os.environ.get("NUM_OF_INSTANCE")) - cores_per_instance = int(os.environ.get("CORES_PER_INSTANCE")) - - logger.info("num of instance: {}".format(num_of_instance)) - logger.info("cores per instance: {}".format(cores_per_instance)) - - if sys.platform in ["linux"] and get_architecture() == "aarch64" and int(get_threads_per_core()) > 1: - raise OSError("Currently no support on ARM with hyperthreads") - elif sys.platform in ["linux"]: - bounded_threads = get_bounded_threads(get_core_ids(), get_threads(), get_physical_ids()) - - for i in range(0, num_of_instance): - if sys.platform in ["linux"] and get_architecture() == "x86_64": - core_list_idx = np.arange(0, cores_per_instance) + i * cores_per_instance - core_list = np.array(bounded_threads)[core_list_idx] - else: - core_list = np.arange(0, cores_per_instance) + i * cores_per_instance - # bind cores only allowed in linux/mac os with numactl enabled - prefix = self.generate_prefix(core_list) - instance_cmd = "{} {}".format(prefix, raw_cmd) - if sys.platform in ["linux"]: - instance_log = "{}_{}_{}.log".format(num_of_instance, cores_per_instance, i) - multi_instance_cmd += "{} 2>&1|tee {} & \\\n".format(instance_cmd, instance_log) - else: # pragma: no cover - multi_instance_cmd += "{} \n".format(instance_cmd) - - multi_instance_cmd += "wait" if sys.platform in ["linux"] else "" - logger.info("Running command is\n{}".format(multi_instance_cmd)) - # each instance will execute single instance - set_env_var("NC_ENV_CONF", True, overwrite_existing=True) - if sys.platform in ["linux"]: - p = subprocess.Popen(multi_instance_cmd, preexec_fn=os.setsid, shell=True) # nosec - elif sys.platform in ["win32"]: # pragma: no cover - cmd_list = multi_instance_cmd.split("\n")[:-1] - threads = [] - for idx, cmd in enumerate(cmd_list): - # wrap each execution of windows bat file in one thread - # write the log to the log file of the corresponding instance - logger.info("Will dump to {}_{}_{}.log".format(num_of_instance, cores_per_instance, idx)) - threads.append( - Thread( - target=self.call_one, - args=(cmd, "{}_{}_{}.log".format(num_of_instance, cores_per_instance, idx)), - ) - ) - for command_thread in threads: - command_thread.start() - logger.info("Worker threads start") - # Wait for all of them to finish - for command_thread in threads: - command_thread.join() - logger.info("Worker threads join") - return - try: - p.communicate() - except KeyboardInterrupt: - os.killpg(os.getpgid(p.pid), signal.SIGKILL) - - def generate_prefix(self, core_list): - """Generate the command prefix with numactl. - - Args: - core_list: a list of core indexes bound with specific instances - """ - if sys.platform in ["linux"] and os.system("numactl --show >/dev/null 2>&1") == 0: - return "OMP_NUM_THREADS={} numactl --localalloc --physcpubind={}".format( - len(core_list), ",".join(core_list.astype(str)) - ) - elif sys.platform in ["win32"]: # pragma: no cover - # (TODO) should we move the hw_info from ux? - from neural_compressor.utils.utility import get_number_of_sockets - - num_of_socket = int(get_number_of_sockets()) - cores_per_instance = int(os.environ.get("CORES_PER_INSTANCE")) - cores_per_socket = int(psutil.cpu_count(logical=False)) / num_of_socket - socket_id = int(core_list[0] // cores_per_socket) - # cores per socket should integral multiple of cores per instance, else not bind core - if cores_per_socket % cores_per_instance == 0: - from functools import reduce - - hex_core = hex(reduce(lambda x, y: x | y, [1 << p for p in core_list])) - return "start /b /WAIT /node {} /affinity {} CMD /c".format(socket_id, hex_core) - else: - return "" - - def run_instance(self, mode): - """Run the instance with the configuration. - - Args: - mode: 'performance' or 'accuracy' - 'performance' mode runs benchmarking with numactl on specific cores and instances set - by user config and returns model performance - 'accuracy' mode runs benchmarking with full cores and returns model accuracy - """ - cfg = self.conf.usr_cfg - GLOBAL_STATE.STATE = MODE.BENCHMARK - framework_specific_info = { - "device": cfg.device, - "approach": cfg.quantization.approach, - "random_seed": cfg.tuning.random_seed, - "backend": cfg.model.get("backend", "default"), - "domain": cfg.model.get("domain", "auto"), - "format": cfg.model.get("quant_format", "default"), - } - framework = cfg.model.framework.lower() - if "tensorflow" in framework: - framework_specific_info.update( - { - "inputs": cfg.model.inputs, - "outputs": cfg.model.outputs, - "recipes": cfg.model.recipes, - "workspace_path": cfg.tuning.workspace.path, - } - ) - if framework == "keras": - framework_specific_info.update({"workspace_path": cfg.tuning.workspace.path}) - if framework == "mxnet": - framework_specific_info.update({"b_dataloader": self._b_dataloader}) - if "onnx" in framework.lower(): - framework_specific_info.update( - {"workspace_path": cfg.tuning.workspace.path, "recipes": cfg.quantization.get("recipes", {})} - ) - if framework == "pytorch_ipex" or framework == "pytorch" or framework == "pytorch_fx": - framework_specific_info.update({"workspace_path": cfg.tuning.workspace.path, "q_dataloader": None}) - - assert isinstance(self._model, BaseModel), "need set neural_compressor Model for quantization...." - - adaptor = FRAMEWORKS[framework](framework_specific_info) - - if deep_get(cfg, "evaluation.{}.iteration".format(mode)) == -1 and "dummy_v2" in deep_get( - cfg, "evaluation.{}.dataloader.dataset".format(mode), {} - ): - deep_set(cfg, "evaluation.{}.iteration".format(mode), 10) - - iteration = ( - -1 - if deep_get(cfg, "evaluation.{}.iteration".format(mode)) is None - else deep_get(cfg, "evaluation.{}.iteration".format(mode)) - ) - - metric = [self._metric] if self._metric else deep_get(cfg, "evaluation.{}.metric".format(mode)) - b_postprocess_cfg = deep_get(cfg, "evaluation.{}.postprocess".format(mode)) - if self._b_func is None and self._b_dataloader is None: - assert ( - deep_get(cfg, "evaluation.{}.dataloader".format(mode)) is not None - ), "dataloader field of yaml file is missing" - - b_dataloader_cfg = deep_get(cfg, "evaluation.{}.dataloader".format(mode)) - self._b_dataloader = create_dataloader(self.framework, b_dataloader_cfg) - is_measure = False - if self._b_func is None: - is_measure = True - self._b_func = create_eval_func( - self.framework, self._b_dataloader, adaptor, metric, b_postprocess_cfg, iteration=iteration - ) - else: - self._custom_b_func = True - - objectives = ( - [i.lower() for i in cfg.tuning.multi_objectives.objective] - if deep_get(cfg, "tuning.multi_objectives") - else [cfg.tuning.objective] - ) - assert len(objectives) == 1, "benchmark supports one objective at a time" - self.objectives = MultiObjective(objectives, cfg.tuning.accuracy_criterion, is_measure=is_measure) - if self._custom_b_func: - val = self.objectives.evaluate(self._b_func, self._model.model) - return - else: - val = self.objectives.evaluate(self._b_func, self._model) - # measurer contain info not only performance(eg, memory, model_size) - # also measurer have result list among steps - acc, _ = val - batch_size = self._b_dataloader.batch_size - warmup = ( - 0 - if deep_get(cfg, "evaluation.{}.warmup".format(mode)) is None - else deep_get(cfg, "evaluation.{}.warmup".format(mode)) - ) - - if len(self.objectives.objectives[0].result_list()) < warmup: - if len(self.objectives.objectives[0].result_list()) > 1 and warmup != 0: - warmup = 1 - else: - warmup = 0 - - result_list = self.objectives.objectives[0].result_list()[warmup:] - latency = np.array(result_list).mean() / batch_size - self._results[mode] = acc, batch_size, result_list - - logger.info("\n{} mode benchmark result:".format(mode)) - for i, res in enumerate(result_list): - logger.debug("Iteration {} result {}:".format(i, res)) - if mode == "accuracy": - logger.info("Batch size = {}".format(batch_size)) - if isinstance(acc, list): - logger.info("Accuracy is" + "".join([" {:.4f}".format(i) for i in acc])) - else: - logger.info("Accuracy is {:.4f}".format(acc)) - elif mode == "performance": - logger.info("Batch size = {}".format(batch_size)) - logger.info("Latency: {:.3f} ms".format(latency * 1000)) - logger.info("Throughput: {:.3f} images/sec".format(1.0 / latency)) - - @property - def results(self): - """Get the results of benchmarking.""" - return self._results - - @property - def b_dataloader(self): - """Get the dataloader for the benchmarking.""" - return self._b_dataloader - - @b_dataloader.setter - def b_dataloader(self, dataloader): - """Set dataloader for benchmarking. - - It is iterable and the batched data should consist of a tuple like (input, label) or yield (input, _). - When b_dataloader is set, users can configure postprocess(optional) and metric - in yaml file or set postprocess and metric cls for evaluation, - or just get performance without a label in dataloader and configure postprocess/metric. - - Args: - dataloader(generator): users are supported to set a user-defined dataloader - which meet the requirements that can yield a tuple of - (input, label)/(input, _) batched data. - Another good practice is to use - neural_compressor.experimental.common.DataLoader - to initialize a neural_compressor dataloader object. - Notice neural_compressor.experimental.common.DataLoader - is just a wrapper of the information needed to - build a dataloader, it can't yield - batched data and only in this setter method - a 'real' eval_dataloader will be created, - the reason is we have to know the framework info - and only after the Quantization object is created then - framework information can be known. - Future we will support creating iterable dataloader - from neural_compressor.experimental.common.DataLoader - """ - self._b_dataloader = _generate_common_dataloader(dataloader, self.framework) - - @property - def b_func(self): - """Not support getting b_func.""" - assert False, "Should not try to get the value of `b_func` attribute." - return None - - @b_func.setter - def b_func(self, user_b_func): - """Eval function for benchmark. - - Args: - user_b_func: This function takes "model" as input parameter - and executes the entire training process with self - contained training hyper-parameters. If train_func is set, - an evaluation process must be triggered and the user should - set eval_dataloader with metric configured or directly eval_func - to make an evaluation of the model executed. - """ - self._b_func = user_b_func - - @property - def model(self): - """Get the model.""" - return self._model - - @model.setter - def model(self, user_model): - """Set the user model and dispatch to the framework-specific internal model object. - - Args: - user_model: users are supported to set model from the original framework model format - (eg, tensorflow frozen_pb or path to a saved model), - but not recommended. A best practice is to set from an initialized - neural_compressor.experimental.common.Model. - If tensorflow model is used, the model's inputs/outputs will be - auto inferenced, but sometimes auto inferenced - inputs/outputs will not meet your requests, so it is better to - set them manually in config yaml file. - Another corner case is the slim model of tensorflow, - be careful of the name of the model configured in the yaml file, - make sure the name is in the supported slim model list. - """ - cfg = self.conf.usr_cfg - if cfg.model.framework == "NA": - assert not isinstance( - user_model, BaseModel - ), "Please pass an original framework model but not neural compressor model!" - self.framework = get_model_fwk_name(user_model) - if self.framework == "tensorflow": - from ..model.tensorflow_model import get_model_type - - if get_model_type(user_model) == "keras" and cfg.model.backend == "itex": - self.framework = "keras" - if self.framework == "pytorch": - if cfg.model.backend == "default": - self.framework = "pytorch_fx" - elif cfg.model.backend == "ipex": - self.framework = "pytorch_ipex" - import intel_extension_for_pytorch - cfg.model.framework = self.framework - - if not isinstance(user_model, BaseModel): - logger.warning("Force convert framework model to neural_compressor model.") - if "tensorflow" in self.framework or self.framework == "keras": - self._model = NCModel(user_model, framework=self.framework, device=cfg.device) - else: - self._model = NCModel(user_model, framework=self.framework) - else: - # It is config of neural_compressor version < 2.0, no need in 2.0 - if cfg.model.framework == "pytorch_ipex": - from neural_compressor.model.torch_model import IPEXModel - - if not isinstance(user_model, IPEXModel): - self._model = NCModel(user_model.model, framework=cfg.model.framework) - return - self._model = user_model - - # (TODO) ugly to set these params, but tensorflow need - if "tensorflow" in self.framework: - self._model.name = cfg.model.name - self._model.output_tensor_names = cfg.model.outputs - self._model.input_tensor_names = cfg.model.inputs - self._model.workspace_path = cfg.tuning.workspace.path - - @property - def metric(self): - """Not support getting metric.""" - assert False, "Should not try to get the value of `metric` attribute." - return None - - @metric.setter - def metric(self, user_metric): - """Set the metric class and Neural Compressor will initialize this class when evaluation. - - Neural Compressor has many built-in metrics, but users can set specific metrics through - this api. The metric class should take the outputs of the model or - postprocess (if have) as inputs. Neural Compressor built-in metrics always take - (predictions, labels) as inputs for update, - and user_metric.metric_cls should be sub_class of neural_compressor.metric.BaseMetric - or user-defined metric object - - Args: - user_metric: user_metric should be an object initialized from - neural_compressor.experimental.common.Metric, and in this method the - user_metric.metric_cls will be registered to - specific frameworks and initialized. - """ - if deep_get(self.conf.usr_cfg, "evaluation.accuracy.metric"): - logger.warning( - "Override the value of `metric` field defined in yaml file" - " as user defines the value of `metric` attribute by code." - ) - - if isinstance(user_metric, NCMetric): - metric_cfg = {user_metric.name: {**user_metric.kwargs}} - deep_set(self.conf.usr_cfg, "evaluation.accuracy.metric", metric_cfg) - self.conf.usr_cfg = DotDict(self.conf.usr_cfg) - metrics = METRICS(self.framework) - metrics.register(user_metric.name, user_metric.metric_cls) - else: - for i in ["reset", "update", "result"]: - assert hasattr(user_metric, i), "Please realize {} function" "in user defined metric".format(i) - self._metric = user_metric - - @property - def postprocess(self, user_postprocess): - """Not support getting postprocess.""" - assert False, "Should not try to get the value of `postprocess` attribute." - return None - - @postprocess.setter - def postprocess(self, user_postprocess): - """Set postprocess class and neural_compressor will initialize this class when evaluation. - - The postprocess class should take the outputs of the model as inputs, and - outputs (predictions, labels) as inputs for metric updates. - user_postprocess.postprocess_cls should be sub_class of neural_compressor.data.BaseTransform. - - Args: - user_postprocess: user_postprocess should be an object initialized from - neural_compressor.experimental.common.Postprocess, and - in this method the user_postprocess.postprocess_cls will be - registered to specific frameworks and initialized. - """ - assert isinstance( - user_postprocess, NCPostprocess - ), "please initialize a neural_compressor.experimental.common.Postprocess and set...." - postprocess_cfg = {user_postprocess.name: {**user_postprocess.kwargs}} - if deep_get(self.conf.usr_cfg, "evaluation.accuracy.postprocess"): - logger.warning( - "Override the value of `postprocess` field defined in yaml file" - " as user defines the value of `postprocess` attribute by code." - ) - deep_set(self.conf.usr_cfg, "evaluation.accuracy.postprocess.transform", postprocess_cfg) - from neural_compressor.data import TRANSFORMS - - postprocesses = TRANSFORMS(self.framework, "postprocess") - postprocesses.register(user_postprocess.name, user_postprocess.postprocess_cls) - - def __repr__(self): - """Get the object representation in string format.""" - return "Benchmark" diff --git a/neural_compressor/experimental/common/__init__.py b/neural_compressor/experimental/common/__init__.py deleted file mode 100644 index 04bdc96a14e..00000000000 --- a/neural_compressor/experimental/common/__init__.py +++ /dev/null @@ -1,27 +0,0 @@ -"""Intel® Neural Compressor: An open-source Python library supporting common model.""" - -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from .model import Model -from .dataloader import DataLoader, _generate_common_dataloader -from .postprocess import Postprocess -from .metric import Metric -from .criterion import Criterions -from .optimizer import Optimizers - -__all__ = ["Model", "DataLoader", "Postprocess", "Metric", "_generate_common_dataloader"] diff --git a/neural_compressor/experimental/common/criterion.py b/neural_compressor/experimental/common/criterion.py deleted file mode 100644 index c3fefdd368b..00000000000 --- a/neural_compressor/experimental/common/criterion.py +++ /dev/null @@ -1,1131 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Initialize critetion classes. - -Classes includes: - TensorFlowCrossEntropyLoss, PyTorchCrossEntropyLoss, - TensorflowKnowledgeDistillationLoss, PyTorchKnowledgeDistillationLoss, - PyTorchIntermediateLayersKnowledgeDistillationLoss. -""" - -from collections import Counter - -import numpy as np -from deprecated import deprecated - -from neural_compressor.adaptor.pytorch import pytorch_forward_wrapper -from neural_compressor.utils import logger -from neural_compressor.utils.utility import LazyImport, singleton - -torch = LazyImport("torch") -tf = LazyImport("tensorflow") - - -@deprecated(version="2.0") -@singleton -class TensorflowCriterions(object): - """Record criterions in TensorflowCriterions class.""" - - def __init__(self): - """Initialize the TensorflowCriterions class.""" - self.criterions = {} - self.criterions.update(TENSORFLOW_CRITERIONS) - - -@deprecated(version="2.0") -@singleton -class PyTorchCriterions(object): - """Record criterions in PyTorchCriterions class.""" - - def __init__(self): - """Initialize the TensorflowCriterions class.""" - self.criterions = {} - self.criterions.update(PYTORCH_CRITERIONS) - - -framework_criterions = { - "tensorflow": TensorflowCriterions, - "pytorch": PyTorchCriterions, - "pytorch_fx": PyTorchCriterions, -} - -# user/model specific criterions will be registered here -TENSORFLOW_CRITERIONS = {} -PYTORCH_CRITERIONS = {} - -registry_criterions = { - "tensorflow": TENSORFLOW_CRITERIONS, - "pytorch": PYTORCH_CRITERIONS, - "pytorch_fx": PYTORCH_CRITERIONS, -} - - -@deprecated(version="2.0") -class Criterions(object): - """Integrate criterions of different framework.""" - - def __init__(self, framework): - """Initialize the Criterions class. - - Args: - framework (string): framework name. - """ - assert framework in ("tensorflow", "pytorch", "pytorch_fx"), "framework support tensorflow pytorch" - self.criterions = framework_criterions[framework]().criterions - - def __getitem__(self, criterion_type): - """Fetch criterion result by criterion type. - - Args: - criterion_type (string): criterion type. - - Returns: - cls: criterion class. - """ - assert criterion_type in self.criterions.keys(), "only support criterions in {}".format(self.criterions.keys()) - - return self.criterions[criterion_type] - - def register(self, name, criterion_cls): - """Register criterion name and result in existing criterion class. - - Args: - name (string): criterion name/type. - criterion_cls (string): criterion class. - """ - assert name not in self.criterions.keys(), "registered criterion name already exists." - self.criterions.update({name: criterion_cls}) - - -@deprecated(version="2.0") -def criterion_registry(criterion_type, framework): - """Use to register criterion classes in registry_criterions. - - Args: - criterion_type (str): The string of supported criterion. - framework (str): The string of supported framework. - - Returns: - cls: The class of register. - """ - - def decorator_criterion(cls): - """Decorate criterion class to check framework and criterion name.""" - for fw in [fwk.strip() for fwk in framework.split(",")]: - assert fw in ["tensorflow", "pytorch"], "The framework support tensorflow pytorch" - - if criterion_type in registry_criterions[fw].keys(): - raise ValueError("Cannot have two criterions with the same name") - registry_criterions[fw][criterion_type] = cls - return cls - - return decorator_criterion - - -@deprecated(version="2.0") -class KnowledgeDistillationFramework(object): - """Knowledge Distillation Framework.""" - - def __init__(self, student_model=None, teacher_model=None): - """Initialize the KnowledgeDistillationFramework class. - - Args: - student_model: student_model. - teacher_model: teacher_model. - """ - self._student_model = student_model - self._teacher_model = teacher_model - - @property - def student_model(self): - """Return student model.""" - return self._student_model - - @student_model.setter - def student_model(self, model): - """Setter of teacher model.""" - self._student_model = model - - @property - def teacher_model(self): - """Return teacher model.""" - return self._teacher_model - - @teacher_model.setter - def teacher_model(self, model): - """Setter of teacher model.""" - self._teacher_model = model - - -@deprecated(version="2.0") -class KnowledgeDistillationLoss(KnowledgeDistillationFramework): - """Initialize the KnowledgeDistillationLoss class.""" - - def __init__( - self, temperature=1.0, loss_types=["CE", "CE"], loss_weights=[0.5, 0.5], student_model=None, teacher_model=None - ): - """Initialize Knowledge Distillation Loss class. - - Args: - temperature (float, optional): Hyperparameters that control the entropy - of probability distributions. Defaults to 1.0. - loss_types (list, optional): loss type. Defaults to ['CE', 'CE']. - loss_weights (list, optional): loss weights. Defaults to [0.5, 0.5]. - student_model (model, optional): student model. Defaults to None. - teacher_model (model, optional): teacher model. Defaults to None. - """ - super(KnowledgeDistillationLoss, self).__init__(student_model=student_model, teacher_model=teacher_model) - self.teacher_outputs = None - self.temperature = temperature - self.loss_weights = loss_weights - self.loss_types = loss_types - self.teacher_student_loss = self.student_targets_loss = None - assert len(loss_weights) == len(loss_types) == 2, ( - "Wrong length for " + "loss_weights or loss_types, should be 2." - ) - assert sum(loss_weights) == 1.0, "Sum of loss_weights should be 1.0." - - def teacher_model_forward(self, input, teacher_model=None): - """Define parameters for teacher_model_forward function. - - Args: - input (tensor, tuple or dict): input data. - teacher_model (model, optional): teacher model. Defaults to None. - - Raises: - NotImplementedError: NotImplementedError - """ - raise NotImplementedError("Function teacher_model_forward " "should be framework related.") - - def teacher_student_loss_cal(self, student_outputs, teacher_outputs): - """Define parameters for teacher_student_loss_cal function. - - Args: - student_outputs (tensor): student outputs - teacher_outputs (tensor): student outputs - - Raises: - NotImplementedError: NotImplementedError - """ - raise NotImplementedError("Function teacher_student_loss_cal " "should be framework related.") - - def student_targets_loss_cal(self, student_outputs, targets): - """Define parameters for student_targets_loss_cal function. - - Args: - student_outputs (tensor): student outputs - targets (tensor): groud truth label - - Raises: - NotImplementedError: NotImplementedError - """ - raise NotImplementedError("Function student_targets_loss_cal " "should be framework related.") - - def loss_cal(self, student_outputs, targets): - """Calculate loss of student model. - - Args: - student_outputs (tensor): student outputs - targets (tensor): groud truth label - - Returns: - tensor: loss - """ - return self.student_targets_loss_cal(student_outputs, targets) - - def loss_cal_sloss(self, student_outputs, teacher_outputs, student_loss): - """Calculate all losses between student model and teacher model. - - Args: - student_outputs (tensor): student outputs - teacher_outputs (tensor): teacher outputs - student_loss (tensor): student loss - - Returns: - tensor: loss - """ - if self.loss_weights[0] > 0: - origin_loss = student_loss - else: - origin_loss = 0 - - if self.loss_weights[1] > 0: - student_out_ = student_outputs / self.temperature - teacher_out_ = teacher_outputs / self.temperature - distillation_loss = self.teacher_student_loss_cal(student_out_, teacher_out_) - distillation_loss *= self.temperature**2 - else: - distillation_loss = 0 - - self.loss = origin_loss * self.loss_weights[0] + distillation_loss * self.loss_weights[1] - return self.loss - - def __call__(self, student_outputs, targets): - """Call the class to calculate loss. - - Args: - student_outputs (tensor): student outputs - targets (tensor): groud truth label - - Returns: - tensor: loss - """ - return self.loss_cal(student_outputs, targets) - - -@deprecated(version="2.0") -class PyTorchKnowledgeDistillationLoss(KnowledgeDistillationLoss): - """The PyTorchKnowledgeDistillationLoss class inherits from KnowledgeDistillationLoss.""" - - def __init__( - self, temperature=1.0, loss_types=["CE", "CE"], loss_weights=[0.5, 0.5], student_model=None, teacher_model=None - ): - """Initialize PyTorch Knowledge Distillation Loss class. - - Args: - temperature (float, optional): Hyperparameters that control the entropy - of probability distributions. Defaults to 1.0. - loss_types (list, optional): loss types. Defaults to ['CE', 'CE']. - loss_weights (list, optional): loss weights. Defaults to [0.5, 0.5]. - student_model (torch.nn.model, optional): student model. Defaults to None. - teacher_model (torch.nn.model, optional): teacher model. Defaults to None. - - Raises: - NotImplementedError: NotImplementedError - NotImplementedError: NotImplementedError - """ - super(PyTorchKnowledgeDistillationLoss, self).__init__( - temperature=temperature, - loss_types=loss_types, - loss_weights=loss_weights, - student_model=student_model, - teacher_model=teacher_model, - ) - if self.student_targets_loss is None: - if self.loss_types[0] == "CE": - self.student_targets_loss = torch.nn.CrossEntropyLoss() - elif self.loss_types[0] == "MSE": - self.student_targets_loss = torch.nn.MSELoss() - else: - raise NotImplementedError( - "Now we only support CrossEntropyLoss and MSELoss " - "for loss of student model output with respect to targets." - ) - logger.info("student_targets_loss: {}, {}".format(self.loss_types[0], self.loss_weights[0])) - if self.teacher_student_loss is None: - if self.loss_types[1] == "CE": - self.teacher_student_loss = self.SoftCrossEntropy - elif self.loss_types[1] == "KL": - self.teacher_student_loss = self.KullbackLeiblerDivergence - elif self.loss_types[1] == "MSE": - self.teacher_student_loss = torch.nn.MSELoss() - else: - raise NotImplementedError( - "Now we only support CrossEntropyLoss KL Divergence" - " and MSELoss for loss of student model output with respect to teacher model output." - ) - logger.info("teacher_student_loss: {}, {}".format(self.loss_types[1], self.loss_weights[1])) - - def SoftCrossEntropy(self, logits, targets): - """Return SoftCrossEntropy. - - Args: - logits (tensor): output logits - targets (tensor): ground truth label - - Returns: - tensor: SoftCrossEntropy - """ - log_prob = torch.nn.functional.log_softmax(logits, dim=-1) - targets_prob = torch.nn.functional.softmax(targets, dim=-1) - return (-targets_prob * log_prob).sum(dim=-1).mean() - - def KullbackLeiblerDivergence(self, logits, targets): - """Return KullbackLeiblerDivergence. - - Args: - logits (tensor): output logits - targets (tensor): ground truth label - - Returns: - tensor: KullbackLeiblerDivergence - """ - log_prob = torch.nn.functional.log_softmax(logits, dim=-1) - targets_prob = torch.nn.functional.softmax(targets, dim=-1) - return torch.nn.functional.kl_div(log_prob, targets_prob) - - def teacher_model_forward(self, input, teacher_model=None, device=None): - """Teacher model forward. - - Args: - input (tensor): input data - teacher_model (torch.nn.model, optional): teacher model. Defaults to None. - device (torch.device, optional): device. Defaults to None. - - Returns: - tensor: output - """ - outputs = None - if self.loss_weights[1] > 0: - model = self.teacher_model if teacher_model is None else teacher_model - assert isinstance(model, torch.nn.Module), "Teacher model should be a torch Module instead of {}".format( - type(model) - ) - model.eval() - try: - model_device = next(model.parameters()).device - except: - logger.warning("Cannot get model device, assuming it's in CPU.") - model_device = "cpu" - device = model_device if device is None else device - if device != model_device: - model.to(device) - with torch.no_grad(): - outputs = pytorch_forward_wrapper(model, input) - self.teacher_outputs = outputs - return outputs - - def teacher_student_loss_cal(self, student_outputs, teacher_outputs): - """Calculate loss between student model and teacher model. - - Args: - student_outputs (tensor): student outputs - teacher_outputs (tensor): teacher outputs - - Returns: - tensor: loss - """ - assert self.teacher_student_loss, "teacher_student_loss not specified." - return self.teacher_student_loss(student_outputs, teacher_outputs) - - def student_targets_loss_cal(self, student_outputs, targets): - """Calculate loss of student model. - - Args: - student_outputs (tensor): student outputs - targets (tensor): groud truth label - - Returns: - tensor: loss - """ - assert self.student_targets_loss, "student_targets_loss not specified." - return self.student_targets_loss(student_outputs, targets) - - -@deprecated(version="2.0") -@criterion_registry("KnowledgeDistillationLoss", "pytorch") -class PyTorchKnowledgeDistillationLossWrapper(object): - """PyTorchKnowledgeDistillationLossWrapper wraps PyTorchKnowledgeDistillationLoss.""" - - def __init__(self, param_dict): - """Initialize PyTorch Knowledge Distillation Loss Wrapper. - - Args: - param_dict (dict): parameter dict - """ - self.param_dict = param_dict - - def _param_check(self): - param_dict = self.param_dict - _params = ["temperature", "loss_types", "loss_weights"] - assert all(key in param_dict for key in _params), "Keys {} must be in input parameters.".format(_params) - assert param_dict["temperature"] > 0.0, "Value of temperature must be positive." - assert len(param_dict["loss_types"]) == len( - param_dict["loss_weights"] - ), "Length of loss_types and loss_weights must be the same." - assert all( - type(param_dict[k]) in [list, tuple] for k in ["loss_types", "loss_weights"] - ), "Type of loss_types and loss_weights must be list or tuple." - assert all( - any(isinstance(e, t) for t in [str, torch.nn.Module]) for e in param_dict["loss_types"] - ), "Type of loss_types element must be str or torch Module." - assert ( - all(0.0 <= e <= 1.0 for e in param_dict["loss_weights"]) - and abs(sum(param_dict["loss_weights"]) - 1.0) < 1e-9 - ), "Element of loss_weights must be in interval [0, 1] and summed to 1.0." - new_dict = {} - for k in _params: - new_dict[k] = param_dict[k] - return new_dict - - def __call__(self, **kwargs): - """Return PyTorchKnowledgeDistillationLoss, param dict. - - Returns: - PyTorchKnowledgeDistillationLoss (class): PyTorchKnowledgeDistillationLoss - param dict (dict): param dict - """ - return PyTorchKnowledgeDistillationLoss, self._param_check() - - -@deprecated(version="2.0") -class TensorflowKnowledgeDistillationLossExternal(KnowledgeDistillationLoss): - """TensorflowKnowledgeDistillationLossExternal inherits from KnowledgeDistillationLoss.""" - - def __init__( - self, temperature=1.0, loss_types=["CE", "CE"], loss_weights=[0.5, 0.5], student_model=None, teacher_model=None - ): - """Initialize Tensorflow Knowledge Distillation Loss class. - - Args: - temperature (float, optional): Hyperparameters that control the entropy - of probability distributions. Defaults to 1.0. - loss_types (list, optional): loss types. Defaults to ['CE', 'CE']. - loss_weights (list, optional): loss weights. Defaults to [0.5, 0.5]. - student_model (optional): student model. Defaults to None. - teacher_model (optional): teacher model. Defaults to None. - - Raises: - NotImplementedError: NotImplementedError - NotImplementedError: NotImplementedError - """ - super(TensorflowKnowledgeDistillationLossExternal, self).__init__( - temperature=temperature, - loss_types=loss_types, - loss_weights=loss_weights, - student_model=student_model, - teacher_model=teacher_model, - ) - if self.student_targets_loss is None: - if self.loss_types[0] == "CE": - self.student_targets_loss = tf.keras.losses.CategoricalCrossentropy() - else: - raise NotImplementedError( - "Now we only support CrossEntropyLoss " "for loss of student model output with respect to targets." - ) - logger.info("student_targets_loss: {}, {}".format(self.loss_types[0], self.loss_weights[0])) - if self.teacher_student_loss is None: - if self.loss_types[1] == "CE": - self.teacher_student_loss = tf.keras.losses.CategoricalCrossentropy() - elif self.loss_types[1] == "KL": - self.teacher_student_loss = tf.keras.losses.KLDivergence() - else: - raise NotImplementedError( - "Now we only support CrossEntropyLoss" - " for loss of student model output with respect to teacher model output." - ) - logger.info("teacher_student_loss: {}, {}".format(self.loss_types[1], self.loss_weights[1])) - - def teacher_model_forward(self, input, teacher_model=None): - """Teacher model forward. - - Args: - input (tensor): input data - teacher_model (optional): teacher model. Defaults to None. - device (optional): device. Defaults to None. - - Returns: - tensor: output - """ - outputs = None - if self.loss_weights[1] > 0 and input is not None: - model = self.teacher_model if teacher_model is None else teacher_model - if isinstance(input, list) or isinstance(input, tuple): # pragma: no cover - outputs = model(*input, training=True) - elif isinstance(input, dict): # pragma: no cover - outputs = model(**input, training=True) - else: - outputs = model(input, training=True) - self.teacher_outputs = outputs - return outputs - - def teacher_student_loss_cal(self, student_outputs, teacher_outputs): - """Calculate loss between student model and teacher model. - - Args: - student_outputs (tensor): student outputs - teacher_outputs (tensor): teacher outputs - - Returns: - tensor: loss - """ - assert self.teacher_student_loss, "teacher_student_loss not specified." - return self.teacher_student_loss(teacher_outputs, student_outputs) - - def student_targets_loss_cal(self, student_outputs, targets): - """Calculate loss of student model. - - Args: - student_outputs (tensor): student outputs - targets (tensor): groud truth label - - Returns: - tensor: loss - """ - assert self.student_targets_loss, "student_targets_loss not specified." - return self.student_targets_loss(targets, student_outputs) - - -@deprecated(version="2.0") -class IntermediateLayersKnowledgeDistillationLoss(KnowledgeDistillationFramework): - """The IntermediateLayersKnowledgeDistillationLoss class inherits from KnowledgeDistillationLoss.""" - - def __init__( - self, - layer_mappings=[], - loss_types=None, - loss_weights=None, - add_origin_loss=False, - student_model=None, - teacher_model=None, - ): - """Initialize PyTorch Knowledge Distillation Loss class. - - Args: - temperature (float, optional): Hyperparameters that control the entropy - of probability distributions. Defaults to 1.0. - loss_types (list, optional): loss types. Defaults to ['CE', 'CE']. - loss_weights (list, optional): loss weights. Defaults to [0.5, 0.5]. - student_model (torch.nn.model, optional): student model. Defaults to None. - teacher_model (torch.nn.model, optional): teacher model. Defaults to None. - - Raises: - NotImplementedError: NotImplementedError - NotImplementedError: NotImplementedError - """ - super(IntermediateLayersKnowledgeDistillationLoss, self).__init__( - student_model=student_model, teacher_model=teacher_model - ) - self.student_features = {} - self.teacher_features = {} - - self.layer_mappings = [] - self.layer_output_process = [] - for item in layer_mappings: - assert len(item) == 1 or len(item) == 2, ( - "Each item in layer_mappings " - + "should be a list or tuple containing 1 list or 2 lists, with format " - + "[(layer_name, )] or [(student_layer_name, ), (teacher_layer_name, )], " - + "first one is the abbreviation for cases that student_layer_name and teacher_layer_name " - + "are the same. The length of tuples in the list could be either 1 like previous cases, " - + "or 2, like [(layer_name, layer_output_process)] or " - + "[(student_layer_name, student_layer_output_process), " - + "(teacher_layer_name, teacher_layer_output_process)]." - + "For example, with 2 tuples of length 2, element looks like " - + "[('student_model.layer1.attention', '1'), ('teacher_model.layer1.attention', '1')], " - + "where 'student_model.layer1.attention' and 'teacher_model.layer1.attention' " - + "represent attention module on layer 1 of the student model and the " - + "teacher model respectively, two '1' represent the index to retrieve the " - + "desired output from the defined module's outputs, in this case, the above " - + "two module's outputs are lists, with desired output in index 1 of these " - + "lists, in cases of dict output, retrieving can be done by defining the " - + "corresponding key, in cases of module's output is the desired output, " - + "just adopt the format such as [('student_model.layer1.output" - + ".output', ), ('teacher_model.layer1.output', )]." - ) - if len(item) == 1: - item = [item[0], item[0]] - for i in range(len(item)): - if not isinstance(item[i], (list, tuple)): - item[i] = [item[i], ""] - elif len(item[i]) == 1: - item[i] = [item[i][0], ""] - else: - assert len(item[i]) == 2, "Expect {} to be a tuple of length 1 or 2.".format(item[i]) - self.layer_mappings.append((item[0][0], item[1][0])) - self.layer_output_process.append((item[0][1], item[1][1])) - for student_layer, teacher_layer in self.layer_mappings: - self.student_features[student_layer] = [] - self.teacher_features[teacher_layer] = [] - - self.loss_weights = ( - [1.0 / len(layer_mappings)] * len(layer_mappings) - if (loss_weights is None or loss_weights == []) - else loss_weights - ) - self.loss_types = ["MSE"] * len(layer_mappings) if (loss_types is None or loss_types == []) else loss_types - self.add_origin_loss = add_origin_loss - self.loss_funcs = [] - self.feature_matchers = None - self.init_loss_funcs() - assert len(self.layer_mappings) == len(self.loss_weights) == len(self.loss_types), ( - f"Wrong length for layer_mappings:{self.layer_mappings}, " - + f"loss_weights:{self.loss_weights} or loss_types:{self.loss_types}, " - + "all should be the same." - ) - - def init_loss_funcs(self): - """Init loss funcs. - - Raises: - NotImplementedError: NotImplementedError - """ - raise NotImplementedError("Function init_loss_funcs " "should be framework related.") - - def init_feature_matcher(self, student_feature, teacher_feature): - """Init feature matcher. - - Raises: - NotImplementedError: NotImplementedError - """ - raise NotImplementedError("Function init_feature_matcher " "should be framework related.") - - def teacher_model_forward(self, input, teacher_model=None): - """Teacher model forward. - - Raises: - NotImplementedError: NotImplementedError - """ - raise NotImplementedError("Function teacher_model_forward " "should be framework related.") - - def loss_cal(self): - """Calculate loss. - - Raises: - NotImplementedError: NotImplementedError - """ - raise NotImplementedError("Function loss_cal should be framework related.") - - def loss_cal_sloss(self, student_outputs, teacher_outputs, student_loss): - """Calculate all losses between student model and teacher model. - - Args: - student_outputs (tensor): student outputs - teacher_outputs (tensor): teacher outputs - student_loss (tensor): student loss - - Returns: - tensor: loss - """ - return self.loss_cal() - - def clear_features(self): - """Clean features in list.""" - for student_layer in self.student_features: - self.student_features[student_layer] = [] - for teacher_layer in self.teacher_features: - self.teacher_features[teacher_layer] = [] - - def __call__(self, student_outputs, targets): - """Return 0.""" - return 0 - - -@deprecated(version="2.0") -class PyTorchIntermediateLayersKnowledgeDistillationLoss(IntermediateLayersKnowledgeDistillationLoss): - """PyTorch Intermediate Layers Knowledge Distillation Loss.""" - - def __init__( - self, - layer_mappings=[], - loss_types=None, - loss_weights=None, - add_origin_loss=False, - student_model=None, - teacher_model=None, - ): - """Initialize PyTorch Knowledge Distillation Loss class. - - Args: - temperature (float, optional): Hyperparameters that control the entropy - of probability distributions. Defaults to 1.0. - loss_types (list, optional): loss types. Defaults to ['CE', 'CE']. - loss_weights (list, optional): loss weights. Defaults to [0.5, 0.5]. - student_model (optional): student model. Defaults to None. - teacher_model (optional): teacher model. Defaults to None. - - Raises: - NotImplementedError: NotImplementedError - NotImplementedError: NotImplementedError - """ - super(PyTorchIntermediateLayersKnowledgeDistillationLoss, self).__init__( - layer_mappings=layer_mappings, - loss_types=loss_types, - loss_weights=loss_weights, - add_origin_loss=add_origin_loss, - student_model=student_model, - teacher_model=teacher_model, - ) - self.register_hooks_for_models() - - def register_hooks_for_models(self): - """Register hooks for models to record module output. - - Raises: - AttributeError: AttributeError - """ - from neural_compressor.experimental.common import torch_utils - - def register_model_forward_hook(model, path, output_process="", student=False): - nodes = path.split(".") - module = model - for node in nodes: - try: - module = module.__getattr__(node) - except: - raise AttributeError("There is no path {} in the model.".format(path)) - return module.register_forward_hook(torch_utils.get_activation(path, output_process, student)) - - assert isinstance(self.student_model, torch.nn.Module) and isinstance(self.teacher_model, torch.nn.Module), ( - "Expect student_model and teacher_model to be an torch.nn.Module object, " - + "got student_model:{} and teacher_model:{}".format(type(self.student_model), type(self.teacher_model)) - ) - self.hook_handles = [] - for idx in range(len(self.layer_mappings)): - student_layer, teacher_layer = self.layer_mappings[idx] - student_output_process, teacher_output_process = self.layer_output_process[idx] - st_handle = register_model_forward_hook(self.student_model, student_layer, student_output_process, True) - te_handle = register_model_forward_hook(self.teacher_model, teacher_layer, teacher_output_process) - torch_utils.STUDENT_FEATURES = self.student_features - torch_utils.TEACHER_FEATURES = self.teacher_features - self.hook_handles.extend([st_handle, te_handle]) - - def remove_all_hooks(self): - """Remove all hooks.""" - for hook in self.hook_handles: - hook.remove() - - def init_loss_funcs(self): - """Init loss funcs.""" - for loss_type in self.loss_types: - if loss_type == "MSE": - loss_func = torch.nn.MSELoss() - elif loss_type == "KL": - loss_func = torch.nn.KLDivLoss() - elif loss_type == "L1": - loss_func = torch.nn.L1Loss() - else: - raise NotImplementedError( - f"Unsupported loss type {loss_type}, supported loss is " - "MSE for mean squared error, KL for Kullback-Leibler divergence and " - "L1 for L1 loss." - ) - self.loss_funcs.append(loss_func) - - def init_feature_matcher(self, student_feature, teacher_feature): - """Init feature matcher. - - Args: - student_feature (tensor): student feature - teacher_feature (tensor): teacher feature - - Returns: - pytorch_linear_feature_matcher - """ - - class pytorch_linear_feature_matcher(torch.nn.Module): - def __init__(self, src_shape, dst_shape): - super().__init__() - shape_diff = [abs(i - j) for i, j in zip(dst_shape, src_shape)] - assert shape_diff.count(0) == len(shape_diff) - 1, ( - "Expect only one " + "different dimension between student_feature and teacher_feature." - ) - self.dim_idx = np.argmax(shape_diff) - self.dense = torch.nn.Linear(src_shape[self.dim_idx], dst_shape[self.dim_idx]) - - def forward(self, input): - output = torch.transpose(input, self.dim_idx, -1) - if input.device != next(self.parameters()).device: - self.to(input.device) - output = self.dense(output) - output = torch.transpose(output, self.dim_idx, -1) - return output - - assert isinstance(student_feature, (torch.Tensor, np.ndarray)) and isinstance( - teacher_feature, (torch.Tensor, np.ndarray) - ), ( - "Expect student_feature and teacher_feature to be torch.Tensor or np.ndarray " - + "objects, got student_feature a {st} object, teacher_feature a {tt} object.".format( - st=type(student_feature), tt=type(teacher_feature) - ) - ) - assert len(student_feature.shape) == len(teacher_feature.shape), ( - "Expect student_feature and teacher_feature to have the same length of shape, " - + "got student_feature of {}, teacher_feature of {}.".format(student_feature.shape, teacher_feature.shape) - ) - if sum([abs(i - j) for i, j in zip(student_feature.shape, teacher_feature.shape)]) == 0: - return lambda x: x - return pytorch_linear_feature_matcher(student_feature.shape, teacher_feature.shape) - - def teacher_model_forward(self, input, teacher_model=None, device=None): - """Define parameters for teacher_model_forward function. - - Args: - input (tensor, tuple or dict): input data. - teacher_model (model, optional): teacher model. Defaults to None. - - Raises: - NotImplementedError: NotImplementedError - """ - model = self.teacher_model if teacher_model is None else teacher_model - assert isinstance(model, torch.nn.Module), "Teacher model should be a torch Module instead of {}".format( - type(model) - ) - model.eval() - try: - model_device = next(model.parameters()).device - except: - logger.warning("Cannot get model device, assuming it's in CPU.") - model_device = "cpu" - device = model_device if device is None else device - if device != model_device: - model.to(device) - with torch.no_grad(): - outputs = pytorch_forward_wrapper(model, input) - return outputs - - def loss_cal_sloss(self, student_outputs, teacher_outputs, student_loss): - """Calculate all losses between student model and teacher model. - - Args: - student_outputs (tensor): student outputs - teacher_outputs (tensor): teacher outputs - student_loss (tensor): student loss - - Returns: - tensor: loss - """ - loss = self.loss_cal() - if self.add_origin_loss: - loss += student_loss - return loss - - def loss_cal(self): - """Calculate loss of student model. - - Returns: - tensor: loss - """ - self.loss = 0 - init_feature_matchers = False - if self.feature_matchers is None: - init_feature_matchers = True - self.feature_matchers = {} - for idx in range(len(self.layer_mappings)): - student_layer, teacher_layer = self.layer_mappings[idx] - student_feature = self.student_features[student_layer] - teacher_feature = self.teacher_features[teacher_layer] - assert len(student_feature) == len(teacher_feature) and len(student_feature) > 0, ( - "Lengths of student_feature and teacher_feature should be the same and larger than 0, " - + "instead of {} and {}, ".format(len(student_feature), len(teacher_feature)) - + "please run student and teacher model forward properly before calculating the loss." - ) - - def device2feature_gen(features): - devices_count = Counter([f.device for f in features]) - assert [1] * len(devices_count) == [ - _ for _ in devices_count.values() - ], "Currently only support 1 feature tensor per device, " + "got {}.".format(devices_count) - return {feat.device: feat for feat in features} - - student_feature = device2feature_gen(student_feature) - teacher_feature = device2feature_gen(teacher_feature) - assert student_feature.keys() == teacher_feature.keys(), ( - "Features from student model have different devices with that of " - + "teacher model, got student: {}, teacher: {}.".format(student_feature.keys(), teacher_feature.keys()) - ) - output_device = ( - torch.device("cuda:0") if torch.device("cuda:0") in student_feature.keys() else torch.device("cpu") - ) - if init_feature_matchers: - feature_matcher = self.init_feature_matcher( - student_feature[output_device], teacher_feature[output_device] - ) - self.feature_matchers[student_layer] = feature_matcher - - tmp_loss = 0 - for device in student_feature.keys(): - student_feature[device] = student_feature[device].to(output_device) - teacher_feature[device] = teacher_feature[device].to(output_device) - stfeat, tefeat = student_feature[device], teacher_feature[device] - stfeat = self.feature_matchers[student_layer](stfeat) - if self.loss_types[idx] == "KL": - check_is_not_prob = lambda x: (torch.abs(x.sum(dim=-1) - 1.0) > 0.2).any().item() - if isinstance(self.feature_matchers[student_layer], torch.nn.Module): - stfeat = torch.nn.LogSoftmax(dim=-1)(stfeat) - else: - if check_is_not_prob(stfeat): - stfeat = torch.softmax(stfeat, dim=-1) - stfeat = torch.log(stfeat + 1e-9) - if check_is_not_prob(tefeat): - tefeat = torch.softmax(tefeat, dim=-1) - tmp_loss += self.loss_funcs[idx](stfeat, tefeat) * self.loss_weights[idx] - self.loss += tmp_loss - self.clear_features() - return self.loss - - -@deprecated(version="2.0") -@criterion_registry("IntermediateLayersKnowledgeDistillationLoss", "pytorch") -class PyTorchIntermediateLayersKnowledgeDistillationLossWrapper(object): - """PyTorch Intermediate Layers Knowledge Distillation Loss Wrapper.""" - - def __init__(self, param_dict): - """Initialize PyTorchIntermediateLayersKnowledgeDistillationLossWrapper class. - - Args: - param_dict (dict): param dict - """ - self.param_dict = param_dict - - def _param_check(self): - param_dict = self.param_dict - _params = ["layer_mappings", "loss_types", "loss_weights", "add_origin_loss"] - layer_mappings = param_dict["layer_mappings"] - if "loss_types" not in param_dict or param_dict["loss_types"] == []: - param_dict["loss_types"] = ["MSE"] * len(layer_mappings) - if "loss_weights" not in param_dict or param_dict["loss_weights"] == []: - param_dict["loss_weights"] = [1.0 / len(layer_mappings)] * len(layer_mappings) - if "add_origin_loss" not in param_dict: - param_dict["add_origin_loss"] = False - assert "layer_mappings" in param_dict, "Key layer_mappings must be in input parameters." - assert all( - type(param_dict[k]) in [list, tuple] for k in ["layer_mappings", "loss_types", "loss_weights"] - ), "Type of loss_types and loss_weights must be list or tuple." - assert isinstance(param_dict["add_origin_loss"], bool), "Type of add_origin_loss should be bool." - assert ( - len(param_dict["layer_mappings"]) == len(param_dict["loss_types"]) == len(param_dict["loss_weights"]) - ), "Length of layer_mappings, loss_types and loss_weights must be the same." - assert all( - type(it) in [list, tuple] and (len(it) == 1 or len(it) == 2) for it in param_dict["layer_mappings"] - ), ( - "Each item in layer_mappings should be a list containing 1 tuple or 2 tuples, with format " - + "[(layer_name, )] or [(student_layer_name, ), (teacher_layer_name, )], " - + "first one is the abbreviation for cases that student_layer_name and teacher_layer_name " - + "are the same. The length of tuples in the list could be either 1 like previous cases, " - + "or 2, like [(layer_name, layer_output_process)] or " - + "[(student_layer_name, student_layer_output_process), " - + "(teacher_layer_name, teacher_layer_output_process)]." - + "For example, with 2 tuples of length 2, element looks like " - + "[('student_model.layer1.attention', '1'), ('teacher_model.layer1.attention', '1')], " - + "where 'student_model.layer1.attention' and 'teacher_model.layer1.attention' " - + "represent attention module on layer 1 of the student model and the " - + "teacher model respectively, two '1' represent the index to retrieve the " - + "desired output from the defined module's outputs, in this case, the above " - + "two module's outputs are lists, with desired output in index 1 of these " - + "lists, in cases of dict output, retrieving can be done by defining the " - + "corresponding key, in cases of module's output is the desired output, " - + "just adopt the format such as [('student_model.layer1.output" - + ".output', ), ('teacher_model.layer1.output', )]." - ) - assert all( - any(isinstance(e, t) for t in [str, torch.nn.Module]) for e in param_dict["loss_types"] - ), "Type of loss_types element must be str or torch Module." - assert all( - 0.0 <= e <= 1.0 for e in param_dict["loss_weights"] - ), "Element of loss_weights must be in interval [0, 1]." - new_dict = {} - for k in _params: - new_dict[k] = param_dict[k] - return new_dict - - def __call__(self, **kwargs): - """Return PyTorchIntermediateLayersKnowledgeDistillationLossWrapper, param dict. - - Returns: - class: PyTorchIntermediateLayersKnowledgeDistillationLoss - param dict (dict): param dict - """ - return PyTorchIntermediateLayersKnowledgeDistillationLoss, self._param_check() - - -@deprecated(version="2.0") -class SelfKnowledgeDistillationLoss(KnowledgeDistillationFramework): - """SelfKnowledge Distillation Loss.""" - - def __init__( - self, - layer_mappings=[], - loss_types=None, - loss_weights=None, - temperature=1.0, - add_origin_loss=False, - student_model=None, - teacher_model=None, - ): - """Initialize SelfKnowledge Distillation Loss class. - - Args: - layer_mappings (list): layers of distillation.Format like - [[[student1_layer_name1, teacher_layer_name1],[student2_layer_name1, teacher_layer_name1]], - [[student1_layer_name2, teacher_layer_name2],[student2_layer_name2, teacher_layer_name2]]] - loss_types (list, optional): loss types. Defaults to ['CE'] * len(layer_mappings). - loss_weights (list, optional): loss weights. Defaults to [1.0 / len(layer_mappings)] * - len(layer_mappings).temperature (float, optional): use to calculate the soft label CE. - temperature (optional): temperature. Defaults to 1.0. - add_origin_loss (bool, optional): whether to add origin loss for hard label loss. - student_model (optional): student model. Defaults to None. - teacher_model (optional): teacher model. Defaults to None. - """ - super(SelfKnowledgeDistillationLoss, self).__init__(student_model=student_model, teacher_model=teacher_model) - self.temperature = temperature - self.layer_mappings = [] - for items in layer_mappings: - for value in items: - assert len(value) == 2, ( - "Each item in layer_mappings " - + "should be a list or tuple of length 2, with format " - + "[student_layer_name, teacher_layer_name]." - ) - self.layer_mappings.append(items) - - self.loss_weights = ( - [1.0 / len(self.layer_mappings)] * len(self.layer_mappings) if loss_weights is None else loss_weights - ) - self.loss_types = ["CE"] * len(self.layer_mappings) if loss_types is None else loss_types - self.add_origin_loss = add_origin_loss - self.loss_funcs = [] - self.init_loss_funcs() - assert len(self.layer_mappings) == len(self.loss_weights) == len(self.loss_types), ( - f"Wrong length for layer_mappings:{self.layer_mappings}, " - + f"loss_weights:{self.loss_weights} or loss_types:{self.loss_types}, " - + "all should be the same." - ) - - def init_loss_funcs(self): - """Init loss funcs. - - Raises: - NotImplementedError: NotImplementedError - """ - raise NotImplementedError("Function init_loss_funcs " "should be framework related.") - - def teacher_model_forward(self, input, teacher_model=None): - """Teacher model forward. - - Raises: - NotImplementedError: NotImplementedError - """ - raise NotImplementedError("Function teacher_model_forward " "should be framework related.") - - def loss_cal(self, student_outputs): - """Calculate loss. - - Raises: - NotImplementedError: NotImplementedError - """ - raise NotImplementedError("Function loss_cal should be framework related.") - - def loss_cal_sloss(self, student_outputs, teacher_outputs, student_loss): - """Calculate all losses between student model and teacher model. - - Args: - student_outputs (dict): student outputs - teacher_outputs (dict): teacher outputs - student_loss (tensor): student loss - - Returns: - tensor: loss - """ - loss = self.loss_cal(student_outputs) - if self.add_origin_loss: - loss += student_loss - return loss - - def __call__(self, student_outputs, targets): - """Return 0.""" - return 0 diff --git a/neural_compressor/experimental/common/dataloader.py b/neural_compressor/experimental/common/dataloader.py deleted file mode 100644 index 203d0e23b49..00000000000 --- a/neural_compressor/experimental/common/dataloader.py +++ /dev/null @@ -1,121 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Common DataLoader just collects the information to construct a dataloader.""" -from deprecated import deprecated - -from ..data import DATALOADERS - - -@deprecated(version="2.0") -class DataLoader(object): - """A wrapper of the information needed to construct a dataloader. - - This class can't yield batched data and only in this Quantization/Benchmark - object's setter method a 'real' calib_dataloader will be created, the reason - is we have to know the framework info and only after the Quantization/Benchmark - object created then framework information can be known. Future we will support - creating iterable dataloader from neural_compressor.experimental.common.DataLoader - """ - - def __init__( - self, - dataset, - batch_size=1, - collate_fn=None, - last_batch="rollover", - sampler=None, - batch_sampler=None, - num_workers=0, - pin_memory=False, - shuffle=False, - distributed=False, - ): - """Initialize a Dataloader with needed information. - - Args: - dataset (object): A dataset object from which to get data. Dataset must implement - __iter__ or __getitem__ method. - batch_size (int, optional): How many samples per batch to load. Defaults to 1. - collate_fn (Callable, optional): Callable function that processes the batch you - want to return from your dataloader. Defaults to None. - last_batch (str, optional): How to handle the last batch if the batch size does - not evenly divide by the number of examples in the dataset. 'discard': throw - it away. 'rollover': insert the examples to the beginning of the next batch. - Defaults to 'rollover'. - sampler (Iterable, optional): Defines the strategy to draw samples from the dataset. - Defaults to None. - batch_sampler (Iterable, optional): Returns a batch of indices at a time. Defaults to None. - num_workers (int, optional): how many subprocesses to use for data loading. - 0 means that the data will be loaded in the main process. Defaults to 0. - pin_memory (bool, optional): If True, the data loader will copy Tensors into device - pinned memory before returning them. Defaults to False. - shuffle (bool, optional): Set to ``True`` to have the data reshuffled - at every epoch. Defaults to False. - distributed (bool, optional): Set to ``True`` to support distributed computing. - Defaults to False. - """ - assert hasattr(dataset, "__iter__") or hasattr( - dataset, "__getitem__" - ), "dataset must implement __iter__ or __getitem__ magic method!" - self.dataset = dataset - self.batch_size = batch_size - self.collate_fn = collate_fn - self.last_batch = last_batch - self.sampler = sampler - self.batch_sampler = batch_sampler - self.num_workers = num_workers - self.pin_memory = pin_memory - self.shuffle = shuffle - self.distributed = distributed - - -@deprecated(version="2.0") -def _generate_common_dataloader(dataloader, framework, distributed=False): - """Generate common dataloader. - - Args: - dataloader (generator): A dataloader which can yield tuple of (input, label)/(input, _) - batched data. - framework (str): The string of supported framework. - distributed (bool, optional): Set to ``True`` to support distributed computing. - Defaults to False. - - Returns: - BaseDataLoader: neural_compressor built-in dataloader - """ - if not isinstance(dataloader, DataLoader): - assert hasattr(dataloader, "__iter__") and hasattr( - dataloader, "batch_size" - ), "dataloader must implement __iter__ method and batch_size attribute" - assert ( - not distributed - ), "Please use \ - neural_compressor.experimental.common.DataLoader to support distributed computing" - return dataloader - else: - return DATALOADERS[framework]( - dataset=dataloader.dataset, - batch_size=dataloader.batch_size, - collate_fn=dataloader.collate_fn, - last_batch=dataloader.last_batch, - sampler=dataloader.sampler, - batch_sampler=dataloader.batch_sampler, - num_workers=dataloader.num_workers, - pin_memory=dataloader.pin_memory, - shuffle=dataloader.shuffle, - distributed=bool(dataloader.distributed or distributed), - ) diff --git a/neural_compressor/experimental/common/metric.py b/neural_compressor/experimental/common/metric.py deleted file mode 100644 index 7855cc5ead8..00000000000 --- a/neural_compressor/experimental/common/metric.py +++ /dev/null @@ -1,40 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Common Metric just collects the information to construct a Metric.""" -from deprecated import deprecated - - -@deprecated(version="2.0") -class Metric(object): - """A wrapper of the information needed to construct a Metric. - - The metric class should take the outputs of the model as the metric's inputs, - neural_compressor built-in metric always take (predictions, labels) as inputs, it's - recommended to design metric_cls to take (predictions, labels) as inputs. - """ - - def __init__(self, metric_cls, name="user_metric", **kwargs): - """Initialize a Metric with needed information. - - Args: - metric_cls (cls): Should be a sub_class of neural_compressor.metric.BaseMetric, - which takes (predictions, labels) as inputs - name (str, optional): Name for metric. Defaults to 'user_metric'. - """ - self.metric_cls = metric_cls - self.name = name - self.kwargs = kwargs diff --git a/neural_compressor/experimental/common/model.py b/neural_compressor/experimental/common/model.py deleted file mode 100644 index eccf9e52c87..00000000000 --- a/neural_compressor/experimental/common/model.py +++ /dev/null @@ -1,72 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Common Model just collects the information to construct a Model.""" -from deprecated import deprecated - -from neural_compressor.model.model import MODELS, get_model_fwk_name -from neural_compressor.model.tensorflow_model import get_model_type -from neural_compressor.utils import logger - -BACKEND = "default" - - -@deprecated(version="2.0") -class Model(object): - """A wrapper of the information needed to construct a Model.""" - - def __new__(cls, root, **kwargs): - """Create a new instance object of Model. - - Args: - root (object): raw model format. For Tensorflow model, could be path to frozen pb file, - path to ckpt or savedmodel folder, loaded estimator/graph_def/graph/keras model object. - For PyTorch model, it's torch.nn.model instance. For MXNet model, it's mxnet.symbol.Symbol - or gluon.HybirdBlock instance. For ONNX model, it's path to onnx model or loaded ModelProto - model object. - - Returns: - BaseModel: neural_compressor built-in model - """ - framework = kwargs.get("framework", "NA") - if framework == "NA": - framework = get_model_fwk_name(root) - - if "tensorflow" in framework: - if "modelType" in kwargs: - model_type = kwargs["modelType"] - else: - model_type = get_model_type(root) - if model_type == "AutoTrackable": # pragma: no cover - model = MODELS["tensorflow"]("keras", root, **kwargs) - else: - model = MODELS["tensorflow"](model_type, root, **kwargs) - elif framework == "keras": - model = MODELS["keras"](root, **kwargs) - elif framework == "pytorch": - if BACKEND != "default": - framework = BACKEND - model = MODELS[framework](root, **kwargs) - else: - model = MODELS[framework](root, **kwargs) - return model - - -@deprecated(version="2.0") -def set_backend(backend: str): - """Set backed from configure file.""" - global BACKEND - BACKEND = backend diff --git a/neural_compressor/experimental/common/optimizer.py b/neural_compressor/experimental/common/optimizer.py deleted file mode 100644 index 7da1d7cc418..00000000000 --- a/neural_compressor/experimental/common/optimizer.py +++ /dev/null @@ -1,205 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ============================================================================== -"""Intel Neural Compressor built-in Optimizers on multiple framework backends.""" - -from deprecated import deprecated - -from neural_compressor.utils.utility import LazyImport, singleton - -torch = LazyImport("torch") -tf = LazyImport("tensorflow") -tfa = LazyImport("tensorflow_addons") - - -@deprecated(version="2.0") -@singleton -class TensorflowOptimizers(object): - """Class to get all registered TensorFlow Optimizers once only.""" - - def __init__(self): - """Initialize `TensorflowOptimizers` class once only.""" - self.optimizers = {} - self.optimizers.update(TENSORFLOW_OPTIMIZERS) - - -@deprecated(version="2.0") -@singleton -class PyTorchOptimizers(object): - """Class to get all registered PyTorch Optimizers once only.""" - - def __init__(self): - """Initialize `PyTorchOptimizers` class once only.""" - self.optimizers = {} - self.optimizers.update(PYTORCH_OPTIMIZERS) - - -framework_optimizers = { - "tensorflow": TensorflowOptimizers, - "pytorch": PyTorchOptimizers, - "pytorch_fx": PyTorchOptimizers, -} - -# user/model specific optimizers will be registered here -TENSORFLOW_OPTIMIZERS = {} -PYTORCH_OPTIMIZERS = {} - -registry_optimizers = { - "tensorflow": TENSORFLOW_OPTIMIZERS, - "pytorch": PYTORCH_OPTIMIZERS, - "pytorch_fx": PYTORCH_OPTIMIZERS, -} - - -@deprecated(version="2.0") -class Optimizers(object): - """Main entry to get the specific type of optimizer.""" - - def __init__(self, framework): - """Initialize `Optimizers` class.""" - assert framework in ("tensorflow", "pytorch", "pytorch_fx"), "framework support tensorflow pytorch" - self.optimizers = framework_optimizers[framework]().optimizers - - def __getitem__(self, optimizer_type): - """Return the specific type of optimizer object according to the given optimizer_type.""" - assert optimizer_type in self.optimizers.keys(), "only support optimizers in {}".format(self.optimizers.keys()) - - return self.optimizers[optimizer_type] - - def register(self, name, optimizer_cls): - """Allow registration of non-built-in optimizers.""" - assert name not in self.optimizers.keys(), "registered optimizer name already exists." - self.optimizers.update({name: optimizer_cls}) - - -@deprecated(version="2.0") -def optimizer_registry(optimizer_type, framework): - """Class decorator used to register all Optimizer subclasses. - - Cross framework optimizer is supported by add param as framework='tensorflow, pytorch' - - Args: - optimizer_type (str): The string of supported criterion. - framework (str): The string of supported framework. - - Returns: - cls: The class of register. - """ - - def decorator_optimizer(cls): - for fw in [fwk.strip() for fwk in framework.split(",")]: - assert fw in ["tensorflow", "pytorch"], "The framework support tensorflow pytorch" - - if optimizer_type in registry_optimizers[fw].keys(): - raise ValueError("Cannot have two optimizers with the same name") - registry_optimizers[fw][optimizer_type] = cls - return cls - - return decorator_optimizer - - -@deprecated(version="2.0") -@optimizer_registry("SGD", "tensorflow") -class TensorFlowSGD(object): - """TensorFlow keras SGD optimizer. - - Args: - param_dict (dict): The dict of parameters setting by user for SGD optimizer - """ - - def __init__(self, param_dict): - """Initialize `TensorFlowSGD` class.""" - assert isinstance(param_dict, dict), "This optimizer constructor parameter must be a dict" - self._param_dict = param_dict - - def _mapping(self): - _param_map = {"learning_rate": "learning_rate", "momentum": "momentum", "nesterov": "nesterov"} - _dict = {} - for key in self._param_dict: - if key in _param_map: - _dict.update({_param_map[key]: self._param_dict[key]}) - return _dict - - def __call__(self, **kwargs): - """Call `TensorFlowSGD` object.""" - return tf.keras.optimizers.SGD, self._mapping(**kwargs) - - -@deprecated(version="2.0") -@optimizer_registry("AdamW", "tensorflow") -class TensorFlowAdamW(object): - """tensorflow_addons AdamW optimizer. - - Args: - param_dict (dict): The dict of parameters setting by user for AdamW optimizer - """ - - def __init__(self, param_dict): - """Initialize `TensorFlowAdamW` class.""" - assert isinstance(param_dict, dict), "This optimizer constructor parameter must be a dict" - self._param_dict = param_dict - - def _mapping(self): - _param_map = { - "learning_rate": "learning_rate", - "weight_decay": "weight_decay", - "beta_1": "beta_1", - "beta_2": "beta_2", - "epsilon": "epsilon", - "amsgrad": "amsgrad", - } - _dict = {} - for key in self._param_dict: - if key in _param_map: - _dict.update({_param_map[key]: self._param_dict[key]}) - return _dict - - def __call__(self, **kwargs): - """Call `TensorFlowAdamW` object.""" - return tfa.optimizers.AdamW, self._mapping(**kwargs) - - -@deprecated(version="2.0") -@optimizer_registry("SGD", "pytorch") -class PyTorchSGD(object): - """PyTorch SGD optimizer. - - Args: - param_dict (dict): The dict of parameters setting by user for SGD optimizer - """ - - def __init__(self, param_dict): - """Initialize `PyTorchSGD` class.""" - assert isinstance(param_dict, dict), "This optimizer constructor parameter must be a dict" - self._param_dict = param_dict - - def _mapping(self): - _param_map = { - "learning_rate": "lr", - "momentum": "momentum", - "nesterov": "nesterov", - "weight_decay": "weight_decay", - } - _dict = {} - for key in self._param_dict: - if key in _param_map: - _dict.update({_param_map[key]: self._param_dict[key]}) - return _dict - - def __call__(self, **kwargs): - """Call `PyTorchSGD` object.""" - return torch.optim.SGD, self._mapping(**kwargs) diff --git a/neural_compressor/experimental/common/postprocess.py b/neural_compressor/experimental/common/postprocess.py deleted file mode 100644 index 19168e82e07..00000000000 --- a/neural_compressor/experimental/common/postprocess.py +++ /dev/null @@ -1,30 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Common Postprocess.""" -from deprecated import deprecated - - -@deprecated(version="2.0") -class Postprocess(object): - # class Transform(object): - """Just collect the infos to construct a Postprocess.""" - - def __init__(self, postprocess_cls, name="user_postprocess", **kwargs): - """Initialize `Postprocess` class.""" - self.postprocess_cls = postprocess_cls - self.name = name - self.kwargs = kwargs diff --git a/neural_compressor/experimental/common/torch_utils.py b/neural_compressor/experimental/common/torch_utils.py deleted file mode 100644 index 2dedf11d7a4..00000000000 --- a/neural_compressor/experimental/common/torch_utils.py +++ /dev/null @@ -1,68 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2022 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""This is an utility file for PyTorch distillation.""" -from deprecated import deprecated - -from neural_compressor.utils.utility import LazyImport - -torch = LazyImport("torch") - -STUDENT_FEATURES = {} -TEACHER_FEATURES = {} - - -# for adapting fx model -@deprecated(version="2.0") -@torch.fx.wrap -def record_output(output, name, output_process, student=False): - """Record layers output. - - It is a help function. - """ - recorded_output = output - if output_process != "": - if isinstance(output, dict) and output_process in output: - recorded_output = output[output_process] - elif isinstance(output, (tuple, list)) and str.isnumeric(output_process): - recorded_output = output[int(output_process)] - elif callable(output_process): - recorded_output = output_process(output) - else: - raise NotImplementedError( - "Current only support get the data with " - + "integer index in case the output is tuple or list and only " - + "need one item or with key in case the output is dict, " - + "or output_process is a function." - ) - if student: - STUDENT_FEATURES[name].append(recorded_output) - else: - TEACHER_FEATURES[name].append(recorded_output) - return output - - -@deprecated(version="2.0") -def get_activation(name, output_process="", student=False): - """Get a hook for getting activation.""" - - def hook(model, input, output): - if model.training or not student: - return record_output(output, name, output_process, student=student) - else: - return output - - return hook diff --git a/neural_compressor/experimental/component.py b/neural_compressor/experimental/component.py deleted file mode 100644 index 7d864eefb14..00000000000 --- a/neural_compressor/experimental/component.py +++ /dev/null @@ -1,547 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""This is a module for Component class. - -The Component class will be inherited by the class 'Quantization', 'Pruning' and 'Distillation'. -""" - -import importlib - -from deprecated import deprecated - -from ..adaptor import FRAMEWORKS -from ..model.model import get_model_fwk_name -from ..utils import logger -from ..utils.create_obj_from_config import create_dataloader, create_eval_func, create_train_func -from ..utils.utility import required_libs -from .common import Model - - -@deprecated(version="2.0") -class Component(object): - """This is base class of Neural Compressor Component. - - This class will be inherited by the class 'Quantization', 'Pruning' and 'Distillation'. - This design is mainly for one-shot optimization for pruning/distillation/quantization-aware training. - In this class will apply all hooks for 'Quantization', 'Pruning' and 'Distillation'. - """ - - def __init__(self, conf_fname_or_obj=None, combination=None): - """Construct all the necessary attributes for the Component object. - - Args: - conf_fname_or_obj: A YAML configuration file path or a Config object which definds the compressor behavior. - combination: What components to be combined in this object. - """ - self.conf = None - self.cfg = None - self.combination = combination - self.framework = None - self._model = None - self._train_func = None - self._train_dataloader = None - self._eval_func = None - self._eval_dataloader = None - self._train_distributed = False - self._evaluation_distributed = False - self.adaptor = None - self._metric = None - self.hooks = { - "on_train_begin": self.on_train_begin, - "on_train_end": self.on_train_end, - "on_epoch_begin": self.on_epoch_begin, - "on_epoch_end": self.on_epoch_end, - "on_step_begin": self.on_step_begin, - "on_step_end": self.on_step_end, - "on_after_compute_loss": self.on_after_compute_loss, - "on_before_optimizer_step": self.on_before_optimizer_step, - "on_after_optimizer_step": self.on_after_optimizer_step, - "on_before_eval": self.on_before_eval, - "on_after_eval": self.on_after_eval, - } - self.hooks_dict = { - "on_train_begin": [], - "on_train_end": [], - "on_epoch_begin": [], - "on_epoch_end": [], - "on_step_begin": [], - "on_step_end": [], - "on_after_compute_loss": [], - "on_before_optimizer_step": [], - "on_after_optimizer_step": [], - "on_before_eval": [], - "on_after_eval": [], - } - if conf_fname_or_obj is not None: # pragma: no cover - from ..conf.config import Conf - - if isinstance(conf_fname_or_obj, str): - self.conf = Conf(conf_fname_or_obj) - elif isinstance(conf_fname_or_obj, Conf): - self.conf = conf_fname_or_obj - else: - assert ( - False - ), "Please pass a YAML configuration file path and name or \ - Conf class to Component" - self._init_with_conf() - - def _init_with_conf(self): - """Initialize some attributers.""" - self.cfg = self.conf.usr_cfg - if self.cfg.model.framework != "NA": - self.framework = self.cfg.model.framework.lower() - from neural_compressor.experimental.common.model import set_backend - - set_backend(self.framework) - if self.framework in required_libs: - for lib in required_libs[self.framework]: - try: - importlib.import_module(lib) - except Exception as e: - logger.error("{}.".format(e)) - raise RuntimeError( - "{} is not correctly installed. " "Please check your environment".format(lib) - ) - - def prepare(self): - """Register Quantization Aware Training hooks.""" - if self.combination is not None and "Quantization" in self.combination: - if self.adaptor is None: - framework_specific_info = { - "device": self.cfg.device, - "approach": "post_training_static_quant", - "random_seed": self.cfg.tuning.random_seed, - "workspace_path": self.cfg.tuning.workspace.path, - "q_dataloader": None, - } - if self.cfg.quantization.approach is not None: - framework_specific_info["approach"] = self.cfg.quantization.approach - - if "tensorflow" in self.framework: - framework_specific_info.update({"inputs": self.cfg.model.inputs, "outputs": self.cfg.model.outputs}) - self.adaptor = FRAMEWORKS[self.framework](framework_specific_info) - self.adaptor.model = self.model - self.register_hook("on_train_begin", self.adaptor._pre_hook_for_qat) - self.register_hook("on_train_end", self.adaptor._post_hook_for_qat) - - def prepare_qat(self): - """Register Quantization Aware Training hooks.""" - if self.adaptor is None: - framework_specific_info = { - "device": self.cfg.device, - "approach": "quant_aware_training", - "random_seed": self.cfg.tuning.random_seed, - "workspace_path": self.cfg.tuning.workspace.path, - "q_dataloader": None, - "backend": self.cfg.model.get("backend", "default"), - "format": self.cfg.model.get("quant_format", "default"), - } - if self.cfg.quantization.approach is not None: - framework_specific_info["approach"] = self.cfg.quantization.approach - - if "tensorflow" in self.framework: - framework_specific_info.update({"inputs": self.cfg.model.inputs, "outputs": self.cfg.model.outputs}) - self.adaptor = FRAMEWORKS[self.framework](framework_specific_info) - self.adaptor.model = self.model - self.register_hook("on_train_begin", self.adaptor._pre_hook_for_qat) - self.register_hook("on_train_end", self.adaptor._post_hook_for_qat) - - def pre_process(self): - """Initialize some attributes, such as the adaptor, the dataloader and train/eval functions from yaml config. - - Component base class provides default function to initialize dataloaders and functions - from user config. And for derived classes(Pruning, Quantization, etc.), an override - function is required. - """ - if self.adaptor is None: - # create adaptor - framework_specific_info = { - "device": self.cfg.device, - "random_seed": self.cfg.tuning.random_seed, - "workspace_path": self.cfg.tuning.workspace.path, - "q_dataloader": None, - } - if self.cfg.quantization.approach is not None: - framework_specific_info["approach"] = self.cfg.quantization.approach - - if "tensorflow" in self.framework: - framework_specific_info.update({"inputs": self.cfg.model.inputs, "outputs": self.cfg.model.outputs}) - - self.adaptor = FRAMEWORKS[self.framework](framework_specific_info) - self.adaptor.model = self.model - - # create dataloaders - if self._train_dataloader is None and self._train_func is None: - train_dataloader_cfg = self.cfg.train.dataloader - assert train_dataloader_cfg is not None, ( - "No training dataloader setting in current component. Please check " - "dataloader field of train field in yaml file. Or manually pass " - "dataloader to component." - ) - - self._train_dataloader = create_dataloader(self.framework, train_dataloader_cfg) - if self._eval_dataloader is None and self._eval_func is None: - if self._eval_dataloader is None: - eval_dataloader_cfg = self.cfg.evaluation.accuracy.dataloader - assert eval_dataloader_cfg is not None, ( - "No evaluation dataloader setting in current component. Please check " - "dataloader field of evaluation field in yaml file. Or manually pass " - "dataloader to component." - ) - self._eval_dataloader = create_dataloader(self.framework, eval_dataloader_cfg) - - # create functions - if self._train_func is None: - self._train_func = create_train_func( - self.framework, self._train_dataloader, self.adaptor, self.cfg.train, hooks=self.hooks - ) - if self._eval_func is None: - metric = [self._metric] if self._metric else self.cfg.evaluation.accuracy.metric - self._eval_func = create_eval_func( - self.framework, - self._eval_dataloader, - self.adaptor, - metric, - self.cfg.evaluation.accuracy.postprocess, - fp32_baseline=False, - ) - - self.prepare() - # strategy will be considered in future - if getattr(self.train_dataloader, "distributed", False): - self.register_hook("on_train_begin", self.adaptor._pre_hook_for_hvd) - - def execute(self): - """Execute the processing of this compressor. - - Component base class provides compressing processing. And for derived classes(Pruning, Quantization, etc.), - an override function is required. - """ - # TODO: consider strategy sync during combination - if self._train_func is not None: - modified_model = self._train_func( - self._model if getattr(self._train_func, "builtin", None) else self._model.model - ) - # for the cases that model is changed not inplaced during training, for example, - # oneshot with torch_fx QAT interfaces. Needs to reset model afterwards. - if modified_model is not None: - self._model.model = modified_model - if self._eval_func is not None: - score = self._eval_func(self._model if getattr(self._eval_func, "builtin", None) else self._model.model) - logger.info("Evaluated model score is {}.".format(str(score))) - return self._model - - def post_process(self): - """Post process after execution. - - For derived classes(Pruning, Quantization, etc.), an override function is required. - """ - pass - - def on_train_begin(self, dataloader=None): - """Be called before the beginning of epochs.""" - for on_train_begin_hook in self.hooks_dict["on_train_begin"]: - on_train_begin_hook(dataloader) - - def on_train_end(self): - """Be called after the end of epochs.""" - for on_train_end_hook in self.hooks_dict["on_train_end"]: - on_train_end_hook() - - @deprecated(version="2.0", reason="please use `on_train_begin` instead") - def pre_epoch_begin(self, dataloader=None): - """Be called before the beginning of epochs.""" - for on_train_begin_hook in self.hooks_dict["on_train_begin"]: - on_train_begin_hook(dataloader) - - @deprecated(version="2.0", reason="please use `on_train_end` instead") - def post_epoch_end(self): - """Be called after the end of epochs.""" - for on_train_end_hook in self.hooks_dict["on_train_end"]: - on_train_end_hook() - - def on_epoch_begin(self, epoch): - """Be called on the beginning of epochs.""" - for on_epoch_begin_hook in self.hooks_dict["on_epoch_begin"]: - on_epoch_begin_hook(epoch) - - def on_step_begin(self, batch_id): - """Be called on the beginning of batches.""" - res_list = [] - for on_step_begin_hook in self.hooks_dict["on_step_begin"]: - res_list.append(on_step_begin_hook(batch_id)) - return res_list - - @deprecated(version="2.0", reason="please use `on_step_begin` instead") - def on_batch_begin(self, batch_id): - """Be called on the beginning of batches.""" - return self.on_step_begin(batch_id) - - def on_after_compute_loss(self, input, student_output, student_loss, teacher_output=None): - """Be called on the end of loss computation.""" - loss = student_loss - for on_after_compute_loss_hook in self.hooks_dict["on_after_compute_loss"]: - loss = on_after_compute_loss_hook(input, student_output, loss, teacher_output) - return loss - - def on_before_optimizer_step(self): - """Be called before optimizer step.""" - for on_before_optimizer_step_hook in self.hooks_dict["on_before_optimizer_step"]: - on_before_optimizer_step_hook() - - def on_after_optimizer_step(self): - """Be called after optimizer step.""" - for on_after_optimizer_step_hook in self.hooks_dict["on_after_optimizer_step"]: - on_after_optimizer_step_hook() - - def on_before_eval(self): - """Be called before evaluation.""" - for on_before_eval_hook in self.hooks_dict["on_before_eval"]: - on_before_eval_hook() - - def on_after_eval(self): - """Be called after evaluation.""" - for on_after_eval_hook in self.hooks_dict["on_after_eval"]: - on_after_eval_hook() - - @deprecated(version="2.0", reason="please use `on_before_optimizer_step` instead") - def on_post_grad(self): - """Be called before optimizer step.""" - return self.on_before_optimizer_step() - - def on_step_end(self): - """Be called on the end of batches.""" - res_list = [] - for on_step_end_hook in self.hooks_dict["on_step_end"]: - res_list.append(on_step_end_hook()) - return res_list - - @deprecated(version="2.0", reason="please use `on_step_end` instead") - def on_batch_end(self): - """Be called on the end of batches.""" - return self.on_step_end() - - def on_epoch_end(self): - """Be called on the end of epochs.""" - res_list = [] - - for on_epoch_end_hook in self.hooks_dict["on_epoch_end"]: - res_list.append(on_epoch_end_hook()) - - return res_list - - def register_hook(self, scope, hook, input_args=None, input_kwargs=None): - """Register hook for component. - - Input_args and input_kwargs are reserved for user registered hooks. - """ - if hook not in self.hooks_dict[scope]: - self.hooks_dict[scope].append(hook) - - def __call__(self): - """Execute this class. - - For derived classes(Pruning, Quantization, etc.), an override function is required. - """ - self.pre_process() - results = self.execute() - self.post_process() - return results - - def __repr__(self): - """Represent this class.""" - if self.combination: - return "Combination of " + ",".join(self.combination) - else: - return "Base Component" - - @property - def train_func(self): - """Not support get train_func.""" - assert False, "Should not try to get the value of `train_func` attribute." - return None - - @train_func.setter - def train_func(self, user_train_func): - """Training function. - - Args: - user_train_func: This function takes "model" as input parameter - and executes entire training process with self - contained training hyper-parameters. If training_func set, - an evaluation process must be triggered and user should - set eval_dataloader with metric configured or directly eval_func - to make evaluation of the model executed. training_func will return - a trained model. - """ - self._train_func = user_train_func - - @property - def eval_func(self): - """Not support get eval_func.""" - assert False, "Should not try to get the value of `eval_func` attribute." - return None - - @eval_func.setter - def eval_func(self, user_eval_func): - """Eval function for component. - - Args: - user_eval_func: This function takes "model" as input parameter - and executes entire evaluation process with self - contained metrics. If eval_func set, - an evaluation process must be triggered - to make evaluation of the model executed. - """ - self._eval_func = user_eval_func - - @property - def train_dataloader(self): - """Getter to train dataloader.""" - return self._train_dataloader - - @train_dataloader.setter - def train_dataloader(self, dataloader): - """Set Data loader for training for Component. - - It is iterable and the batched data should consists of a tuple like - (input, label) if the training dataset containing label, or yield (input, _) - for label-free train dataset, the input in the batched data will be used for - model inference, so it should satisfy the input format of specific model. - In train process, label of data loader will not be used and - neither the postprocess and metric. User only need to set - train_dataloader when train_dataloader can not be configured from yaml file. - - Args: - dataloader(generator): user are supported to set a user defined dataloader - which meet the requirements that can yield tuple of - (input, label)/(input, _) batched data. Another good - practice is to use neural_compressor.experimental.common.DataLoader - to initialize a neural_compressor dataloader object. Notice - neural_compressor.experimental.common.DataLoader is just a wrapper of the - information needed to build a dataloader, it can't yield - batched data and only in this setter method - a 'real' train_dataloader will be created, - the reason is we have to know the framework info - and only after the Component object created then - framework information can be known. - Future we will support creating iterable dataloader - from neural_compressor.experimental.common.DataLoader. - """ - from .common import _generate_common_dataloader - - self._train_dataloader = _generate_common_dataloader(dataloader, self.framework, self._train_distributed) - - @property - def eval_dataloader(self): - """Getter to eval dataloader.""" - return self._eval_dataloader - - @eval_dataloader.setter - def eval_dataloader(self, dataloader): - """Set Data loader for evaluation of component. - - It is iterable and the batched data should consists of yield (input, _). - the input in the batched data will be used for model inference, so it - should satisfy the input format of specific model. - User only need to set eval_dataloader when eval_dataloader can not be - configured from yaml file. - - Args: - dataloader(generator): user are supported to set a user defined dataloader - which meet the requirements that can yield tuple of - (input, label)/(input, _) batched data. Another good - practice is to use neural_compressor.experimental.common.DataLoader - to initialize a neural_compressor dataloader object. Notice - neural_compressor.experimental.common.DataLoader is just a wrapper of the - information needed to build a dataloader, it can't yield - batched data and only in this setter method - a 'real' train_dataloader will be created, - the reason is we have to know the framework info - and only after the Component object created then - framework information can be known. - Future we will support creating iterable dataloader - from neural_compressor.experimental.common.DataLoader. - """ - from .common import _generate_common_dataloader - - self._eval_dataloader = _generate_common_dataloader(dataloader, self.framework, self._evaluation_distributed) - - @property - def model(self): - """Getter of model in neural_compressor.model.""" - return self._model - - @model.setter - def model(self, user_model): - """Set the user model and dispatch to framework specific internal model object. - - Args: - user_model: user are supported to set model from original framework model format - (eg, tensorflow frozen_pb or path to a saved model), - but not recommended. Best practice is to set from a initialized - neural_compressor.experimental.common.Model. - If tensorflow model is used, model's inputs/outputs will be - auto inferenced, but sometimes auto inferenced - inputs/outputs will not meet your requests, - set them manually in config yaml file. - Another corner case is slim model of tensorflow, - be careful of the name of model configured in yaml file, - make sure the name is in supported slim model list. - """ - from ..model import BaseModel - - if self.cfg.model.framework == "NA": - assert not isinstance( - user_model, BaseModel - ), "Please pass an original framework model but not neural compressor model!" - self.framework = get_model_fwk_name(user_model) - if self.framework == "tensorflow": - from ..model.tensorflow_model import get_model_type - - if get_model_type(user_model) == "keras" and self.cfg.model.backend == "itex": - self.framework = "keras" - if self.framework == "pytorch": - if self.cfg.model.backend == "default": - self.framework = "pytorch_fx" - elif self.cfg.model.backend == "ipex": - self.framework = "pytorch_ipex" - self.cfg.model.framework = self.framework - - if not isinstance(user_model, BaseModel): - logger.warning("Force convert framework model to neural_compressor model.") - if "tensorflow" in self.framework or self.framework == "keras": - self._model = Model(user_model, framework=self.framework, device=self.cfg.device) - else: - self._model = Model(user_model, framework=self.framework) - else: - # It is config of neural_compressor version < 2.0, no need in 2.0 - if self.cfg.model.framework == "pytorch_ipex": - from neural_compressor.model.torch_model import IPEXModel - - if not isinstance(user_model, IPEXModel): - self._model = Model(user_model.model, framework=self.cfg.model.framework) - return - - self._model = user_model - - if "tensorflow" in self.framework: - self._model.name = self.cfg.model.name - self._model.output_tensor_names = self.cfg.model.outputs - self._model.input_tensor_names = self.cfg.model.inputs - self._model.workspace_path = self.cfg.tuning.workspace.path diff --git a/neural_compressor/experimental/compression/__init__.py b/neural_compressor/experimental/compression/__init__.py deleted file mode 100644 index 5333f7cae00..00000000000 --- a/neural_compressor/experimental/compression/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -"""Experimental pruning API.""" - -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2023 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/neural_compressor/experimental/contrib/__init__.py b/neural_compressor/experimental/contrib/__init__.py deleted file mode 100644 index a3ff38fab97..00000000000 --- a/neural_compressor/experimental/contrib/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Built-in strategy for multiple framework backends.""" -from .strategy import * diff --git a/neural_compressor/experimental/contrib/strategy/__init__.py b/neural_compressor/experimental/contrib/strategy/__init__.py deleted file mode 100644 index abfb7c7bb67..00000000000 --- a/neural_compressor/experimental/contrib/strategy/__init__.py +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Built-in strategy for multiple framework backends.""" -from os.path import dirname, basename, isfile, join -import glob - -modules = glob.glob(join(dirname(__file__), "*.py")) -for f in modules: - if isfile(f) and not f.startswith("__") and not f.endswith("__init__.py"): - __import__(basename(f)[:-3], globals(), locals(), level=1) diff --git a/neural_compressor/experimental/contrib/strategy/sigopt.py b/neural_compressor/experimental/contrib/strategy/sigopt.py deleted file mode 100644 index 920d2e36145..00000000000 --- a/neural_compressor/experimental/contrib/strategy/sigopt.py +++ /dev/null @@ -1,278 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""The SigOpt Tuning Strategy provides support for the quantization process.""" -import copy -from collections import OrderedDict - -from deprecated import deprecated - -from neural_compressor.experimental.strategy.strategy import TuneStrategy, strategy_registry -from neural_compressor.experimental.strategy.utils.tuning_sampler import OpWiseTuningSampler -from neural_compressor.experimental.strategy.utils.tuning_structs import OpTuningConfig -from neural_compressor.utils import logger -from neural_compressor.utils.utility import LazyImport - -sigopt = LazyImport("sigopt") - - -@deprecated(version="2.0") -@strategy_registry -class SigOptTuneStrategy(TuneStrategy): - """The tuning strategy using SigOpt HPO search in tuning space. - - Args: - model (object): The FP32 model specified for low precision tuning. - conf (Conf): The Conf class instance initialized from user yaml - config file. - q_dataloader (generator): Data loader for calibration, mandatory for - post-training quantization. - It is iterable and should yield a tuple (input, - label) for calibration dataset containing label, - or yield (input, _) for label-free calibration - dataset. The input could be a object, list, tuple or - dict, depending on user implementation, as well as - it can be taken as model input. - q_func (function, optional): Reserved for future use. - eval_dataloader (generator, optional): Data loader for evaluation. It is iterable - and should yield a tuple of (input, label). - The input could be a object, list, tuple or dict, - depending on user implementation, as well as it can - be taken as model input. The label should be able - to take as input of supported metrics. If this - parameter is not None, user needs to specify - pre-defined evaluation metrics through configuration - file and should set "eval_func" parameter as None. - Tuner will combine model, eval_dataloader and - pre-defined metrics to run evaluation process. - eval_func (function, optional): The evaluation function provided by user. - This function takes model as parameter, and - evaluation dataset and metrics should be - encapsulated in this function implementation and - outputs a higher-is-better accuracy scalar value. - - The pseudo code should be something like: - - def eval_func(model): - input, label = dataloader() - output = model(input) - accuracy = metric(output, label) - return accuracy - dicts (dict, optional): The dict containing resume information. - Defaults to None. - """ - - def __init__( - self, model, conf, q_dataloader, q_func=None, eval_dataloader=None, eval_func=None, dicts=None, q_hooks=None - ): - """Initialize the SigOpt tuning strategy if the user specified to use it.""" - super().__init__(model, conf, q_dataloader, q_func, eval_dataloader, eval_func, dicts, q_hooks) - strategy_name = conf.usr_cfg.tuning.strategy.name - if strategy_name.lower() == "sigopt": - try: - import sigopt - except ImportError: - try: - import subprocess - import sys - - subprocess.check_call([sys.executable, "-m", "pip", "install", "sigopt"]) - import sigopt # pylint: disable=import-error - except: - assert False, "Unable to import sigopt from the local environment." - else: - pass - # SigOpt init - client_token = conf.usr_cfg.tuning.strategy.sigopt_api_token - self.project_id = conf.usr_cfg.tuning.strategy.sigopt_project_id - self.experiment_name = conf.usr_cfg.tuning.strategy.sigopt_experiment_name - try: - assert client_token is not None - except AssertionError: - logger.error( - "`sigopt_api_token` field in yaml file is required. " - "Please refer to details in /docs/sigopt_strategy.md." - ) - exit(0) - try: - assert self.project_id is not None - logger.warning( - "Project id is {}, " "Please check whether it is created in the sigopt account.".format(self.project_id) - ) - except AssertionError: - logger.error( - "`sigopt_project_id` field in yaml file is required. " - "Please refer to details in /docs/sigopt_strategy.md." - ) - exit(0) - if self.experiment_name == "nc-tune": - logger.info( - "Default experiment name `nc-tune` is used, " - "Please refer to details in /docs/sigopt_strategy.md " - "if user wants to modify it." - ) - else: - logger.info("Experiment name is {}.".format(self.experiment_name)) - - self.conn = sigopt.Connection(client_token) - self.experiment = None - - def params_to_tune_configs(self, params): - """Get the parameters of the tuning strategy.""" - op_tuning_cfg = {} - calib_sampling_size_lst = self.tuning_space.root_item.get_option_by_name("calib_sampling_size").options - for op_name_type, configs in self.op_configs.items(): - if len(configs) == 1: - op_tuning_cfg[op_name_type] = configs[0] - else: - op_tuning_cfg[op_name_type] = configs[min(len(configs) - 1, int(params[op_name_type[0]]))] - calib_sampling_size = calib_sampling_size_lst[min(len(configs) - 1, int(params["calib_sampling_size"]))] - op_tuning_cfg["calib_sampling_size"] = calib_sampling_size - return op_tuning_cfg - - def next_tune_cfg(self): - """Yielding the tuning config to traverse by concreting strategies according to last tuning result.""" - while self.experiment.progress.observation_count < self.experiment.observation_budget: - suggestion = self.conn.experiments(self.experiment.id).suggestions().create() - yield self.params_to_tune_configs(suggestion.assignments) - values = [ - dict(name="accuracy", value=self.last_tune_result[0]), - dict(name="latency", value=self.last_tune_result[1]), - ] - obs = ( - self.conn.experiments(self.experiment.id).observations().create(suggestion=suggestion.id, values=values) - ) - logger.debug("`suggestion_id` is {}, `observation_id` is {}.".format(suggestion.id, obs.id)) - self.experiment = self.conn.experiments(self.experiment.id).fetch() - - def get_acc_target(self, base_acc): - """Get the tuning target of the accuracy ceiterion.""" - if self.cfg.tuning.accuracy_criterion.relative: - return base_acc * (1.0 - self.cfg.tuning.accuracy_criterion.relative) - else: - return base_acc - self.cfg.tuning.accuracy_criterion.absolute - - def traverse(self): - """The main traverse logic, which could be override by some concrete strategy which needs more hooks. - - This is SigOpt version of traverse -- with additional constraints setting to HPO. - """ - self._eval_baseline() - - baseline_msg = ( - "[Accuracy: {:.4f}".format(self.baseline[0]) - + "".join( - [ - ", {}: {:.4f}".format(x, y) - for x, y in zip(self.objectives.representation, self.baseline[1]) - if x != "Accuracy" - ] - ) - + "]" - if self.baseline - else "n/a" - ) - logger.info("FP32 baseline is: {}".format(baseline_msg)) - self.experiment = self.create_exp(acc_target=self.get_acc_target(self.baseline[0])) - trials_count = 0 - for tune_cfg in self.next_tune_cfg(): - # add tune_cfg here as quantize use tune_cfg - tune_cfg["advance"] = self.cfg.quantization.advance - trials_count += 1 - tuning_history = self._find_tuning_history(tune_cfg) - if tuning_history and trials_count < self.cfg.tuning.exit_policy.max_trials: - self.last_tune_result = tuning_history["last_tune_result"] - self.best_tune_result = tuning_history["best_tune_result"] - logger.warn("Find evaluated tuning config, skip.") - continue - - logger.debug("Dump current tuning configuration:") - logger.debug(tune_cfg) - self.last_qmodel = self.adaptor.quantize(tune_cfg, self.model, self.calib_dataloader, self.q_func) - assert self.last_qmodel - # Return the last quantized model as a result. if performance only. - if self.cfg.tuning.exit_policy.performance_only: - self.best_qmodel = self.last_qmodel - self._add_tuning_history(copy.deepcopy(tune_cfg), (-1, [0]), q_config=self.last_qmodel.q_config) - return - self.last_tune_cfg = copy.deepcopy(tune_cfg) - self.last_tune_result = self._evaluate(self.last_qmodel) - - need_stop = self.stop(self.cfg.tuning.exit_policy.timeout, trials_count) - - # record the tuning history - saved_tune_cfg = copy.deepcopy(tune_cfg) - saved_last_tune_result = copy.deepcopy(self.last_tune_result) - self._add_tuning_history(saved_tune_cfg, saved_last_tune_result) - - if need_stop: - break - - def create_exp(self, acc_target): - """Set the config for the experiment.""" - params = [] - from copy import deepcopy - - tuning_space = self.tuning_space - initial_op_tuning_cfg = {} - for item in tuning_space.root_item.options: - if item.item_type == "op": - op_name, op_type = item.name - initial_op_tuning_cfg[item.name] = OpTuningConfig(op_name, op_type, "fp32", tuning_space) - calib_sampling_size_lst = tuning_space.root_item.get_option_by_name("calib_sampling_size").options - # step1. collect the ops that support static and dynamic - quant_mode_wise_items = OrderedDict() - query_order = ["static", "dynamic", "bf16", "fp16", "fp32"] - pre_items = set() - for quant_mode in query_order: - items = tuning_space.query_items_by_quant_mode(quant_mode) - filtered_items = [item for item in items if item not in pre_items] - pre_items = pre_items.union(set(items)) - quant_mode_wise_items[quant_mode] = filtered_items - - def initial_op_quant_mode(items_lst, target_quant_mode, op_item_dtype_dict): - """Initialize the op tuning mode.""" - for item in items_lst: - op_item_dtype_dict[item.name] = target_quant_mode - - op_item_dtype_dict = OrderedDict() - for quant_mode, quant_mode_items in quant_mode_wise_items.items(): - initial_op_quant_mode(quant_mode_items, quant_mode, op_item_dtype_dict) - - op_wise_pool = OpWiseTuningSampler(tuning_space, [], [], op_item_dtype_dict, initial_op_tuning_cfg) - self.op_configs = op_wise_pool.get_opwise_candidate() - for op, configs in self.op_configs.items(): - if len(configs) > 1: - params.append(dict(name=op[0], type="int", bounds=dict(min=0, max=len(configs) - 1))) - params.append( - dict(name="calib_sampling_size", type="int", bounds=dict(min=0, max=len(calib_sampling_size_lst) - 1)) - ) - experiment = self.conn.experiments().create( - name=self.experiment_name, - parameters=params, - metrics=[ - dict(name="accuracy", objective="maximize", strategy="constraint", threshold=acc_target), - dict(name="latency", objective="minimize", strategy="optimize"), - ], - parallel_bandwidth=1, - # Define an Observation Budget for your experiment - observation_budget=100, - project=self.project_id, - ) - - logger.debug("Create experiment at https://app.sigopt.com/experiment/{}".format(experiment.id)) - - return experiment diff --git a/neural_compressor/experimental/contrib/strategy/tpe.py b/neural_compressor/experimental/contrib/strategy/tpe.py deleted file mode 100644 index 9a331b6e405..00000000000 --- a/neural_compressor/experimental/contrib/strategy/tpe.py +++ /dev/null @@ -1,531 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Fefine the tuning strategy that uses tpe search in tuning space.""" -import copy -import os -from collections import OrderedDict -from functools import partial -from pathlib import Path - -import numpy as np -from deprecated import deprecated - -from neural_compressor.experimental.strategy.strategy import TuneStrategy, strategy_registry -from neural_compressor.experimental.strategy.utils.tuning_sampler import OpWiseTuningSampler -from neural_compressor.experimental.strategy.utils.tuning_structs import OpTuningConfig -from neural_compressor.utils import logger -from neural_compressor.utils.utility import LazyImport - -hyperopt = LazyImport("hyperopt") - -try: - import pandas as pd -except ImportError: - pd = None - logger.info("Pandas package is required for best result and CSV files generation.") - - -@deprecated(version="2.0") -@strategy_registry -class TpeTuneStrategy(TuneStrategy): - """The tuning strategy using tpe search in tuning space. - - Args: - model (object): The FP32 model specified for low precision tuning. - conf (Conf): The Conf class instance initialized from user yaml - config file. - q_dataloader (generator): Data loader for calibration, mandatory for - post-training quantization. - It is iterable and should yield a tuple (input, - label) for calibration dataset containing label, - or yield (input, _) for label-free calibration - dataset. The input could be a object, list, tuple or - dict, depending on user implementation, as well as - it can be taken as model input. - q_func (function, optional): Reserved for future use. - eval_dataloader (generator, optional): Data loader for evaluation. It is iterable - and should yield a tuple of (input, label). - The input could be a object, list, tuple or dict, - depending on user implementation, as well as it can - be taken as model input. The label should be able - to take as input of supported metrics. If this - parameter is not None, user needs to specify - pre-defined evaluation metrics through configuration - file and should set "eval_func" parameter as None. - Tuner will combine model, eval_dataloader and - pre-defined metrics to run evaluation process. - eval_func (function, optional): The evaluation function provided by user. - This function takes model as parameter, and - evaluation dataset and metrics should be - encapsulated in this function implementation and - outputs a higher-is-better accuracy scalar value. - - The pseudo code should be something like: - - def eval_func(model): - input, label = dataloader() - output = model(input) - accuracy = metric(output, label) - return accuracy - dicts (dict, optional): The dict containing resume information. - Defaults to None. - """ - - def __init__( - self, model, conf, q_dataloader, q_func=None, eval_dataloader=None, eval_func=None, dicts=None, q_hooks=None - ): - """Initialize the tpe tuning strategy if the user specified to use it.""" - assert ( - conf.usr_cfg.quantization.approach == "post_training_static_quant" - ), "TPE strategy is only for post training static quantization!" - """Initialize the tpe tuning strategy if the user specified to use it.""" - strategy_name = conf.usr_cfg.tuning.strategy.name - if strategy_name.lower() == "tpe": - try: - import hyperopt - except ImportError: - try: - import subprocess - import sys - - subprocess.check_call([sys.executable, "-m", "pip", "install", "hyperopt"]) - import hyperopt # pylint: disable=import-error - except: - assert False, "Unable to import hyperopt from the local environment." - else: - pass - self.hpopt_search_space = None - self.warm_start = False - self.cfg_evaluated = False - self.hpopt_trials = hyperopt.Trials() - self.max_trials = conf.usr_cfg.tuning.exit_policy.get("max_trials", 200) - self.loss_function_config = { - "acc_th": ( - conf.usr_cfg.tuning.accuracy_criterion.relative - if conf.usr_cfg.tuning.accuracy_criterion and conf.usr_cfg.tuning.accuracy_criterion.relative - else 0.01 - ), - "acc_weight": conf.usr_cfg.tuning.strategy.get("accuracy_weight", 1.0), - "lat_weight": conf.usr_cfg.tuning.strategy.get("latency_weight", 1.0), - } - self.tpe_params = {"n_initial_point": 10, "gamma": 0.3, "n_EI_candidates": 100, "prior_weight": 1.0} - self.best_result = {"best_loss": float("inf"), "best_acc_loss": float("inf"), "best_lat_diff": 0.0} - self._algo = None - - super().__init__(model, conf, q_dataloader, q_func, eval_dataloader, eval_func, dicts, q_hooks) - - def __getstate__(self): - """Magic method for pickle saving. - - Returns: - dict: Saved dict for resuming - """ - for history in self.tuning_history: - if self._same_yaml(history["cfg"], self.cfg): - history["warm_start"] = True - history["hpopt_trials"] = self.hpopt_trials - history["loss_function_config"] = self.loss_function_config - history["tpe_params"] = self.tpe_params - history["hpopt_search_space"] = self.hpopt_search_space - history["_algo"] = self._algo - save_dict = super().__getstate__() - return save_dict - - def _configure_hpopt_search_space_and_params(self, search_space): - """Set the configuration of hpopt searching strategy.""" - self.hpopt_search_space = {} - for param, configs in search_space.items(): - self.hpopt_search_space[(param)] = hyperopt.hp.choice((param[0]), configs) - # Find minimum number of choices for params with more than one choice - multichoice_params = [len(configs) for param, configs in search_space.items() if len(configs) > 1] - if not multichoice_params: - return False - min_param_size = min(multichoice_params) if len(multichoice_params) > 0 else 1 - self.tpe_params["n_EI_candidates"] = min_param_size - self.tpe_params["prior_weight"] = 1 / min_param_size - self._algo = partial( - hyperopt.tpe.suggest, - n_startup_jobs=self.tpe_params["n_initial_point"], - gamma=self.tpe_params["gamma"], - n_EI_candidates=self.tpe_params["n_EI_candidates"], - prior_weight=self.tpe_params["prior_weight"], - ) - return True - - def traverse(self): - """Tpe traverse logic.""" - logger.info("Start to run tpe strategy.") - # prepare log file - trials_file = os.path.join(os.path.dirname(self.history_path), "tpe_trials.csv") - best_result_file = os.path.join(os.path.dirname(self.history_path), "tpe_best_result.csv") - logger.debug("trials_file: {} ".format(trials_file) + "best_result_file: {}".format(best_result_file)) - if Path(trials_file).exists(): - os.remove(trials_file) - status = True - tuning_history = self._find_self_tuning_history() - - from copy import deepcopy - - tuning_space = self.tuning_space - initial_op_tuning_cfg = {} - for item in tuning_space.root_item.options: - if item.item_type == "op": - op_name, op_type = item.name - initial_op_tuning_cfg[item.name] = OpTuningConfig(op_name, op_type, "fp32", tuning_space) - calib_sampling_size_lst = tuning_space.root_item.get_option_by_name("calib_sampling_size").options - # step1. collect the ops that support static and dynamic - quant_mode_wise_items = OrderedDict() - query_order = ["static", "dynamic", "bf16", "fp16", "fp32"] - pre_items = set() - for quant_mode in query_order: - items = tuning_space.query_items_by_quant_mode(quant_mode) - filtered_items = [item for item in items if item not in pre_items] - pre_items = pre_items.union(set(items)) - quant_mode_wise_items[quant_mode] = filtered_items - - def initial_op_quant_mode(items_lst, target_quant_mode, op_item_dtype_dict): - for item in items_lst: - op_item_dtype_dict[item.name] = target_quant_mode - - op_item_dtype_dict = OrderedDict() - for quant_mode, quant_mode_items in quant_mode_wise_items.items(): - initial_op_quant_mode(quant_mode_items, quant_mode, op_item_dtype_dict) - op_wise_pool = OpWiseTuningSampler(tuning_space, [], [], op_item_dtype_dict, initial_op_tuning_cfg) - self.op_configs = op_wise_pool.get_opwise_candidate() - self.opwise_tune_cfgs = {} - for key, val in self.op_configs.items(): - self.opwise_tune_cfgs[key[0]] = val - self.opwise_tune_cfgs["calib_sampling_size"] = self.tuning_space.root_item.get_option_by_name( - "calib_sampling_size" - ).options - - if tuning_history and not self.warm_start: - # prepare loss function scaling (best result from basic can be used) - best_lat, worse_acc_loss = 0, 0 - for history in tuning_history["history"]: - acc_loss, lat_diff = self._calculate_acc_lat_diff(history["tune_result"][0], history["tune_result"][1]) - if lat_diff > best_lat: - best_lat = lat_diff - if acc_loss > worse_acc_loss: - worse_acc_loss = acc_loss - self._calculate_loss_function_scaling_components(worse_acc_loss, best_lat, self.loss_function_config) - first_run_cfg = self.add_loss_to_tuned_history_and_find_best(tuning_history["history"]) - # Prepare hpopt config with best cfg from history - self._configure_hpopt_search_space_and_params(first_run_cfg) - # Run first iteration with best result from history - trials_count = len(self.hpopt_trials.trials) + 1 - hyperopt.fmin( - partial(self.object_evaluation, model=self.model), - space=self.hpopt_search_space, - algo=self._algo, - max_evals=trials_count, - trials=self.hpopt_trials, - show_progressbar=False, - ) - if pd is not None: - self._save_trials(trials_file) - self._update_best_result(best_result_file) - # Prepare full hpopt search space - new_tune_cfgs = self._prepare_final_searchspace(first_run_cfg, self.opwise_tune_cfgs) - status = self._configure_hpopt_search_space_and_params(new_tune_cfgs) - elif not self.warm_start: - self._calculate_loss_function_scaling_components(0.01, 2, self.loss_function_config) - status = self._configure_hpopt_search_space_and_params(self.opwise_tune_cfgs) - - if status: - trials_count = len(self.hpopt_trials.trials) + 1 - # get fp32 model baseline - if self.baseline is None: - logger.info("Get FP32 model baseline.") - self.baseline = self._evaluate(self.model) - self._add_tuning_history() - - baseline_msg = ( - "[Accuracy: {:.4f}".format(self.baseline[0]) - + "".join( - [ - ", {}: {:.4f}".format(x, y) - for x, y in zip(self.objectives.representation, self.baseline[1]) - if x != "Accuracy" - ] - ) - + "]" - if self.baseline - else "n/a" - ) - logger.info("FP32 baseline is: {}".format(baseline_msg)) - - if not self.objectives.relative: - self.loss_function_config["acc_th"] = (self.baseline[0] - self.objectives.acc_goal) / self.baseline[0] - # start trials - exit = False - while not exit: - self.cfg_evaluated = False - logger.debug("Trial iteration start: {} / {}.".format(trials_count, self.max_trials)) - hyperopt.fmin( - partial(self.object_evaluation, model=self.model), - space=self.hpopt_search_space, - algo=self._algo, - max_evals=trials_count, - trials=self.hpopt_trials, - show_progressbar=False, - ) - trials_count += 1 - if pd is not None: - self._save_trials(trials_file) - self._update_best_result(best_result_file) - self._save() - if self.stop(self.cfg.tuning.exit_policy.timeout, trials_count): - exit = True - else: - logger.warn("Can't create search space for input model.") - - def _prepare_final_searchspace(self, first, second): - """Set the final search space.""" - for key, cfgs in second.items(): - new_cfg = [] - for cfg in cfgs: - if cfg != first[key][0]: - new_cfg.append(cfg) - first[key] = first[key] + new_cfg - return first - - def add_loss_to_tuned_history_and_find_best(self, tuning_history_list): - """Find the best tuned history.""" - logger.debug("Number of resumed configs is {}.".format(len(tuning_history_list))) - best_loss = None - first_run_cfg = None - for history in tuning_history_list: - result = self._compute_metrics( - history["tune_cfg"]["op"], history["tune_result"][0], history["tune_result"][1] - ) - if best_loss is None or result["loss"] < best_loss: - best_loss = result["loss"] - first_run_cfg = history["tune_cfg"]["op"].copy() - result["source"] = "finetune" - history["result"] = result - logger.debug( - "Resumed iteration loss is {}, acc_loss is {}, lat_diff is {}, " - "quantization_ratio is {}.".format( - result["loss"], result["acc_loss"], result["lat_diff"], result["quantization_ratio"] - ) - ) - for op, cfg in first_run_cfg.items(): - first_run_cfg[op] = [ - cfg, - ] - return first_run_cfg - - def object_evaluation(self, tune_cfg, model): - """Check if config was already evaluated.""" - for k, v in self.op_configs.items(): - tune_cfg.update({k: tune_cfg.pop(k[0])}) - op_cfgs = self._tune_cfg_converter(tune_cfg) - self.last_qmodel = self.adaptor.quantize(op_cfgs, self.model, self.calib_dataloader) - self.last_tune_cfg = copy.deepcopy(tune_cfg) - self.last_tune_result = self._evaluate(self.last_qmodel) - logger.info("The last tune result is {}.".format((self.last_tune_result[0], self.last_tune_result[1][0]))) - saved_tune_cfg = copy.deepcopy(op_cfgs) - saved_last_tune_result = copy.deepcopy(self.last_tune_result) - # prepare result - result = self._compute_metrics(op_cfgs["op"], self.last_tune_result[0], self.last_tune_result[1][0]) - result["source"] = "tpe" - self._add_tuning_history(saved_tune_cfg, saved_last_tune_result, result=result) - logger.info( - "Current iteration loss is {}, acc_loss is {}, lat_diff is {}, " - "quantization_ratio is {}.".format( - result["loss"], result["acc_loss"], result["lat_diff"], result["quantization_ratio"] - ) - ) - return result - - def _compute_metrics(self, tune_cfg, acc, lat): - quantization_ratio = 1 - len( - [param for param in tune_cfg.values() if param["activation"]["dtype"] == "fp32"] - ) / len(tune_cfg) - acc_diff, lat_diff = self._calculate_acc_lat_diff(acc, lat) - return { - "loss": self.calculate_loss(acc_diff, lat_diff, self.loss_function_config), - "acc": acc, - "lat": lat, - "acc_loss": acc_diff, - "lat_diff": lat_diff, - "quantization_ratio": quantization_ratio, - "status": hyperopt.STATUS_OK, - } - - def _calculate_acc_lat_diff(self, acc, lat): - int8_acc = acc - int8_lat = lat - fp32_acc = self.baseline[0] - fp32_lat = self.baseline[1][0] - acc_diff = (fp32_acc - int8_acc) / fp32_acc - lat_diff = fp32_lat / int8_lat - return acc_diff, lat_diff - - def calculate_loss(self, acc_diff, lat_diff, config): - """Calculate the accuracy loss.""" - gamma_penalty = 40 # penalty term - acc_loss_component = self._calculate_acc_loss_component(acc_diff) - lat_loss_component = self._calculate_lat_diff_component(lat_diff) - acc_weight = config["acc_weight"] if acc_diff > config["acc_th"] else 0.0 - if acc_weight == 0 and config["lat_weight"] == 0: - acc_weight = 1.0 - loss = acc_weight * (config["acc_scale"] * (acc_loss_component - config["acc_min"])) + config["lat_weight"] * ( - config["lat_scale"] * (lat_loss_component - config["lat_min"]) - ) - if acc_diff > config["acc_th"]: - loss += 2 * gamma_penalty - return loss - - def _calculate_acc_loss_component(self, acc_loss): - return np.exp(acc_loss) - - def _calculate_lat_diff_component(self, lat_diff): - return np.log(np.power((1 / (1000 * lat_diff)), 8)) - - def _calculate_loss_function_scaling_components(self, acc_loss, lat_diff, config): - acc_min = self._calculate_acc_loss_component(0) - acc_max = self._calculate_acc_loss_component(acc_loss) - if acc_max == acc_min: - acc_max = self._calculate_acc_loss_component(config["acc_th"]) - config["acc_min"] = acc_min - config["acc_scale"] = 10 / np.abs(acc_max - acc_min) - - lat_min = self._calculate_lat_diff_component(lat_diff) - lat_max = self._calculate_lat_diff_component(1) - if lat_min == lat_max: - lat_min = self._calculate_lat_diff_component(2) - config["lat_min"] = lat_min - config["lat_scale"] = 10 / np.abs(lat_max - lat_min) - - def _save_trials(self, trials_log): - """Save the trial result to the log file.""" - tpe_trials_results = pd.DataFrame(self.hpopt_trials.results) - csv_file = trials_log - tpe_trials_results.to_csv(csv_file) - - def _update_best_result(self, best_result_file): - if not self.hpopt_trials: - raise Exception("No trials loaded to get best result") - trials_results = pd.DataFrame(self.hpopt_trials.results) - - if not trials_results[trials_results.acc_loss <= self.loss_function_config["acc_th"]].empty: - # If accuracy threshold reached, choose best latency - best_result = ( - trials_results[trials_results.acc_loss <= self.loss_function_config["acc_th"]] - .reset_index(drop=True) - .sort_values(by=["lat_diff", "acc_loss"], ascending=[False, True]) - .reset_index(drop=True) - .loc[0] - ) - else: - # If accuracy threshold is not reached, choose based on loss function - best_result = trials_results.sort_values("loss", ascending=True).reset_index(drop=True).loc[0] - - update_best_result = False - if not self.best_result["best_loss"]: - update_best_result = True - elif self.best_result["best_acc_loss"] <= self.loss_function_config["acc_th"]: - if ( - best_result["acc_loss"] <= self.loss_function_config["acc_th"] - and best_result["lat_diff"] > self.best_result["best_lat_diff"] - ): - update_best_result = True - else: - if ( - best_result["acc_loss"] <= self.loss_function_config["acc_th"] - or best_result["loss"] < self.best_result["best_loss"] - ): - update_best_result = True - - if update_best_result: - best_result.to_csv(best_result_file, header=False) - self.best_result["best_loss"] = best_result["loss"] - self.best_result["best_acc_loss"] = best_result["acc_loss"] - self.best_result["best_lat_diff"] = best_result["lat_diff"] - self.best_result["quantization_ratio"] = best_result["quantization_ratio"] - - logger.info( - "Trial iteration end is {} / {}, best loss is {}, acc_loss is {}, " - "lat_diff is {}, quantization_ratio is {}.".format( - len(self.hpopt_trials.trials), - self.max_trials, - self.best_result["best_loss"], - self.best_result["best_acc_loss"], - self.best_result["best_lat_diff"], - self.best_result["quantization_ratio"], - ) - ) - - def stop(self, timeout, trials_count): - """Check if need to stop traversing the tuning space, either accuracy goal is met or timeout is reach. - - Returns: - bool: True if need stop, otherwise False. - """ - need_stop = False - if not self.cfg_evaluated: - if self.objectives.compare(self.best_tune_result, self.baseline): - del self.best_tune_result - del self.best_qmodel - self.best_tune_result = self.last_tune_result - self.best_qmodel = self.last_qmodel - self.adaptor.save(self.best_qmodel, os.path.dirname(self.deploy_path)) - else: - del self.last_qmodel - - last_tune_msg = ( - "[Accuracy ({}|fp32): {:.4f}|{:.4f}".format( - self.cfg.quantization.dtype, self.last_tune_result[0], self.baseline[0] - ) - + "".join( - [ - ", {} ({}|fp32): {:.4f}|{:.4f}".format(x, self.cfg.quantization.dtype, y, z) - for x, y, z in zip(self.objectives.representation, self.last_tune_result[1], self.baseline[1]) - if x != "Accuracy" - ] - ) - + "]" - if self.last_tune_result - else "n/a" - ) - - best_tune_msg = ( - "[Accuracy: {:.4f}".format(self.best_tune_result[0]) - + "".join( - [ - ", {}: {:.4f}".format(x, y) - for x, y in zip(self.objectives.representation, self.best_tune_result[1]) - if x != "Accuracy" - ] - ) - + "]" - if self.best_tune_result - else "n/a" - ) - - logger.info("Tune {} result is: {}, Best tune result is: {}".format(trials_count, last_tune_msg, best_tune_msg)) - - if timeout == 0 and self.best_tune_result: - need_stop = True - elif trials_count >= self.cfg.tuning.exit_policy.max_trials: - need_stop = True - else: - need_stop = False - - return need_stop diff --git a/neural_compressor/experimental/data/__init__.py b/neural_compressor/experimental/data/__init__.py deleted file mode 100644 index d81d6f4300b..00000000000 --- a/neural_compressor/experimental/data/__init__.py +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ============================================================================== -"""Built-in dataloaders, datasets, transforms, filters for multiple framework backends.""" - - -from .datasets import Datasets, Dataset, IterableDataset, dataset_registry -from .transforms import TRANSFORMS, BaseTransform, transform_registry -from .dataloaders import DATALOADERS -from .filters import FILTERS, Filter, filter_registry - -__all__ = [ - "DATALOADERS", - "Datasets", - "Dataset", - "IterableDataset", - "dataset_registry", - "TRANSFORMS", - "BaseTransform", - "transform_registry", - "FILTERS", - "Filter", - "filter_registry", -] diff --git a/neural_compressor/experimental/data/dataloaders/__init__.py b/neural_compressor/experimental/data/dataloaders/__init__.py deleted file mode 100644 index 867409b93c0..00000000000 --- a/neural_compressor/experimental/data/dataloaders/__init__.py +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ============================================================================== -"""Built-in dataloaders for multiple framework backends.""" - -from .dataloader import DATALOADERS - -__all__ = ["DATALOADERS"] diff --git a/neural_compressor/experimental/data/dataloaders/base_dataloader.py b/neural_compressor/experimental/data/dataloaders/base_dataloader.py deleted file mode 100644 index 21303bfad81..00000000000 --- a/neural_compressor/experimental/data/dataloaders/base_dataloader.py +++ /dev/null @@ -1,144 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ============================================================================== -"""BaseDataloder of all dataloaders.""" - -from abc import abstractmethod - -from deprecated import deprecated - - -@deprecated(version="2.0") -class BaseDataLoader: - """Base class for all DataLoaders. - - _generate_dataloader is needed to create a dataloader object - from the general params like batch_size and sampler. The dynamic batching is just to - generate a new dataloader by setting batch_size and last_batch. - """ - - def __init__( - self, - dataset, - batch_size=1, - last_batch="rollover", - collate_fn=None, - sampler=None, - batch_sampler=None, - num_workers=0, - pin_memory=False, - shuffle=False, - distributed=False, - ): - """Initialize BaseDataLoader. - - Args: - dataset (object): dataset from which to load the data - batch_size (int, optional): number of samples per batch. Defaults to 1. - last_batch (str, optional): whether to drop the last batch if it is incomplete. - Support ['rollover', 'discard'], rollover means False, discard means True. - Defaults to 'rollover'. - collate_fn (callable, optional): merge data with outer dimension batch size. Defaults to None. - sampler (Sampler, optional): Sampler object to sample data. Defaults to None. - batch_sampler (BatchSampler, optional): BatchSampler object to generate batch of indices. Defaults to None. - num_workers (int, optional): number of subprocesses to use for data loading. Defaults to 0. - pin_memory (bool, optional): whether to copy data into pinned memory before returning. Defaults to False. - shuffle (bool, optional): whether to shuffle data. Defaults to False. - distributed (bool, optional): whether the dataloader is distributed. Defaults to False. - """ - self.dataset = dataset - self.collate_fn = collate_fn - self.sampler = sampler - self.batch_sampler = batch_sampler - self.num_workers = num_workers - self.pin_memory = pin_memory - self._batch_size = batch_size - self.shuffle = shuffle - self.distributed = distributed - self.last_batch = last_batch - self.drop_last = False if last_batch == "rollover" else True - - self.dataloader = self._generate_dataloader( - self.dataset, - batch_size=batch_size, - last_batch=last_batch, - collate_fn=collate_fn, - sampler=sampler, - batch_sampler=batch_sampler, - num_workers=num_workers, - pin_memory=pin_memory, - shuffle=shuffle, - distributed=distributed, - ) - - def batch(self, batch_size, last_batch=None): - """Set batch size for dataloader. - - Args: - batch_size (int): number of samples per batch. - last_batch (str, optional): whether to drop the last batch if it is incomplete. - Support ['rollover', 'discard'], rollover means False, discard means True. - Defaults to None. - """ - self._batch_size = batch_size - if last_batch is not None: - self.last_batch = last_batch - self.dataloader = self._generate_dataloader( - self.dataset, - batch_size, - self.last_batch, - self.collate_fn, - self.sampler, - self.batch_sampler, - self.num_workers, - self.pin_memory, - self.shuffle, - self.distributed, - ) - - @property - def batch_size(self): - """Get dataloader's batch_size. - - Returns: - int: batch_size - """ - return self._batch_size - - def __iter__(self): - """Yield data in iterative order. - - Returns: - iterator: iterator for dataloder - """ - return iter(self.dataloader) - - @abstractmethod - def _generate_dataloader( - self, - dataset, - batch_size, - last_batch, - collate_fn, - sampler, - batch_sampler, - num_workers, - pin_memory, - shuffle, - distributed, - ): - raise NotImplementedError diff --git a/neural_compressor/experimental/data/dataloaders/dataloader.py b/neural_compressor/experimental/data/dataloaders/dataloader.py deleted file mode 100644 index b632f493065..00000000000 --- a/neural_compressor/experimental/data/dataloaders/dataloader.py +++ /dev/null @@ -1,36 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ============================================================================== -"""Built-in dataloaders for multiple framework backends.""" -from .mxnet_dataloader import MXNetDataLoader -from .onnxrt_dataloader import ONNXRTDataLoader -from .pytorch_dataloader import PyTorchDataLoader -from .tensorflow_dataloader import TensorflowDataLoader - -DATALOADERS = { - "tensorflow": TensorflowDataLoader, - "tensorflow_itex": TensorflowDataLoader, - "keras": TensorflowDataLoader, - "mxnet": MXNetDataLoader, - "pytorch": PyTorchDataLoader, - "pytorch_ipex": PyTorchDataLoader, - "pytorch_fx": PyTorchDataLoader, - "onnxrt_qlinearops": ONNXRTDataLoader, - "onnxrt_integerops": ONNXRTDataLoader, - "onnxrt_qdq": ONNXRTDataLoader, - "onnxruntime": ONNXRTDataLoader, -} diff --git a/neural_compressor/experimental/data/dataloaders/default_dataloader.py b/neural_compressor/experimental/data/dataloaders/default_dataloader.py deleted file mode 100644 index 4358f20d0cb..00000000000 --- a/neural_compressor/experimental/data/dataloaders/default_dataloader.py +++ /dev/null @@ -1,172 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ============================================================================== -"""Default dataloader for multiple framework backends.""" - -import collections -from math import ceil, floor - -import numpy as np -from deprecated import deprecated - -from .base_dataloader import BaseDataLoader -from .fetcher import FETCHERS -from .sampler import BatchSampler, IterableSampler, SequentialSampler - - -@deprecated(version="2.0") -def default_collate(batch): - """Merge data with outer dimension batch size.""" - elem = batch[0] - if isinstance(elem, collections.abc.Mapping): - return {key: default_collate([d[key] for d in batch]) for key in elem} - elif isinstance(elem, collections.abc.Sequence): - batch = zip(*batch) - return [default_collate(samples) for samples in batch] - elif isinstance(elem, np.ndarray): - try: - return np.stack(batch) - except: - return batch - else: - return batch - - -@deprecated(version="2.0") -class DefaultDataLoader(BaseDataLoader): - """DefaultDataLoader for multiple framework backends.""" - - def __init__( - self, - dataset, - batch_size=1, - last_batch="rollover", - collate_fn=None, - sampler=None, - batch_sampler=None, - num_workers=0, - pin_memory=False, - shuffle=False, - distributed=False, - ): - """Initialize DefaultDataLoader. - - Args: - dataset (object): dataset from which to load the data - batch_size (int, optional): number of samples per batch. Defaults to 1. - last_batch (str, optional): whether to drop the last batch if it is incomplete. - Support ['rollover', 'discard'], rollover means False, discard means True. - Defaults to 'rollover'. - collate_fn (callable, optional): merge data with outer dimension batch size. Defaults to None. - sampler (Sampler, optional): Sampler object to sample data. Defaults to None. - batch_sampler (BatchSampler, optional): BatchSampler object to generate batch of indices. Defaults to None. - num_workers (int, optional): number of subprocesses to use for data loading. Defaults to 0. - pin_memory (bool, optional): whether to copy data into pinned memory before returning. Defaults to False. - shuffle (bool, optional): whether to shuffle data. Defaults to False. - distributed (bool, optional): whether the dataloader is distributed. Defaults to False. - """ - self.dataset = dataset - self.last_batch = last_batch - self.sampler = sampler - self.batch_sampler = batch_sampler - self.num_workers = num_workers - self.pin_memory = pin_memory - self.collate_fn = collate_fn - self._batch_size = batch_size - self.shuffle = shuffle - self.distributed = distributed - self.drop_last = False if last_batch == "rollover" else True - if self.collate_fn is None: - self.collate_fn = default_collate - - def batch(self, batch_size, last_batch="rollover"): - """Set batch_size and last_batch.""" - self._batch_size = batch_size - self.last_batch = last_batch - - @property - def dataloader(self): - """Return dataloader.""" - return self - - def __iter__(self): - """Yield data in iterative order.""" - return self._generate_dataloader( - self.dataset, - batch_size=self.batch_size, - last_batch=self.last_batch, - collate_fn=self.collate_fn, - sampler=self.sampler, - batch_sampler=self.batch_sampler, - num_workers=self.num_workers, - pin_memory=self.pin_memory, - shuffle=self.shuffle, - distributed=self.distributed, - ) - - def __len__(self): - """Get dataset length.""" - try: - dataset_len = self.dataset.__len__() - except (AttributeError, TypeError): - dataset_len = 0 - for _ in self.dataset: - dataset_len += 1 - except Exception: - raise ValueError( - f"{self.dataset} is invalid, {self.dataset}" - " does not support calculating the length of its dataloader" - ) - if self.drop_last is False: - dataloader_len = ceil(dataset_len / self.batch_size) - else: - dataloader_len = floor(dataset_len / self.batch_size) - return dataloader_len - - def _generate_dataloader( - self, - dataset, - batch_size, - last_batch, - collate_fn, - sampler, - batch_sampler, - num_workers, - pin_memory, - shuffle, - distributed, - ): - sampler = self._generate_sampler(dataset, distributed) - self.batch_sampler = BatchSampler(sampler, batch_size, self.drop_last) - self.fetcher = FETCHERS[self.dataset_type](dataset, collate_fn, self.drop_last, distributed) - - for batched_indices in self.batch_sampler: - try: - data = self.fetcher(batched_indices) - yield data - except StopIteration: - return - - def _generate_sampler(self, dataset, distributed): - if hasattr(dataset, "__getitem__"): - self.dataset_type = "index" - return SequentialSampler(dataset, distributed) - elif hasattr(dataset, "__iter__"): - self.dataset_type = "iter" - return IterableSampler(dataset) - else: - raise ValueError("dataset type only support (index, iter)") diff --git a/neural_compressor/experimental/data/dataloaders/fetcher.py b/neural_compressor/experimental/data/dataloaders/fetcher.py deleted file mode 100644 index 8f76545e67a..00000000000 --- a/neural_compressor/experimental/data/dataloaders/fetcher.py +++ /dev/null @@ -1,135 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ============================================================================== -"""Definitions of the methods to fetch data from an iterable-style or list-style dataset.""" - -from abc import abstractmethod - -from deprecated import deprecated - - -@deprecated(version="2.0") -class Fetcher(object): - """Base class for different fetchers.""" - - def __init__(self, dataset, collate_fn, drop_last): - """Initialize Fetcher. - - Args: - dataset (object): dataset object from which to get data - collate_fn (callable): merge data with outer dimension batch size - drop_last (bool): whether to drop the last batch if it is incomplete - """ - self.dataset = dataset - self.collate_fn = collate_fn - self.drop_last = drop_last - - @abstractmethod - def __call__(self, batched_indices): - """Fetch data. - - Args: - batched_indices (list): fetch data according to batched_indices - """ - raise NotImplementedError - - -@deprecated(version="2.0") -class IterableFetcher(Fetcher): - """Iterate to get next batch-size samples as a batch.""" - - def __init__(self, dataset, collate_fn, drop_last, distributed): - """Initialize IterableFetcher. - - Args: - dataset (object): dataset object from which to get data - collate_fn (callable): merge data with outer dimension batch size - drop_last (bool): whether to drop the last batch if it is incomplete - distributed (bool): whether the dataloader is distributed - """ - super(IterableFetcher, self).__init__(dataset, collate_fn, drop_last) - self.dataset_iter = iter(dataset) - self.index_whole = 0 - self.process_rank = 0 # The default rank is 0, which represents the main process - self.process_size = 1 # By default, process_size=1, only the main process is running - if distributed: - import horovod.tensorflow as hvd - - hvd.init() - self.process_rank = hvd.rank() - self.process_size = hvd.size() - if self.process_size < 2: - raise EnvironmentError( - "The program is now trying to traverse" - " the distributed TensorFlow DefaultDataLoader in only one process." - " If you do not want to use distributed DataLoader, please set" - " 'distributed: False'. Or If you want to use distributed DataLoader," - " please set 'distributed: True' and launch multiple processes." - ) - - def __call__(self, batched_indices): - """Fetch data. - - Args: - batched_indices (list): fetch data according to batched_indices - """ - batch_data = [] - batch_size = len(batched_indices) - while True: - try: - iter_data = next(self.dataset_iter) - if (self.index_whole - self.process_rank) % self.process_size == 0: - batch_data.append(iter_data) - self.index_whole += 1 - if len(batch_data) == batch_size: - break - except StopIteration: - break - if len(batch_data) == 0 or (self.drop_last and len(batch_data) < len(batched_indices)): - raise StopIteration - return self.collate_fn(batch_data) - - -@deprecated(version="2.0") -class IndexFetcher(Fetcher): - """Take single index or a batch of indices to fetch samples as a batch.""" - - def __init__(self, dataset, collate_fn, drop_last, distributed): - """Initialize IndexFetcher. - - Args: - dataset (object): dataset object from which to get data - collate_fn (callable): merge data with outer dimension batch size - drop_last (bool): whether to drop the last batch if it is incomplete - distributed (bool): whether the dataloader is distributed - """ - super(IndexFetcher, self).__init__(dataset, collate_fn, drop_last) - - def __call__(self, batched_indices): - """Fetch data. - - Args: - batched_indices (list): fetch data according to batched_indices - """ - data = [self.dataset[idx] for idx in batched_indices] - return self.collate_fn(data) - - -FETCHERS = { - "index": IndexFetcher, - "iter": IterableFetcher, -} diff --git a/neural_compressor/experimental/data/dataloaders/mxnet_dataloader.py b/neural_compressor/experimental/data/dataloaders/mxnet_dataloader.py deleted file mode 100644 index c4bbc397d4c..00000000000 --- a/neural_compressor/experimental/data/dataloaders/mxnet_dataloader.py +++ /dev/null @@ -1,60 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ============================================================================== -"""MXNet Dataloader implementation.""" - -import logging - -from deprecated import deprecated - -from neural_compressor.utils.utility import LazyImport - -from .base_dataloader import BaseDataLoader - -mx = LazyImport("mxnet") - - -@deprecated(version="2.0") -class MXNetDataLoader(BaseDataLoader): - """Subclass of BaseDataLoader.""" - - def _generate_dataloader( - self, - dataset, - batch_size, - last_batch, - collate_fn, - sampler, - batch_sampler, - num_workers, - pin_memory, - shuffle, - distributed, - ): - """Overwrite _generate_dataloader function.""" - if shuffle: - logging.warning("Shuffle is not supported yet in MXNetDataLoader, " "ignoring shuffle keyword.") - return mx.gluon.data.DataLoader( - dataset, - batch_size=batch_size, - batchify_fn=collate_fn, - last_batch=last_batch, - num_workers=num_workers, - pin_memory=pin_memory, - sampler=sampler, - batch_sampler=batch_sampler, - ) diff --git a/neural_compressor/experimental/data/dataloaders/onnxrt_dataloader.py b/neural_compressor/experimental/data/dataloaders/onnxrt_dataloader.py deleted file mode 100644 index 042c5a04d62..00000000000 --- a/neural_compressor/experimental/data/dataloaders/onnxrt_dataloader.py +++ /dev/null @@ -1,122 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ============================================================================== -"""Built-in dataloaders for onnxruntime framework backends.""" - -import logging - -from deprecated import deprecated - -from neural_compressor.utils.utility import LazyImport - -from ..datasets.bert_dataset import ONNXRTBertDataset -from .base_dataloader import BaseDataLoader -from .default_dataloader import DefaultDataLoader - -torch = LazyImport("torch") - - -@deprecated(version="2.0") -class ONNXRTBertDataLoader(DefaultDataLoader): - """Built-in dataloader for onnx bert model and its variants.""" - - def _generate_dataloader( - self, - dataset, - batch_size, - last_batch, - collate_fn, - sampler, - batch_sampler, - num_workers, - pin_memory, - shuffle, - distributed, - ): - import numpy as np - from torch.utils.data import DataLoader, SequentialSampler - - sampler = SequentialSampler(dataset) - dataloader = DataLoader(dataset, sampler=sampler, batch_size=batch_size) - dynamic_length = dataset.dynamic_length - model_type = dataset.model_type - max_seq_length = dataset.max_seq_length - - for batch in dataloader: - try: - batch_seq_length = max_seq_length if not dynamic_length else torch.max(batch[-2], 0)[0].item() - batch = tuple(t.detach().cpu().numpy() if not isinstance(t, np.ndarray) else t for t in batch) - if model_type == "bert": - data = [ - batch[0][:, :batch_seq_length], - batch[1][:, :batch_seq_length], - batch[2][:, :batch_seq_length], - ] - else: - data = [batch[0][:, :batch_seq_length], batch[1][:, :batch_seq_length]] - label = batch[-1] - yield data, label - except StopIteration: - return - - -@deprecated(version="2.0") -class ONNXRTDataLoader(BaseDataLoader): - """Built-in dataloader for onnxruntime framework backends.""" - - def _generate_dataloader( - self, - dataset, - batch_size, - last_batch, - collate_fn, - sampler, - batch_sampler, - num_workers, - pin_memory, - shuffle, - distributed, - ): - if shuffle: - logging.warning("Shuffle is not supported yet in ONNXRTDataLoader, " "ignoring shuffle keyword.") - - if isinstance(dataset, ONNXRTBertDataset): - return ONNXRTBertDataLoader( - dataset, - batch_size, - last_batch, - collate_fn, - sampler, - batch_sampler, - num_workers, - pin_memory, - shuffle, - distributed, - ) - else: - return DefaultDataLoader( - dataset, - batch_size, - last_batch, - collate_fn, - sampler, - batch_sampler, - num_workers, - pin_memory, - shuffle, - distributed, - ) diff --git a/neural_compressor/experimental/data/dataloaders/pytorch_dataloader.py b/neural_compressor/experimental/data/dataloaders/pytorch_dataloader.py deleted file mode 100644 index 3de7a5b8140..00000000000 --- a/neural_compressor/experimental/data/dataloaders/pytorch_dataloader.py +++ /dev/null @@ -1,82 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Initialize the Datasets class.""" -from deprecated import deprecated - -from neural_compressor.utils.utility import LazyImport - -from .base_dataloader import BaseDataLoader - -torch = LazyImport("torch") -hvd = LazyImport("horovod.torch") - - -@deprecated(version="2.0") -class PyTorchDataLoader(BaseDataLoader): - """PyTorchDataLoader inherits from BaseDataLoader.""" - - def _generate_dataloader( - self, - dataset, - batch_size, - last_batch, - collate_fn, - sampler, - batch_sampler, - num_workers, - pin_memory, - shuffle, - distributed, - ): - """Generate PyTorch dataloader. - - Args: - dataset: dataset - batch_size (int): batch size - last_batch (string): rollover last batch or not. - collate_fn: collate_fn - sampler: sampler - batch_sampler: batch_sampler - num_workers (int): num_workers - pin_memory (bool): pin_memory - shuffle (bool): shuffle - distributed (bool): distributed - - Returns: - _type_: _description_ - """ - drop_last = False if last_batch == "rollover" else True - assert len(dataset) != 0, "Warning: Dataset is empty, Please check dataset path!" - if distributed and sampler is None: - # TODO: lazy init here - hvd.init() - # sampler option is mutually exclusive with shuffle pytorch - self.sampler = sampler = torch.utils.data.distributed.DistributedSampler( - dataset, num_replicas=hvd.size(), rank=hvd.rank() - ) - - return torch.utils.data.DataLoader( - dataset, - shuffle=shuffle, - batch_size=batch_size, - collate_fn=collate_fn, - drop_last=drop_last, - num_workers=num_workers, - pin_memory=pin_memory, - sampler=sampler, - batch_sampler=batch_sampler, - ) diff --git a/neural_compressor/experimental/data/dataloaders/sampler.py b/neural_compressor/experimental/data/dataloaders/sampler.py deleted file mode 100644 index c80002e9a44..00000000000 --- a/neural_compressor/experimental/data/dataloaders/sampler.py +++ /dev/null @@ -1,148 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ============================================================================== -"""Definitions of the methods to sample data.""" - -from abc import abstractmethod - -from deprecated import deprecated - - -@deprecated(version="2.0") -class Sampler(object): - """Base class for all Samplers. - - __iter__ is needed no matter whether you use IterableSampler - or Squential sampler, if you want implement your own sampler, make clear what the type is - your Dataset, if IterableDataset(method __iter__ implemented), try to use IterableSampler, - else if you have an IndexDataset(method __getitem__ implemented), your dataset should have - method __len__ implemented. - """ - - def __init__(self, data_source): - """Initialize Sampler.""" - pass - - @abstractmethod - def __iter__(self): - """Convert dataloder to an iterator.""" - raise NotImplementedError - - -@deprecated(version="2.0") -class IterableSampler(Sampler): - """Internally samples elements. - - Used for datasets retrieved element by iterator. Yield None to act as a placeholder for each iteration. - """ - - def __init__(self, dataset): - """Initialize IterableSampler. - - Args: - dataset (object): dataset object from which to get data - """ - super(IterableSampler, self).__init__(None) - self.whole_dataset = dataset - - def __iter__(self): - """Yield data in iterative order.""" - while True: - yield None - - def __len__(self): - """Return the length of dataset.""" - raise NotImplementedError("'__len__' for IterableDataset object has not defined") - - -@deprecated(version="2.0") -class SequentialSampler(Sampler): - """Sequentially samples elements, used for datasets retrieved element by index.""" - - def __init__(self, dataset, distributed): - """Initialize SequentialSampler. - - Args: - dataset (object): dataset object from which to get data - distributed (bool): whether the dataloader is distributed - """ - self.whole_dataset = dataset - self.distributed = distributed - - def __iter__(self): - """Yield data in iterative order.""" - self.process_rank = 0 # The default rank is 0, which represents the main process - self.process_size = 1 # By default, process_size=1, only the main process is running - if self.distributed: - import horovod.tensorflow as hvd - - hvd.init() - self.process_rank = hvd.rank() - self.process_size = hvd.size() - if self.process_size < 2: - raise EnvironmentError( - "The program is now trying to traverse" - " the distributed TensorFlow DefaultDataLoader in only one process." - " If you do not want to use distributed DataLoader, please set" - " 'distributed: False'. Or If you want to use distributed DataLoader," - " please set 'distributed: True' and launch multiple processes." - ) - return iter(range(self.process_rank, len(self.whole_dataset), self.process_size)) - - def __len__(self): - """Return the length of dataset.""" - return len(self.whole_dataset) - - -@deprecated(version="2.0") -class BatchSampler(Sampler): - """Yield a batch of indices and number of batches.""" - - def __init__(self, sampler, batch_size, drop_last=True): - """Initialize BatchSampler. - - Args: - sampler (Sampler): sampler used for generating batches - batch_size (int): size of batch - drop_last (bool, optional): whether to drop the last batch if it is incomplete. Defaults to True. - """ - if isinstance(drop_last, bool): - self.drop_last = drop_last - else: - raise ValueError("last_batch only support bool as input") - - self.sampler = sampler - self.batch_size = batch_size - self.drop_last = drop_last - - def __iter__(self): - """Yield data in iterative order.""" - batch = [] - for idx in self.sampler: - batch.append(idx) - if len(batch) == self.batch_size: - yield batch - batch = [] - if len(batch) > 0 and not self.drop_last: - yield batch - - def __len__(self): - """Return the number of batches.""" - if self.drop_last: - return len(self.sampler) // self.batch_size - else: - return (len(self.sampler) + self.batch_size - 1) // self.batch_size diff --git a/neural_compressor/experimental/data/dataloaders/tensorflow_dataloader.py b/neural_compressor/experimental/data/dataloaders/tensorflow_dataloader.py deleted file mode 100644 index 4a8fb4cc4b7..00000000000 --- a/neural_compressor/experimental/data/dataloaders/tensorflow_dataloader.py +++ /dev/null @@ -1,408 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""TensorFlow Dataloader implementation.""" - -import collections -import logging -import sys -from math import ceil, floor - -import numpy as np -from deprecated import deprecated - -from neural_compressor.utils.utility import LazyImport - -from ..datasets.bert_dataset import TensorflowBertDataset, TensorflowModelZooBertDataset -from .base_dataloader import BaseDataLoader -from .default_dataloader import DefaultDataLoader, default_collate -from .fetcher import FETCHERS -from .sampler import BatchSampler - -tf = LazyImport("tensorflow") -neural_compressor = LazyImport("neural_compressor") - - -@deprecated(version="2.0") -class TFDataDataLoader(BaseDataLoader): - """Tensorflow dataloader class. - - In tensorflow1.x dataloader is coupled with the graph, but it also support feed_dict - method to do session run, this dataloader is designed to satisfy the usage of feed dict - in tf1.x. Although it's a general dataloader and can be used in MXNet and PyTorch. - - Args: - dataset: obj. wrapper of needed data. - batch_size: int. batch size - """ - - def __init__(self, dataset, batch_size=1, last_batch="rollover"): - """Initialize `TFDataDataLoader` class.""" - self.dataset = dataset - self.last_batch = last_batch - self._batch_size = batch_size - dataset = dataset.batch(batch_size) - - def batch(self, batch_size, last_batch="rollover"): - """Dataset return data per batch.""" - drop_last = False if last_batch == "rollover" else True - self._batch_size = batch_size - self.dataset = self.dataset.batch(batch_size, drop_last) - - def __iter__(self): - """Iterate dataloader.""" - return self._generate_dataloader( - self.dataset, - batch_size=self.batch_size, - last_batch=self.last_batch, - ) - - def _generate_dataloader( - self, - dataset, - batch_size=1, - last_batch="rollover", - collate_fn=None, - sampler=None, - batch_sampler=None, - num_workers=None, - pin_memory=None, - shuffle=False, - distributed=False, - ): - """Yield data.""" - drop_last = False if last_batch == "rollover" else True - if shuffle: - logging.warning("Shuffle is not supported yet in TFDataLoader, " "ignoring shuffle keyword.") - - def check_dynamic_shape(element_spec): - if isinstance(element_spec, collections.abc.Sequence): - return any([check_dynamic_shape(ele) for ele in element_spec]) - elif isinstance(element_spec, tf.TensorSpec): - return True if element_spec.shape.num_elements() is None else False - else: - raise ValueError("unrecognized element spec...") - - def squeeze_output(output): - if isinstance(output, collections.abc.Sequence): - return [squeeze_output(ele) for ele in output] - elif isinstance(output, np.ndarray): - return np.squeeze(output, axis=0) - else: - raise ValueError("not supported output format....") - - if tf.executing_eagerly(): - index = 0 - outputs = [] - for iter_tensors in dataset: - samples = [] - iter_inputs, iter_labels = iter_tensors[0], iter_tensors[1] - if isinstance(iter_inputs, tf.Tensor): - samples.append(iter_inputs.numpy()) - else: - samples.append((iter_input.numpy() for iter_input in iter_inputs)) - if isinstance(iter_labels, tf.Tensor): - samples.append(iter_labels.numpy()) - else: - samples.append([np.array(l) for l in iter_labels]) - index += 1 - outputs.append(samples) - if index == batch_size: - outputs = default_collate(outputs) - yield outputs - outputs = [] - index = 0 - if len(outputs) > 0: - outputs = default_collate(outputs) - yield outputs - else: - try_single_batch = check_dynamic_shape(dataset.element_spec) - dataset = dataset.batch(1 if try_single_batch else batch_size, drop_last) - ds_iterator = tf.compat.v1.data.make_one_shot_iterator(dataset) - iter_tensors = ds_iterator.get_next() - data_config = tf.compat.v1.ConfigProto() - data_config.use_per_session_threads = 1 - data_config.intra_op_parallelism_threads = 1 - data_config.inter_op_parallelism_threads = 16 - data_sess = tf.compat.v1.Session(config=data_config) - # pylint: disable=no-name-in-module - from tensorflow.python.framework.errors_impl import OutOfRangeError - - while True: - if not try_single_batch: - try: - outputs = data_sess.run(iter_tensors) - yield outputs - except OutOfRangeError: - data_sess.close() - return - else: - try: - outputs = [] - for i in range(0, batch_size): - outputs.append(squeeze_output(data_sess.run(iter_tensors))) - outputs = default_collate(outputs) - yield outputs - except OutOfRangeError: - if len(outputs) == 0: - data_sess.close() - return - else: - outputs = default_collate(outputs) - yield outputs - data_sess.close() - return - - -@deprecated(version="2.0") -class TensorflowBertDataLoader(DefaultDataLoader): - """Subclass of DefaultDataLoader. - - this dataloader is designed to satisfy the usage of Bert models. - """ - - def _generate_dataloader( - self, - dataset, - batch_size, - last_batch, - collate_fn, - sampler, - batch_sampler, - num_workers, - pin_memory, - shuffle, - distributed, - ): - if shuffle: - logging.warning("Shuffle is not supported yet in TensorflowBertDataLoader, " "ignoring shuffle keyword.") - - def bert_collate_fn(batch): - elem = batch[0] - return elem - - drop_last = False if last_batch == "rollover" else True - sampler = self._generate_sampler(dataset, distributed) - self.batch_sampler = BatchSampler(sampler, batch_size, drop_last) - self.fetcher = FETCHERS[self.dataset_type](dataset, bert_collate_fn, drop_last, distributed) - - for batched_indices in self.batch_sampler: - try: - data = self.fetcher(batched_indices) - yield (data[0], batch_size), data[1] - except StopIteration: - return - - -@deprecated(version="2.0") -class TensorflowModelZooBertDataLoader(DefaultDataLoader): - """Subclass of DefaultDataLoader. - - this dataloader is designed to satisfy the usage of Model Zoo Bert models. - """ - - def _generate_dataloader( - self, - dataset, - batch_size, - last_batch, - collate_fn, - sampler, - batch_sampler, - num_workers, - pin_memory, - shuffle, - distributed, - ): - if shuffle: - logging.warning("Shuffle is not supported yet in TensorflowBertDataLoader, " "ignoring shuffle keyword.") - - def bert_collate_fn(batch): - input_ids = [] - input_mask = [] - segment_ids = [] - for elem in batch: - input_ids.append(elem[0][0][0]) - input_mask.append(elem[0][1][0]) - segment_ids.append(elem[0][2][0]) - inputs = [input_ids, input_mask, segment_ids] - return inputs, batch[0][1] - - drop_last = False if last_batch == "rollover" else True - sampler = self._generate_sampler(dataset, distributed) - self.batch_sampler = BatchSampler(sampler, batch_size, drop_last) - self.fetcher = FETCHERS[self.dataset_type](dataset, bert_collate_fn, drop_last, distributed) - - inputs = [] - for batched_indices in self.batch_sampler: - try: - data = self.fetcher(batched_indices) - yield data - except StopIteration: - return - - -@deprecated(version="2.0") -class TensorflowDataLoader(BaseDataLoader): - """DataLoader for framework Tensorflow. - - if it's a tf.data.Dataset we will directly use the dataloader in the other case - will use DefaultDataLoader instead. - """ - - def _generate_dataloader( - self, - dataset, - batch_size, - last_batch, - collate_fn, - sampler, - batch_sampler, - num_workers, - pin_memory, - shuffle, - distributed, - ): - if shuffle: - logging.warning("Shuffle is not supported yet in TensorflowDataLoader, " "ignoring shuffle keyword.") - if isinstance(dataset, tf.data.Dataset): - if int(tf.__version__[0]) > 1: - has_batch = hasattr(dataset, "_batch_size") - else: - has_batch = hasattr(dataset._dataset, "_batch_size") - if has_batch: - raise TypeError( - f"Parameter 'batch_size={batch_size}'" - " conflicts with 'tf.data.Dataset'," - f" because {dataset} is already a BatchDataset." - f" Please pass in 'tf.data.Dataset' without batch attributes." - ) - process_rank = 0 # The default rank is 0, which represents the main process - process_size = 1 # By default, process_size=1, only the main process is running - if self.distributed: - import horovod.tensorflow as hvd - - hvd.init() - process_rank = hvd.rank() - process_size = hvd.size() - if process_size < 2: - raise EnvironmentError( - "The program is now trying to generate" - " the distributed TensorflowDataLoader in only one process." - " If you do not want to use distributed DataLoader, please set" - " 'distributed: False'. Or If you want to use distributed DataLoader," - " please set 'distributed: True' and launch multiple processes." - ) - dataset = dataset.shard(process_size, process_rank) - tf_dataloader = TFDataDataLoader(dataset, batch_size, last_batch=last_batch) - return tf_dataloader - elif isinstance(dataset, TensorflowBertDataset): - if distributed: - raise NotImplementedError( - "Distributed TensorflowBertDataLoader" " is not yet supported, please set 'distributed: False'" - ) - tf_bert_dataloader = TensorflowBertDataLoader( - dataset, - batch_size, - last_batch, - collate_fn, - sampler, - batch_sampler, - num_workers, - pin_memory, - shuffle, - distributed, - ) - return tf_bert_dataloader - elif isinstance(dataset, TensorflowModelZooBertDataset): - if distributed: - raise NotImplementedError( - "Distributed TensorflowBertDataLoader" " is not yet supported, please set 'distributed: False'" - ) - tf_bert_dataloader = TensorflowModelZooBertDataLoader( - dataset, - batch_size, - last_batch, - collate_fn, - sampler, - batch_sampler, - num_workers, - pin_memory, - shuffle, - distributed, - ) - return tf_bert_dataloader - else: - return DefaultDataLoader( - dataset, - batch_size, - last_batch, - collate_fn, - sampler, - batch_sampler, - num_workers, - pin_memory, - shuffle, - distributed, - ) - - def __bool__(self): - """Judgement if the dataloader exists.""" - # workaround in assert dataloader which will overload __len__() without __bool__() - # provided. Calling __len__() in asserting is not supposed and may cause issues. - return True - - def __len__(self): - """Total number of dataset.""" - try: - dataset_len = self.dataset.__len__() - except (AttributeError, TypeError): - try: - dataset_len = 0 - for _ in self.dataset: - dataset_len += 1 - except RuntimeError: - return sum([1 for _ in self]) - except Exception: - raise ValueError( - f"{self.dataset} is invalid, {self.dataset}" - " does not support calculating the length of its dataloader" - ) - process_rank = 0 # The default rank is 0, which represents the main process - process_size = 1 # By default, process_size=1, only the main process is running - if self.distributed: - import horovod.tensorflow as hvd - - hvd.init() - process_rank = hvd.rank() - process_size = hvd.size() - if process_size < 2: - raise EnvironmentError( - "The program is now trying to get length of" - " the distributed TensorflowDataLoader in only one process." - " If you do not want to use distributed DataLoader, please set" - " 'distributed: False'. Or If you want to use distributed DataLoader," - " please set 'distributed: True' and launch multiple processes." - ) - if process_rank < (dataset_len % process_size): - self.dis_dataset_len = dataset_len // process_size + 1 - else: - self.dis_dataset_len = dataset_len // process_size - if self.drop_last is False: - dataloader_len = ceil(self.dis_dataset_len / self.batch_size) - else: - dataloader_len = floor(self.dis_dataset_len / self.batch_size) - return sys.maxsize if dataloader_len > sys.maxsize else dataloader_len diff --git a/neural_compressor/experimental/data/datasets/__init__.py b/neural_compressor/experimental/data/datasets/__init__.py deleted file mode 100644 index 1a6a9457f49..00000000000 --- a/neural_compressor/experimental/data/datasets/__init__.py +++ /dev/null @@ -1,30 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Built-in datasets class for multiple framework backends.""" - -from .dataset import Datasets, Dataset, IterableDataset, dataset_registry -from os.path import dirname, basename, isfile, join -import glob - -modules = glob.glob(join(dirname(__file__), "*.py")) - -for f in modules: - if isfile(f) and not f.startswith("__") and not f.endswith("__init__.py"): - __import__(basename(f)[:-3], globals(), locals(), level=1) - - -__all__ = ["Datasets", "Dataset", "IterableDataset", "dataset_registry"] diff --git a/neural_compressor/experimental/data/datasets/bert_dataset.py b/neural_compressor/experimental/data/datasets/bert_dataset.py deleted file mode 100644 index 5344c1452c2..00000000000 --- a/neural_compressor/experimental/data/datasets/bert_dataset.py +++ /dev/null @@ -1,491 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Built-in BERT datasets class for multiple framework backends.""" - -import dataclasses -import json -import logging -import os -from dataclasses import dataclass -from typing import List, Optional, Union - -from neural_compressor.utils.utility import LazyImport - -from .dataset import Dataset, dataset_registry - -torch = LazyImport("torch") -transformers = LazyImport("transformers") - -logger = logging.getLogger("neural_compressor") - - -@dataset_registry(dataset_type="bert", framework="pytorch", dataset_format="") -class PytorchBertDataset(Dataset): - """PyTorch dataset used for model Bert. - - This Dataset is to construct from the Bert TensorDataset and not a full implementation - from yaml config. The original repo link is: https://github.com/huggingface/transformers. - When you want use this Dataset, you should add it before you initialize your DataLoader. - (TODO) add end to end support for easy config by yaml by adding the method of - load examples and process method. - - Args: dataset (list): list of data. - task (str): the task of the model, support "classifier", "squad". - model_type (str, default='bert'): model type, support 'distilbert', 'bert', - 'xlnet', 'xlm'. - transform (transform object, default=None): transform to process input data. - filter (Filter objects, default=None): filter out examples according - to specific conditions. - - Examples:: - - dataset = [[ - [101,2043,2001], - [1,1,1], - [[0,0,0,0,0,0,0], - [0,0,0,0,0,0,0], - [0,0,0,0,0,0,0]], - [1,1,1], - [1,1,1], - [[0,0,0,0,0,0,0], - [0,0,0,0,0,0,0], - [0,0,0,0,0,0,0]] - ]] - dataset = PytorchBertDataset(dataset=dataset, task='classifier', model_type='bert', - transform=preprocess, filter=filter) - """ - - def __init__(self, dataset, task, model_type="bert", transform=None, filter=None): - """Initialize the attributes of class.""" - self.dataset = dataset - assert task in ("classifier", "squad"), "Bert task support only classifier squad" - self.task = task - self.transform = transform - self.model_type = model_type - - def __len__(self): - """Length of the dataset.""" - return len(self.dataset) - - def __getitem__(self, index): - """Magic method. - - x[i] is roughly equivalent to type(x).__getitem__(x, index) - """ - sample = self.dataset[index] - if self.transform is not None: - sample = self.transform(sample) - if self.task == "classifier": - inputs = {"input_ids": sample[0], "attention_mask": sample[1], "labels": sample[3]} - - if self.model_type != "distilbert": - # XLM, DistilBERT and RoBERTa don't use segment_ids - if self.model_type in ["bert", "xlnet"]: - inputs["token_type_ids"] = sample[2] - sample = (inputs, inputs["labels"]) - - elif self.task == "squad": - inputs = { - "input_ids": sample[0], - "attention_mask": sample[1], - } - if self.model_type != "distilbert": - # XLM, DistilBERT and RoBERTa don't use segment_ids - inputs["token_type_ids"] = sample[2] if self.model_type in ["bert", "xlnet"] else None - if self.model_type in ["xlnet", "xlm"]: - inputs.update({"cls_index": sample[4], "p_mask": sample[5]}) - example_indices = sample[3] - sample = (inputs, example_indices) - return sample - - -@dataset_registry( - dataset_type="GLUE", - framework="onnxrt_qlinearops, \ - onnxrt_integerops", - dataset_format="", -) -class ONNXRTBertDataset(Dataset): - """ONNXRT dataset used for model Bert. - - Args: data_dir (str): The input data dir. - model_name_or_path (str): Path to pre-trained student model or shortcut name, - selected in the list: - max_seq_length (int, default=128): The maximum length after tokenization. - Sequences longer than this will be truncated, - sequences shorter will be padded. - do_lower_case (bool, default=True): Whether to lowercase the input when tokenizing. - task (str, default=mrpc): The name of the task to fine-tune. - Choices include mrpc, qqp, qnli, rte, - sts-b, cola, mnli, wnli. - model_type (str, default='bert'): model type, support 'distilbert', 'bert', - 'mobilebert', 'roberta'. - dynamic_length (bool, default=False): Whether to use fixed sequence length. - evaluate (bool, default=True): Whether do evaluation or training. - transform (transform object, default=None): transform to process input data. - filter (Filter objects, default=None): filter out examples according - to specific conditions. - - Examples:: - - dataset = ONNXRTBertDataset(data_dir=data_dir, model_name_or_path='bert-base-uncase', - transform=preprocess, filter=filter) - """ - - def __init__( - self, - data_dir, - model_name_or_path, - max_seq_length=128, - do_lower_case=True, - task="mrpc", - model_type="bert", - dynamic_length=False, - evaluate=True, - transform=None, - filter=None, - ): - """Initialize the attributes of class.""" - task = task.lower() - model_type = model_type.lower() - assert task in ["mrpc", "qqp", "qnli", "rte", "sts-b", "cola", "mnli", "wnli"], "Unsupported task type" - assert model_type in [ - "distilbert", - "bert", - "mobilebert", - "roberta", - ], "Unsupported \ - model type" - - self.dynamic_length = dynamic_length - self.model_type = model_type - self.max_seq_length = max_seq_length - tokenizer = transformers.AutoTokenizer.from_pretrained(model_name_or_path, do_lower_case=do_lower_case) - self.dataset = load_and_cache_examples( - data_dir, model_name_or_path, max_seq_length, task, model_type, tokenizer, evaluate - ) - - def __len__(self): - """Length of the dataset.""" - return len(self.dataset) - - def __getitem__(self, index): - """Magic method. - - x[i] is roughly equivalent to type(x).__getitem__(x, index) - """ - return self.dataset[index] - - -def load_and_cache_examples(data_dir, model_name_or_path, max_seq_length, task, model_type, tokenizer, evaluate): - """Load and cache the examples. - - Helper Function for ONNXRTBertDataset. - """ - from torch.utils.data import TensorDataset - - processor = transformers.glue_processors[task]() - output_mode = transformers.glue_output_modes[task] - # Load data features from cache or dataset file - if not os.path.exists("./dataset_cached"): - os.makedirs("./dataset_cached") - cached_features_file = os.path.join( - "./dataset_cached", - "cached_{}_{}_{}_{}".format( - "dev" if evaluate else "train", - list(filter(None, model_name_or_path.split("/"))).pop(), - str(max_seq_length), - str(task), - ), - ) - if os.path.exists(cached_features_file): - logger.info("Load features from cached file {}.".format(cached_features_file)) - features = torch.load(cached_features_file) - else: - logger.info("Create features from dataset file at {}.".format(data_dir)) - label_list = processor.get_labels() - if task in ["mnli", "mnli-mm"] and model_type in ["roberta"]: - # HACK(label indices are swapped in RoBERTa pretrained model) - label_list[1], label_list[2] = label_list[2], label_list[1] - examples = processor.get_dev_examples(data_dir) if evaluate else processor.get_train_examples(data_dir) - features = convert_examples_to_features( - examples, - tokenizer, - task=task, - label_list=label_list, - max_length=max_seq_length, - output_mode=output_mode, - ) - logger.info("Save features into cached file {}.".format(cached_features_file)) - torch.save(features, cached_features_file) - # Convert to Tensors and build dataset - all_input_ids = torch.tensor([f.input_ids for f in features], dtype=torch.long) - all_attention_mask = torch.tensor([f.attention_mask for f in features], dtype=torch.long) - all_token_type_ids = torch.tensor([f.token_type_ids for f in features], dtype=torch.long) - all_seq_lengths = torch.tensor([f.seq_length for f in features], dtype=torch.long) - if output_mode == "classification": - all_labels = torch.tensor([f.label for f in features], dtype=torch.long) - elif output_mode == "regression": - all_labels = torch.tensor([f.label for f in features], dtype=torch.float) - dataset = TensorDataset(all_input_ids, all_attention_mask, all_token_type_ids, all_seq_lengths, all_labels) - return dataset - - -def convert_examples_to_features( - examples, - tokenizer, - max_length=128, - task=None, - label_list=None, - output_mode="classification", - pad_token=0, - pad_token_segment_id=0, - mask_padding_with_zero=True, -): - """Convert examples to features. - - Helper function for load_and_cache_examples. - """ - processor = transformers.glue_processors[task]() - if label_list is None: - label_list = processor.get_labels() - logger.info("Use label list {} for task {}.".format(label_list, task)) - label_map = {label: i for i, label in enumerate(label_list)} - features = [] - for ex_index, example in enumerate(examples): - inputs = tokenizer.encode_plus( - example.text_a, - example.text_b, - add_special_tokens=True, - max_length=max_length, - return_token_type_ids=True, - truncation=True, - ) - input_ids, token_type_ids = inputs["input_ids"], inputs["token_type_ids"] - # The mask has 1 for real tokens and 0 for padding tokens. Only real - # tokens are attended to. - attention_mask = [1 if mask_padding_with_zero else 0] * len(input_ids) - - # Zero-pad up to the sequence length. - seq_length = len(input_ids) - padding_length = max_length - len(input_ids) - - input_ids = input_ids + ([pad_token] * padding_length) - attention_mask = attention_mask + ([0 if mask_padding_with_zero else 1] * padding_length) - token_type_ids = token_type_ids + ([pad_token_segment_id] * padding_length) - - assert len(input_ids) == max_length, "Error with input_ids length {} vs {}".format(len(input_ids), max_length) - assert len(attention_mask) == max_length, "Error with attention_mask length {} vs {}".format( - len(attention_mask), max_length - ) - assert len(token_type_ids) == max_length, "Error with token_type_ids length {} vs {}".format( - len(token_type_ids), max_length - ) - if output_mode == "classification": - label = label_map[example.label] - elif output_mode == "regression": - label = float(example.label) - else: - raise KeyError(output_mode) - - feats = InputFeatures( - input_ids=input_ids, - attention_mask=attention_mask, - token_type_ids=token_type_ids, - label=label, - seq_length=seq_length, - ) - features.append(feats) - return features - - -@dataclass(frozen=True) -class InputFeatures: - """Single set of features of data. - - Property names are the same names as the corresponding inputs to a model. - - Args: - input_ids: Indices of input sequence tokens in the vocabulary. - attention_mask: Mask to avoid performing attention on padding token indices. - Mask values selected in ``[0, 1]``: Usually ``1`` for tokens that are NOT MASKED, - ``0`` for MASKED (padded) tokens. - token_type_ids: (Optional) Segment token indices to indicate first and second - portions of the inputs. Only some models use them. - label: (Optional) Label corresponding to the input. Int for classification problems, - float for regression problems. - seq_length: (Optional) The length of input sequence before padding. - """ - - input_ids: List[int] - attention_mask: Optional[List[int]] = None - token_type_ids: Optional[List[int]] = None - label: Optional[Union[int, float]] = None - seq_length: Optional[List[int]] = None - - def to_json_string(self): - """Serialize this instance to a JSON string.""" - return json.dumps(dataclasses.asdict(self)) + "\n" - - -@dataset_registry(dataset_type="bert", framework="tensorflow, tensorflow_itex", dataset_format="") -class TensorflowBertDataset(Dataset): - """Tensorflow dataset used for model Bert. - - This dataset supports tfrecord data, please refer to Guide to create tfrecord file first. - - Args: root (str): path of dataset. - label_file (str): path of label file. - task (str, default='squad'): task type of model. - model_type (str, default='bert'): model type, support 'bert'. - transform (transform object, default=None): transform to process input data. - filter (Filter objects, default=None): filter out examples according - to specific conditions - """ - - def __init__(self, root, label_file, task="squad", model_type="bert", transform=None, filter=None): - """Initialize the attributes of class.""" - import json - - with open(label_file) as lf: - label_json = json.load(lf) - assert label_json["version"] == "1.1", "only support squad 1.1" - self.label = label_json["data"] - self.root = root - self.transform = transform - self.filter = filter - - def __getitem__(self, index): - """Magic method. - - x[i] is roughly equivalent to type(x).__getitem__(x, index). - """ - return self.root, self.label - - def __len__(self): - """Length of the dataset.""" - return 1 - - -class ParseDecodeBert: - """Helper function for TensorflowModelZooBertDataset. - - Parse the features from sample. - """ - - def __call__(self, sample): - """Parse the sample data. - - Args: - sample: Data to be parsed. - """ - import tensorflow as tf - - # Dense features in Example proto. - feature_map = { - "input_ids": tf.compat.v1.VarLenFeature(dtype=tf.int64), - "input_mask": tf.compat.v1.VarLenFeature(dtype=tf.int64), - "segment_ids": tf.compat.v1.VarLenFeature(dtype=tf.int64), - } - - features = tf.io.parse_single_example(sample, feature_map) - - input_ids = features["input_ids"].values - input_mask = features["input_mask"].values - segment_ids = features["segment_ids"].values - - return (input_ids, input_mask, segment_ids) - - -@dataset_registry(dataset_type="mzbert", framework="tensorflow, tensorflow_itex", dataset_format="") -class TensorflowModelZooBertDataset(Dataset): - """Tensorflow dataset for three-input Bert in tf record format. - - Root is a full path to tfrecord file, which contains the file name. - Please use Resize transform when batch_size > 1 - Args: root (str): path of dataset. - label_file (str): path of label file. - task (str, default='squad'): task type of model. - model_type (str, default='bert'): model type, support 'bert'. - transform (transform object, default=None): transform to process input data. - filter (Filter objects, default=None): filter out examples according. - """ - - def __init__(self, root, label_file, task="squad", model_type="bert", transform=None, filter=None, num_cores=28): - """Initialize the attributes of class.""" - import json - - with open(label_file) as lf: - label_json = json.load(lf) - assert label_json["version"] == "1.1", "only support squad 1.1" - self.label = label_json["data"] - import tensorflow as tf - - record_iterator = tf.compat.v1.python_io.tf_record_iterator(root) - example = tf.train.SequenceExample() - for element in record_iterator: - example.ParseFromString(element) - break - feature = example.context.feature - if len(feature["input_ids"].int64_list.value) == 0 and len(feature["input_mask"].int64_list.value) == 0: - raise ValueError( - "Tfrecord format is incorrect, please refer\ - 'https://github.com/tensorflow/models/blob/master/research/\ - object_detection/dataset_tools/' to create correct tfrecord" - ) - # pylint: disable=no-name-in-module - from tensorflow.python.data.experimental import parallel_interleave - - tfrecord_paths = [root] - ds = tf.data.TFRecordDataset.list_files(tfrecord_paths) - ds = ds.apply( - parallel_interleave( - tf.data.TFRecordDataset, - cycle_length=num_cores, - block_length=5, - sloppy=True, - buffer_output_elements=10000, - prefetch_input_elements=10000, - ) - ) - if transform is not None: - transform.transform_list.insert(0, ParseDecodeBert()) - else: - transform = ParseDecodeBert() - ds = ds.map(transform, num_parallel_calls=None) - if filter is not None: - ds = ds.filter(filter) - ds = ds.prefetch(buffer_size=1000) - from ..dataloaders.tensorflow_dataloader import TFDataDataLoader - - ds = TFDataDataLoader(ds) - self.root = [] - for inputs in ds: - self.root.append(inputs) - self.transform = transform - self.filter = filter - - def __getitem__(self, index): - """Magic method. - - x[i] is roughly equivalent to type(x).__getitem__(x, index) - """ - return self.root[index], self.label - - def __len__(self): - """Length of the dataset.""" - return len(self.root) diff --git a/neural_compressor/experimental/data/datasets/coco_dataset.py b/neural_compressor/experimental/data/datasets/coco_dataset.py deleted file mode 100644 index 7a4dddb78a2..00000000000 --- a/neural_compressor/experimental/data/datasets/coco_dataset.py +++ /dev/null @@ -1,339 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# Copyright 2017 The TensorFlow Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ============================================================================== -"""Built-in COCO datasets class for multiple framework backends.""" - -from PIL import Image - -from neural_compressor.utils.utility import LazyImport - -from .dataset import Dataset, IterableDataset, dataset_registry - -tf = LazyImport("tensorflow") -mx = LazyImport("mxnet") -torch = LazyImport("torch") - - -class ParseDecodeCoco: - """Helper function for TensorflowModelZooBertDataset. - - Parse the features from sample. - """ - - def __call__(self, sample): - """Parse the sample data. - - Args: - sample: Data to be parsed. - """ - # Dense features in Example proto. - feature_map = { - "image/encoded": tf.compat.v1.FixedLenFeature([], dtype=tf.string, default_value=""), - "image/object/class/text": tf.compat.v1.VarLenFeature(dtype=tf.string), - "image/object/class/label": tf.compat.v1.VarLenFeature(dtype=tf.int64), - "image/source_id": tf.compat.v1.FixedLenFeature([], dtype=tf.string, default_value=""), - } - sparse_float32 = tf.compat.v1.VarLenFeature(dtype=tf.float32) - # Sparse features in Example proto. - feature_map.update( - { - k: sparse_float32 - for k in [ - "image/object/bbox/xmin", - "image/object/bbox/ymin", - "image/object/bbox/xmax", - "image/object/bbox/ymax", - ] - } - ) - - features = tf.io.parse_single_example(sample, feature_map) - - xmin = tf.expand_dims(features["image/object/bbox/xmin"].values, 0) - ymin = tf.expand_dims(features["image/object/bbox/ymin"].values, 0) - xmax = tf.expand_dims(features["image/object/bbox/xmax"].values, 0) - ymax = tf.expand_dims(features["image/object/bbox/ymax"].values, 0) - - bbox = tf.concat([ymin, xmin, ymax, xmax], 0) - # Force the variable number of bounding boxes into the shape - # [1, num_boxes, coords]. - bbox = tf.expand_dims(bbox, 0) - bbox = tf.transpose(bbox, [0, 2, 1]) - - encoded_image = features["image/encoded"] - image_tensor = tf.image.decode_image(encoded_image, channels=3) - image_tensor.set_shape([None, None, 3]) - - str_label = features["image/object/class/text"].values - int_label = features["image/object/class/label"].values - image_id = features["image/source_id"] - - return image_tensor, (bbox[0], str_label, int_label, image_id) - - -@dataset_registry(dataset_type="COCORecord", framework="tensorflow, tensorflow_itex", dataset_format="") -class COCORecordDataset(IterableDataset): - """Tensorflow COCO dataset in tf record format. - - Root is a full path to tfrecord file, which contains the file name. - Please use Resize transform when batch_size > 1 - - Args: root (str): Root directory of dataset. - num_cores (int, default=28):The number of input Datasets to interleave from in parallel. - transform (transform object, default=None): transform to process input data. - filter (Filter objects, default=None): filter out examples according - to specific conditions. - """ - - def __new__(cls, root, num_cores=28, transform=None, filter=filter): - """Build a new object.""" - record_iterator = tf.compat.v1.python_io.tf_record_iterator(root) - example = tf.train.SequenceExample() - for element in record_iterator: - example.ParseFromString(element) - break - feature = example.context.feature - if ( - len(feature["image/object/class/text"].bytes_list.value) == 0 - and len(feature["image/object/class/label"].int64_list.value) == 0 - ): - raise ValueError( - "Tfrecord format is incorrect, please refer\ - 'https://github.com/tensorflow/models/blob/master/research/\ - object_detection/dataset_tools/create_coco_tf_record.py' to\ - create correct tfrecord" - ) - # pylint: disable=no-name-in-module - from tensorflow.python.data.experimental import parallel_interleave - - tfrecord_paths = [root] - ds = tf.data.TFRecordDataset.list_files(tfrecord_paths) - ds = ds.apply( - parallel_interleave( - tf.data.TFRecordDataset, - cycle_length=num_cores, - block_length=5, - sloppy=True, - buffer_output_elements=10000, - prefetch_input_elements=10000, - ) - ) - if transform is not None: - transform.transform_list.insert(0, ParseDecodeCoco()) - else: - transform = ParseDecodeCoco() - ds = ds.map(transform, num_parallel_calls=None) - if filter is not None: - ds = ds.filter(filter) - ds = ds.prefetch(buffer_size=1000) - return ds - - -@dataset_registry( - dataset_type="COCORaw", - framework="onnxrt_qlinearops, \ - onnxrt_integerops, pytorch, mxnet, tensorflow, \ - tensorflow_itex", - dataset_format="", -) -class COCORaw(Dataset): - """Coco raw dataset. - - Please arrange data in this way: - /root/img_dir/1.jpg - /root/img_dir/2.jpg - ... - /root/img_dir/n.jpg - /root/anno_dir - Please use Resize transform when batch_size > 1 - - Args: root (str): Root directory of dataset. - img_dir (str, default='val2017'): image file directory. - anno_dir (str, default='annotations/instances_val2017.json'): annotation file directory. - transform (transform object, default=None): transform to process input data. - filter (Filter objects, default=None): filter out examples according - to specific conditions. - """ - - def __init__( - self, root, img_dir="val2017", anno_dir="annotations/instances_val2017.json", transform=None, filter=filter - ): - """Initialize the attributes of class.""" - import json - import os - - import numpy as np - from pycocotools.coco import COCO - - self.image_list = [] - self.transform = transform - img_path = os.path.join(root, img_dir) - anno_path = os.path.join(root, anno_dir) - coco = COCO(anno_path) - img_ids = coco.getImgIds() - cat_ids = coco.getCatIds() - for idx, img_id in enumerate(img_ids): - img_info = {} - bboxes = [] - labels = [] - ids = [] - img_detail = coco.loadImgs(img_id)[0] - ids.append(img_detail["file_name"].encode("utf-8")) - pic_height = img_detail["height"] - pic_width = img_detail["width"] - - ann_ids = coco.getAnnIds(imgIds=img_id, catIds=cat_ids) - anns = coco.loadAnns(ann_ids) - for ann in anns: - bbox = ann["bbox"] - if len(bbox) == 0: - continue - bbox = [ - bbox[0] / float(pic_width), - bbox[1] / float(pic_height), - bbox[2] / float(pic_width), - bbox[3] / float(pic_height), - ] - bboxes.append([bbox[1], bbox[0], bbox[1] + bbox[3], bbox[0] + bbox[2]]) - labels.append(coco.cats[ann["category_id"]]["name"].encode("utf8")) - img_file = os.path.join(img_path, img_detail["file_name"]) - if not os.path.exists(img_file) or len(bboxes) == 0: - continue - - if filter and not filter(None, bboxes): - continue - - with Image.open(img_file) as image: - image = np.array(image.convert("RGB")) - self.image_list.append( - ( - image, - [ - np.array(bboxes), - np.array(labels), - np.array([]), - np.array(img_detail["file_name"].encode("utf-8")), - ], - ) - ) - - def __len__(self): - """Length of the dataset.""" - return len(self.image_list) - - def __getitem__(self, index): - """Magic method. - - x[i] is roughly equivalent to type(x).__getitem__(x, index) - """ - sample = self.image_list[index] - if self.transform is not None: - sample = self.transform(sample) - return sample - - -@dataset_registry( - dataset_type="COCONpy", - framework="onnxrt_qlinearops, \ - onnxrt_integerops, pytorch, mxnet, tensorflow, \ - tensorflow_itex", - dataset_format="", -) -class COCONpy(Dataset): - """COCO npy dataset. - - Please arrange data in this way: - /root/npy_dir/1.jpg.npy - /root/npy_dir/2.jpg.npy - ... - /root/npy_dir/n.jpg.npy - /root/anno_dir - - Args: root (str): Root directory of dataset. - npy_dir (str, default='val2017'): npy file directory. - anno_dir (str, default='annotations/instances_val2017.json'): annotation file directory. - transform (transform object, default=None): transform to process input data. - filter (Filter objects, default=None): filter out examples according - to specific conditions. - """ - - def __init__( - self, root, npy_dir="val2017", anno_dir="annotations/instances_val2017.json", transform=None, filter=None - ): - """Initialize the attributes of class.""" - import json - import os - - import numpy as np - from pycocotools.coco import COCO - - self.image_list = [] - npy_path = os.path.join(root, npy_dir) - anno_path = os.path.join(root, anno_dir) - coco = COCO(anno_path) - img_ids = coco.getImgIds() - cat_ids = coco.getCatIds() - for idx, img_id in enumerate(img_ids): - img_info = {} - labels = [] - ids = [] - img_detail = coco.loadImgs(img_id)[0] - ids.append(img_detail["file_name"].encode("utf-8")) - pic_height = img_detail["height"] - pic_width = img_detail["width"] - - ann_ids = coco.getAnnIds(imgIds=img_id, catIds=cat_ids) - anns = coco.loadAnns(ann_ids) - for ann in anns: - bbox = ann["bbox"] - category_id = ann["category_id"] - if len(bbox) == 0: - continue - labels.append((np.array(category_id), np.array(bbox))) - npy_file = os.path.join(npy_path, img_detail["file_name"]) - npy_file = npy_file + ".npy" - if not os.path.exists(npy_file): - continue - - image = np.load(npy_file) - self.image_list.append((image, labels)) - - def __len__(self): - """Length of the dataset.""" - return len(self.image_list) - - def __getitem__(self, index): - """Magic method. - - x[i] is roughly equivalent to type(x).__getitem__(x, index) - """ - sample = self.image_list[index] - return sample diff --git a/neural_compressor/experimental/data/datasets/dataset.py b/neural_compressor/experimental/data/datasets/dataset.py deleted file mode 100644 index 832adcf5f73..00000000000 --- a/neural_compressor/experimental/data/datasets/dataset.py +++ /dev/null @@ -1,1154 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""This is the base class for each framework.""" - -import os -from abc import abstractmethod - -from PIL import Image - -from neural_compressor.utils.utility import LazyImport, singleton - -torch = LazyImport("torch") -torchvision = LazyImport("torchvision") -tf = LazyImport("tensorflow") -mx = LazyImport("mxnet") -np = LazyImport("numpy") -hashlib = LazyImport("hashlib") -gzip = LazyImport("gzip") -tarfile = LazyImport("tarfile") -zipfile = LazyImport("zipfile") -pickle = LazyImport("pickle") -glob = LazyImport("glob") - - -@singleton -class TensorflowDatasets(object): - """The base class of Tensorflow datasets class.""" - - def __init__(self): - """Initialize the attributes of class.""" - self.datasets = {} - self.datasets.update(TENSORFLOW_DATASETS) - - -@singleton -class PyTorchDatasets(object): - """The base class of PyTorch datasets class.""" - - def __init__(self): - """Initialize the attributes of class.""" - self.datasets = { - "ImageFolder": PytorchMxnetWrapDataset(torchvision.datasets.ImageFolder), - } - self.datasets.update(PYTORCH_DATASETS) - - -@singleton -class MXNetDatasets(object): - """The base class of MXNet datasets class.""" - - def __init__(self): - """Initialize the attributes of class.""" - self.datasets = {} - self.datasets.update(MXNET_DATASETS) - - -@singleton -class ONNXRTQLDatasets(object): - """The base class of ONNXRT QLinear datasets class.""" - - def __init__(self): - """Initialize the attributes of class.""" - self.datasets = {} - self.datasets.update(ONNXRTQL_DATASETS) - - -@singleton -class ONNXRTITDatasets(object): - """The base class of ONNXRT IT datasets class.""" - - def __init__(self): - """Initialize the attributes of class.""" - self.datasets = {} - self.datasets.update(ONNXRTIT_DATASETS) - - -class PytorchMxnetWrapDataset: - """The base class for PyTorch and MXNet frameworks. - - Args: - datafunc: The datasets class of PyTorch or MXNet. - """ - - def __init__(self, datafunc): - """Initialize the attributes of class.""" - self.datafunc = datafunc - - def __call__(self, transform=None, filter=None, *args, **kwargs): - """Wrap the dataset for PyTorch and MXNet framework.""" - return PytorchMxnetWrapFunction(self.datafunc, transform=transform, filter=filter, *args, **kwargs) - - -class PytorchMxnetWrapFunction: - """The Helper class for PytorchMxnetWrapDataset. - - Args: - dataset (datasets class): The datasets class of PyTorch or MXNet. - transform (transform object): transform to process input data. - filter (Filter objects): filter out examples according to specific - conditions. - """ - - def __init__(self, dataset, transform, filter, *args, **kwargs): - """Initialize the attributes of class.""" - self.dataset = dataset(*args, **kwargs) - self.transform = transform - self.filter = filter - - def __len__(self): - """Length of the dataset.""" - return len(self.dataset) - - def __getitem__(self, index): - """Magic method. - - x[i] is roughly equivalent to type(x).__getitem__(x, index) - """ - sample = self.dataset[index] - if self.transform is not None: - sample = self.transform(sample) - return sample - - -framework_datasets = { - "tensorflow": TensorflowDatasets, - "tensorflow_itex": TensorflowDatasets, - "mxnet": MXNetDatasets, - "pytorch": PyTorchDatasets, - "pytorch_ipex": PyTorchDatasets, - "pytorch_fx": PyTorchDatasets, - "onnxrt_qdq": ONNXRTQLDatasets, - "onnxrt_qlinearops": ONNXRTQLDatasets, - "onnxruntime": ONNXRTQLDatasets, - "onnxrt_integerops": ONNXRTITDatasets, -} -"""The datasets supported by neural_compressor, it's model specific and can be configured by yaml file. - -User could add new datasets by implementing new Dataset subclass under this directory. -The naming convention of new dataset subclass should be something like ImageClassifier, user -could choose this dataset by setting "imageclassifier" string in tuning.strategy field of yaml. - -Datasets variable is used to store all implemented Dataset subclasses to support -model specific dataset. -""" - - -class Datasets(object): - """A base class for all framework datasets. - - Args: - framework (str): framework name, like:"tensorflow", "tensorflow_itex", "keras", - "mxnet", "onnxrt_qdq", "onnxrt_qlinearops", "onnxrt_integerops", - "pytorch", "pytorch_ipex", "pytorch_fx", "onnxruntime". - """ - - def __init__(self, framework): - """Initialize the attributes of class.""" - assert framework in [ - "tensorflow", - "tensorflow_itex", - "keras", - "mxnet", - "onnxrt_qdq", - "onnxrt_qlinearops", - "onnxrt_integerops", - "pytorch", - "pytorch_ipex", - "pytorch_fx", - "onnxruntime", - ], "framework support tensorflow pytorch mxnet onnxrt" - self.datasets = framework_datasets[framework]().datasets - - def __getitem__(self, dataset_type): - """Magic method. - - x[i] is roughly equivalent to type(x).__getitem__(x, index) - """ - assert dataset_type in self.datasets.keys(), "dataset type only support {}".format(self.datasets.keys()) - return self.datasets[dataset_type] - - -# user/model specific datasets will be registered here -TENSORFLOW_DATASETS = {} -TENSORFLOWITEX_DATASETS = {} -MXNET_DATASETS = {} -PYTORCH_DATASETS = {} -PYTORCHIPEX_DATASETS = {} -PYTORCHFX_DATASETS = {} -ONNXRTQL_DATASETS = {} -ONNXRTIT_DATASETS = {} - -registry_datasets = { - "tensorflow": TENSORFLOW_DATASETS, - "tensorflow_itex": TENSORFLOWITEX_DATASETS, - "mxnet": MXNET_DATASETS, - "pytorch": PYTORCH_DATASETS, - "pytorch_ipex": PYTORCHIPEX_DATASETS, - "pytorch_fx": PYTORCHFX_DATASETS, - "onnxrt_integerops": ONNXRTIT_DATASETS, - "onnxrt_qdq": ONNXRTQL_DATASETS, - "onnxruntime": ONNXRTQL_DATASETS, - "onnxrt_qlinearops": ONNXRTQL_DATASETS, -} - - -def dataset_registry(dataset_type, framework, dataset_format=""): - """Register dataset subclasses. - - Args: - cls (class): The class of register. - dataset_type (str): The dataset registration name - framework (str): support 3 framework including 'tensorflow', 'pytorch', 'mxnet' - data_format (str): The format dataset saved, eg 'raw_image', 'tfrecord' - - Returns: - cls: The class of register. - """ - - def decorator_dataset(cls): - for single_framework in [fwk.strip() for fwk in framework.split(",")]: - assert single_framework in [ - "tensorflow", - "tensorflow_itex", - "mxnet", - "pytorch", - "pytorch_ipex", - "pytorch_fx", - "onnxrt_qlinearops", - "onnxrt_integerops", - "onnxrt_qdq", - "onnxruntime", - ], "The framework support tensorflow mxnet pytorch onnxrt" - dataset_name = dataset_type + dataset_format - if dataset_name in registry_datasets[single_framework].keys(): - raise ValueError("Cannot have two datasets with the same name") - registry_datasets[single_framework][dataset_name] = cls - return cls - - return decorator_dataset - - -class Dataset(object): - """The base class of dataset. - - Subclass datasets should overwrite two methods: - `__getitem__` for indexing to data sample and `__len__`for the size of the dataset - """ - - @abstractmethod - def __getitem__(self, index): - """Magic method. - - x[i] is roughly equivalent to type(x).__getitem__(x, index) - """ - raise NotImplementedError - - # it's suggested to implement your __len__ method though we do not set it in abstract class - # @abstractmethod - # def __len__(self): - # raise NotImplementedError - - -class IterableDataset(object): - """An iterable Dataset. - - Subclass iterable dataset should also implement a method: - `__iter__` for iterating over the samples of the dataset. - """ - - @abstractmethod - def __iter__(self): - """Magic method. - - Returns the iterator object itself. - """ - raise NotImplementedError - - -def download_url(url, root, filename=None, md5=None): # pragma: no cover - """Download from url. - - Args: - url (str): the address to download from. - root (str): the path for saving. - filename (str): the file name for saving. - md5 (str): the md5 string. - """ - import ssl - import urllib - - ssl._create_default_https_context = ssl._create_unverified_context - - root = os.path.expanduser(root) - if not filename: - filename = os.path.basename(url) - fpath = os.path.join(root, filename) - - os.makedirs(root, exist_ok=True) - - if check_integrity(fpath, md5): - print("Using downloaded and verified file: " + fpath) - else: - try: - print("Downloading " + url + " to " + fpath) - urllib.request.urlretrieve(url, fpath, reporthook=gen_bar_updater()) - except (urllib.error.URLError, IOError) as e: - if url[:5] == "https": - url = url.replace("https:", "http:") - print("Failed download. Trying https -> http instead." " Downloading " + url + " to " + fpath) - urllib.request.urlretrieve(url, fpath, reporthook=gen_bar_updater()) - else: - raise e - if not check_integrity(fpath, md5): - raise RuntimeError("File not found or corrupted.") - - -def gen_bar_updater(): - """Generate progress bar.""" - from tqdm import tqdm - - pbar = tqdm(total=None) - - def bar_update(count, block_size, total_size): - """Update progress bar.""" - if pbar.total is None and total_size: - pbar.total = total_size - progress_bytes = count * block_size - pbar.update(progress_bytes - pbar.n) - - return bar_update - - -def check_integrity(fpath, md5): - """Check MD5 checksum.""" - if not os.path.isfile(fpath): - return False - if md5 is None: - return True - return md5 == calculate_md5(fpath) - - -def calculate_md5(fpath, chunk_size=1024 * 1024): - """Generate MD5 checksum for a file.""" - md5 = hashlib.md5() # nosec - with open(fpath, "rb") as f: - for chunk in iter(lambda: f.read(chunk_size), b""): - md5.update(chunk) - return md5.hexdigest() - - -@dataset_registry( - dataset_type="CIFAR10", - framework="onnxrt_qlinearops, \ - onnxrt_integerops", - dataset_format="", -) -class CIFAR10(Dataset): - """The CIFAR10 and CIFAR100 database. - - For CIFAR10: If download is True, it will download dataset to root/ and extract it - automatically, otherwise user can download file from - https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz manually to - root/ and extract it. - For CIFAR100: If download is True, it will download dataset to root/ and extract it - automatically, otherwise user can download file from - https://www.cs.toronto.edu/~kriz/cifar-100-python.tar.gz manually to - root/ and extract it. - - Args: - root (str): Root directory of dataset. - train (bool, default=False): If True, creates dataset from train subset, - otherwise from validation subset. - transform (transform object, default=None): transform to process input data. - filter (Filter objects, default=None): filter out examples according to specific - conditions. - download (bool, default=True): If true, downloads the dataset from the internet - and puts it in root directory. If dataset is already - downloaded, it is not downloaded again. - """ - - url = "https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz" - filename = "cifar-10-python.tar.gz" - tgz_md5 = "c58f30108f718f92721af3b95e74349a" - train_list = [ - ["data_batch_1", "c99cafc152244af753f735de768cd75f"], - ["data_batch_2", "d4bba439e000b95fd0a9bffe97cbabec"], - ["data_batch_3", "54ebc095f3ab1f0389bbae665268c751"], - ["data_batch_4", "634d18415352ddfa80567beed471001a"], - ["data_batch_5", "482c414d41f54cd18b22e5b47cb7c3cb"], - ] - - test_list = [ - ["test_batch", "40351d587109b95175f43aff81a1287e"], - ] - - meta = { - "filename": "batches.meta", - "key": "label_names", - "md5": "5ff9c542aee3614f3951f8cda6e48888", - } - - def __init__(self, root, train=False, transform=None, filter=None, download=True): # pragma: no cover - """Initialize the attributes of class.""" - self.root = root - if download: - self.download() - - if not self._check_integrity(): - raise RuntimeError("Dataset not found or corrupted. You can use download=True to download it") - if train: - downloaded_list = self.train_list - else: - downloaded_list = self.test_list - - self.data = [] - self.targets = [] - for file_name, checksum in downloaded_list: - file_path = os.path.join(self.root, file_name) - with open(file_path, "rb") as f: - entry = pickle.load(f, encoding="latin1") - self.data.append(entry["data"]) - if "labels" in entry: - self.targets.extend(entry["labels"]) - else: - self.targets.extend(entry["fine_labels"]) - self.data = np.vstack(self.data).reshape(-1, 3, 32, 32) - self.data = self.data.transpose((0, 2, 3, 1)) # convert to HWC - - self.load_meta() - self.transform = transform - - def load_meta(self): # pragma: no cover - """Load meta.""" - path = os.path.join(self.root, self.meta["filename"]) - if not check_integrity(path, self.meta["md5"]): - raise RuntimeError( - "Dataset metadata file not found or corrupted." + " You can use download=True to download it" - ) - with open(path, "rb") as infile: - data = pickle.load(infile, encoding="latin1") - self.classes = data[self.meta["key"]] - self.class_to_idx = {_class: i for i, _class in enumerate(self.classes)} - - def __getitem__(self, index): # pragma: no cover - """Magic method. - - x[i] is roughly equivalent to type(x).__getitem__(x, index) - """ - image, label = self.data[index], self.targets[index] - if self.transform is not None: - image, label = self.transform((image, label)) - return image, label - - def __len__(self): # pragma: no cover - """Length of the dataset.""" - return len(self.data) - - def download(self): # pragma: no cover - """Download a file.""" - if self._check_integrity(): - print("Files already downloaded and verified") - return - download_root = os.path.expanduser(self.root) - filename = os.path.basename(self.url) - download_url(self.url, download_root, filename, self.tgz_md5) - archive = os.path.join(download_root, filename) - print("Extracting {} to {}".format(archive, download_root)) - with tarfile.open(archive, "r:gz") as tar: - tar.extractall(path=download_root) - - def _check_integrity(self): # pragma: no cover - """Check MD5 checksum.""" - root = self.root - for fentry in self.train_list + self.test_list: - filename, md5 = fentry[0], fentry[1] - fpath = os.path.join(root, filename) - if not check_integrity(fpath, md5): - return False - return True - - -@dataset_registry(dataset_type="CIFAR10", framework="pytorch", dataset_format="") -class PytorchCIFAR10(CIFAR10): - """The PyTorch datasets for CIFAR10.""" - - def __getitem__(self, index): # pragma: no cover - """Magic method. - - x[i] is roughly equivalent to type(x).__getitem__(x, index) - """ - image, label = self.data[index], self.targets[index] - image = Image.fromarray(image) - if self.transform is not None: - image, label = self.transform((image, label)) - return (image, label) - - -@dataset_registry(dataset_type="CIFAR10", framework="mxnet", dataset_format="") -class MXNetCIFAR10(CIFAR10): - """The MXNet datasets for CIFAR10.""" - - def __getitem__(self, index): # pragma: no cover - """Magic method. - - x[i] is roughly equivalent to type(x).__getitem__(x, index) - """ - image, label = self.data[index], self.targets[index] - image = mx.nd.array(image) - if self.transform is not None: - image, label = self.transform((image, label)) - return (image, label) - - -@dataset_registry(dataset_type="CIFAR10", framework="tensorflow, tensorflow_itex", dataset_format="") -class TensorflowCIFAR10(CIFAR10): - """The Tensorflow datasets for CIFAR10.""" - - def __getitem__(self, index): # pragma: no cover - """Magic method. - - x[i] is roughly equivalent to type(x).__getitem__(x, index) - """ - image, label = self.data[index], self.targets[index] - if self.transform is not None: - image, label = self.transform((image, label)) - if type(image).__name__ == "Tensor": - with tf.compat.v1.Session() as sess: - image = sess.run(image) - elif type(image).__name__ == "EagerTensor": - image = image.numpy() - return (image, label) - - -@dataset_registry( - dataset_type="CIFAR100", - framework="onnxrt_qlinearops, \ - onnxrt_integerops", - dataset_format="", -) -class CIFAR100(CIFAR10): - """CIFAR100 database. - - For CIFAR100: If download is True, it will download dataset to root/ and extract it - automatically, otherwise user can download file from - https://www.cs.toronto.edu/~kriz/cifar-100-python.tar.gz manually to - root/ and extract it. - - Args: - root (str): Root directory of dataset. - train (bool, default=False): If True, creates dataset from train subset, - otherwise from validation subset. - transform (transform object, default=None): transform to process input data. - filter (Filter objects, default=None): filter out examples according to specific - conditions. - download (bool, default=True): If true, downloads the dataset from the internet - and puts it in root directory. If dataset is already - downloaded, it is not downloaded again. - """ - - url = "https://www.cs.toronto.edu/~kriz/cifar-100-python.tar.gz" - filename = "cifar-100-python.tar.gz" - tgz_md5 = "eb9058c3a382ffc7106e4002c42a8d85" - train_list = [ - ["train", "16019d7e3df5f24257cddd939b257f8d"], - ] - test_list = [ - ["test", "f0ef6b0ae62326f3e7ffdfab6717acfc"], - ] - meta = { - "filename": "meta", - "key": "fine_label_names", - "md5": "7973b15100ade9c7d40fb424638fde48", - } - - -@dataset_registry(dataset_type="CIFAR100", framework="pytorch", dataset_format="") -class PytorchCIFAR100(CIFAR100): - """The PyTorch datasets for CIFAR100.""" - - def __getitem__(self, index): # pragma: no cover - """Magic method. - - x[i] is roughly equivalent to type(x).__getitem__(x, index) - """ - image, label = self.data[index], self.targets[index] - image = Image.fromarray(image) - if self.transform is not None: - image, label = self.transform((image, label)) - image = np.array(image) - return (image, label) - - -@dataset_registry(dataset_type="CIFAR100", framework="mxnet", dataset_format="") -class MXNetCIFAR100(CIFAR100): - """The MXNet datasets for CIFAR100.""" - - def __getitem__(self, index): # pragma: no cover - """Magic method. - - x[i] is roughly equivalent to type(x).__getitem__(x, index) - """ - image, label = self.data[index], self.targets[index] - image = mx.nd.array(image) - if self.transform is not None: - image, label = self.transform((image, label)) - return (image, label) - - -@dataset_registry(dataset_type="CIFAR100", framework="tensorflow, tensorflow_itex", dataset_format="") -class TensorflowCIFAR100(CIFAR100): - """The Tensorflow datasets for CIFAR100.""" - - def __getitem__(self, index): # pragma: no cover - """Magic method. - - x[i] is roughly equivalent to type(x).__getitem__(x, index) - """ - image, label = self.data[index], self.targets[index] - if self.transform is not None: - image, label = self.transform((image, label)) - if type(image).__name__ == "Tensor": - with tf.compat.v1.Session() as sess: - image = sess.run(image) - elif type(image).__name__ == "EagerTensor": - image = image.numpy() - return (image, label) - - -@dataset_registry( - dataset_type="MNIST", - framework="onnxrt_qlinearops, \ - onnxrt_integerops", - dataset_format="", -) -class MNIST(Dataset): - """Modified National Institute of Standards and Technology database and FashionMNIST database. - - For MNIST: If download is True, it will download dataset to root/MNIST/, otherwise user - should put mnist.npz under root/MNIST/ manually. - For FashionMNIST: If download is True, it will download dataset to root/FashionMNIST/, - otherwise user should put train-labels-idx1-ubyte.gz, - train-images-idx3-ubyte.gz, t10k-labels-idx1-ubyte.gz - and t10k-images-idx3-ubyte.gz under root/FashionMNIST/ manually. - - Args: - root (str): Root directory of dataset. - train (bool, default=False): If True, creates dataset from train subset, - otherwise from validation subset. - transform (transform object, default=None): transform to process input data. - filter (Filter objects, default=None): filter out examples according to specific - conditions. - download (bool, default=True): If true, downloads the dataset from the internet - and puts it in root directory. If dataset is already - downloaded, it is not downloaded again. - """ - - classes = [ - "0 - zero", - "1 - one", - "2 - two", - "3 - three", - "4 - four", - "5 - five", - "6 - six", - "7 - seven", - "8 - eight", - "9 - nine", - ] - resource = [ - ("https://storage.googleapis.com/tensorflow/tf-keras-datasets/mnist.npz", "8a61469f7ea1b51cbae51d4f78837e45") - ] - - def __init__(self, root, train=False, transform=None, filter=None, download=True): - """Initialize the attributes of class.""" - self.root = root - self.train = train - self.transform = transform - if download: - self.download() - - self.read_data() - - def read_data(self): - """Read data from a file.""" - for file_name, checksum in self.resource: - file_path = os.path.join(self.root, os.path.basename(file_name)) - if not os.path.exists(file_path): - raise RuntimeError("Dataset not found. You can use download=True to download it") - with np.load(file_path, allow_pickle=True) as f: - if self.train: - self.data, self.targets = f["x_train"], f["y_train"] - else: - self.data, self.targets = f["x_test"], f["y_test"] - - def __len__(self): - """Length of the dataset.""" - return len(self.data) - - def __getitem__(self, index): - """Magic method. - - x[i] is roughly equivalent to type(x).__getitem__(x, index) - """ - image, label = self.data[index], int(self.targets[index]) - image = np.expand_dims(image, -1) - if self.transform is not None: - image, label = self.transform((image, label)) - return image, label - - @property - def class_to_idx(self): - """Return a dict of class.""" - return {_class: i for i, _class in enumerate(self.classes)} - - def download(self): - """Download a file.""" - for url, md5 in self.resource: - filename = os.path.basename(url) - if os.path.exists(os.path.join(self.root, filename)): - continue - else: - download_url(url, root=self.root, filename=filename, md5=md5) - - -@dataset_registry(dataset_type="MNIST", framework="pytorch", dataset_format="") -class PytorchMNIST(MNIST): - """The PyTorch datasets for MNIST.""" - - def __getitem__(self, index): - """Magic method. - - x[i] is roughly equivalent to type(x).__getitem__(x, index) - """ - image, label = self.data[index], int(self.targets[index]) - image = Image.fromarray(image, mode="L") - if self.transform is not None: - image, label = self.transform((image, label)) - image = np.array(image) - return (image, label) - - -@dataset_registry(dataset_type="MNIST", framework="mxnet", dataset_format="") -class MXNetMNIST(MNIST): - """The MXNet datasets for MNIST.""" - - def __getitem__(self, index): - """Magic method. - - x[i] is roughly equivalent to type(x).__getitem__(x, index) - """ - image, label = self.data[index], int(self.targets[index]) - image = mx.nd.array(image) - image = image.reshape((image.shape[0], image.shape[1], 1)) - if self.transform is not None: - image, label = self.transform((image, label)) - return (image, label) - - -@dataset_registry(dataset_type="MNIST", framework="tensorflow, tensorflow_itex", dataset_format="") -class TensorflowMNIST(MNIST): - """The Tensorflow datasets for MNIST.""" - - def __getitem__(self, index): - """Magic method. - - x[i] is roughly equivalent to type(x).__getitem__(x, index) - """ - image, label = self.data[index], int(self.targets[index]) - image = np.expand_dims(image, -1) - if self.transform is not None: - image, label = self.transform((image, label)) - if type(image).__name__ == "Tensor": - with tf.compat.v1.Session() as sess: - image = sess.run(image) - elif type(image).__name__ == "EagerTensor": - image = image.numpy() - return (image, label) - - -@dataset_registry( - dataset_type="FashionMNIST", - framework="onnxrt_qlinearops, \ - onnxrt_integerops", - dataset_format="", -) -class FashionMNIST(MNIST): - """FashionMNIST database. - - For FashionMNIST: If download is True, it will download dataset to root/FashionMNIST/, - otherwise user should put train-labels-idx1-ubyte.gz, - train-images-idx3-ubyte.gz, t10k-labels-idx1-ubyte.gz - and t10k-images-idx3-ubyte.gz under root/FashionMNIST/ manually. - - Args: - root (str): Root directory of dataset. - train (bool, default=False): If True, creates dataset from train subset, - otherwise from validation subset. - transform (transform object, default=None): transform to process input data. - filter (Filter objects, default=None): filter out examples according to specific - conditions. - download (bool, default=True): If true, downloads the dataset from the internet - and puts it in root directory. If dataset is already - downloaded, it is not downloaded again. - """ - - resource = [ - ("https://storage.googleapis.com/tensorflow/tf-keras-datasets/" + file_name, None) - for file_name in [ - "train-labels-idx1-ubyte.gz", - "train-images-idx3-ubyte.gz", - "t10k-labels-idx1-ubyte.gz", - "t10k-images-idx3-ubyte.gz", - ] - ] - - classes = ["T-shirt/top", "Trouser", "Pullover", "Dress", "Coat", "Sandal", "Shirt", "Sneaker", "Bag", "Ankle boot"] - - def read_data(self): - """Read data from a file.""" - import struct - - if self.train: - label_path = os.path.join(self.root, "train-labels-idx1-ubyte.gz") - image_path = os.path.join(self.root, "train-images-idx3-ubyte.gz") - else: - label_path = os.path.join(self.root, "t10k-labels-idx1-ubyte.gz") - image_path = os.path.join(self.root, "t10k-images-idx3-ubyte.gz") - with gzip.open(label_path, "rb") as f: - struct.unpack(">II", f.read(8)) - self.targets = np.frombuffer(f.read(), dtype=np.uint8).astype(np.int32) - with gzip.open(image_path, "rb") as f: - struct.unpack(">IIII", f.read(16)) - data = np.frombuffer(f.read(), dtype=np.uint8) - self.data = data.reshape(len(self.targets), 28, 28) - - -@dataset_registry(dataset_type="FashionMNIST", framework="pytorch", dataset_format="") -class PytorchFashionMNIST(FashionMNIST): - """The PyTorch datasets for FashionMNIST.""" - - def __getitem__(self, index): - """Magic method. - - x[i] is roughly equivalent to type(x).__getitem__(x, index) - """ - image, label = self.data[index], int(self.targets[index]) - image = Image.fromarray(image, mode="L") - if self.transform is not None: - image, label = self.transform((image, label)) - image = np.array(image) - return (image, label) - - -@dataset_registry(dataset_type="FashionMNIST", framework="mxnet", dataset_format="") -class MXNetFashionMNIST(FashionMNIST): - """The MXNet Dataset for FashionMNIST.""" - - def __getitem__(self, index): - """Magic method. - - x[i] is roughly equivalent to type(x).__getitem__(x, index) - """ - image, label = self.data[index], int(self.targets[index]) - image = mx.nd.array(image) - image = image.reshape((image.shape[0], image.shape[1], 1)) - if self.transform is not None: - image, label = self.transform((image, label)) - return (image, label) - - -@dataset_registry(dataset_type="FashionMNIST", framework="tensorflow, tensorflow_itex", dataset_format="") -class TensorflowFashionMNIST(FashionMNIST): - """The Tensorflow Dataset for FashionMNIST.""" - - def __getitem__(self, index): - """Magic method. - - x[i] is roughly equivalent to type(x).__getitem__(x, index) - """ - image, label = self.data[index], int(self.targets[index]) - image = np.expand_dims(image, -1) - if self.transform is not None: - image, label = self.transform((image, label)) - if type(image).__name__ == "Tensor": - with tf.compat.v1.Session() as sess: - image = sess.run(image) - elif type(image).__name__ == "EagerTensor": - image = image.numpy() - return (image, label) - - -@dataset_registry( - dataset_type="ImageFolder", - framework="onnxrt_qlinearops, \ - onnxrt_integerops", - dataset_format="", -) -class ImageFolder(Dataset): - """The base class for ImageFolder. - - Expects the data folder to contain subfolders representing the classes to which - its images belong. - - Please arrange data in this way: - root/class_1/xxx.png - root/class_1/xxy.png - root/class_1/xxz.png - ... - root/class_n/123.png - root/class_n/nsdf3.png - root/class_n/asd932_.png - Please put images of different categories into different folders. - - Args: root (str): Root directory of dataset. - transform (transform object, default=None): transform to process input data. - filter (Filter objects, default=None): filter out examples according to specific - conditions. - """ - - def __init__(self, root, transform=None, filter=None): - """Initialize the attributes of class.""" - self.root = root - assert os.path.exists(self.root), "Datapath doesn't exist!" - - self.transform = transform - self.image_list = [] - files = glob.glob(os.path.join(self.root, "*")) - files.sort() - for idx, file in enumerate(files): - imgs = glob.glob(os.path.join(file, "*")) - imgs.sort() - for img in imgs: - self.image_list.append((img, idx)) - - def __len__(self): - """Length of the dataset.""" - return len(self.image_list) - - def __getitem__(self, index): - """Magic method. - - x[i] is roughly equivalent to type(x).__getitem__(x, index) - """ - sample = self.image_list[index] - label = sample[1] - with Image.open(sample[0]) as image: - image = np.array(image) - if self.transform is not None: - image, label = self.transform((image, label)) - return (image, label) - - -@dataset_registry(dataset_type="ImageFolder", framework="mxnet", dataset_format="") -class MXNetImageFolder(ImageFolder): - """The MXNet Dataset for image folder. - - Expects the data folder to contain subfolders representing the classes to which - its images belong. - - Please arrange data in this way: - root/class_1/xxx.png - root/class_1/xxy.png - root/class_1/xxz.png - ... - root/class_n/123.png - root/class_n/nsdf3.png - root/class_n/asd932_.png - Please put images of different categories into different folders. - - Args: root (str): Root directory of dataset. - transform (transform object, default=None): transform to process input data. - filter (Filter objects, default=None): filter out examples according to specific - conditions. - """ - - def __getitem__(self, index): - """Magic method. - - x[i] is roughly equivalent to type(x).__getitem__(x, index) - """ - sample = self.image_list[index] - label = sample[1] - image = mx.image.imread(sample[0]) - if self.transform is not None: - image, label = self.transform((image, label)) - return (image, label) - - -@dataset_registry(dataset_type="ImageFolder", framework="tensorflow, tensorflow_itex", dataset_format="") -class TensorflowImageFolder(ImageFolder): - """The Tensorflow Dataset for image folder. - - Expects the data folder to contain subfolders representing the classes to which - its images belong. - - Please arrange data in this way: - root/class_1/xxx.png - root/class_1/xxy.png - root/class_1/xxz.png - ... - root/class_n/123.png - root/class_n/nsdf3.png - root/class_n/asd932_.png - Please put images of different categories into different folders. - - Args: root (str): Root directory of dataset. - transform (transform object, default=None): transform to process input data. - filter (Filter objects, default=None): filter out examples according to specific - conditions. - """ - - def __getitem__(self, index): - """Magic method. - - x[i] is roughly equivalent to type(x).__getitem__(x, index) - """ - sample = self.image_list[index] - label = sample[1] - with Image.open(sample[0]) as image: - if image.mode != "RGB": - image = image.convert("RGB") - image = np.array(image) - if self.transform is not None: - image, label = self.transform((image, label)) - if type(image).__name__ == "Tensor": - with tf.compat.v1.Session() as sess: - image = sess.run(image) - elif type(image).__name__ == "EagerTensor": - image = image.numpy() - return (image, label) - - -@dataset_registry(dataset_type="TFRecordDataset", framework="tensorflow, tensorflow_itex", dataset_format="") -class TensorflowTFRecordDataset(IterableDataset): - """The Tensorflow TFRecord Dataset. - - Root is a full path to tfrecord file, which contains the file name. - - Args: root (str): filename of dataset. - transform (transform object, default=None): transform to process input data. - filter (Filter objects, default=None): filter out examples according - to specific conditions. - """ - - def __new__(cls, root, transform=None, filter=None): - """Build a new object of TensorflowTFRecordDataset class.""" - # pylint: disable=no-name-in-module - from tensorflow.python.data.experimental import parallel_interleave - from tensorflow.python.platform import gfile - - file_names = gfile.Glob(root) - ds = tf.data.Dataset.from_tensor_slices(file_names) - ds = ds.apply(parallel_interleave(tf.data.TFRecordDataset, cycle_length=len(file_names))) - if transform is not None: - ds = ds.map(transform, num_parallel_calls=None) - ds = ds.prefetch(buffer_size=tf.data.experimental.AUTOTUNE) # this number can be tuned - return ds - - -@dataset_registry(dataset_type="ImageRecord", framework="tensorflow, tensorflow_itex", dataset_format="") -class TensorflowImageRecord(IterableDataset): - """Tensorflow imageNet database in tf record format. - - Please arrange data in this way: - root/validation-000-of-100 - root/validation-001-of-100 - ... - root/validation-099-of-100 - The file name needs to follow this pattern: '* - * -of- *' - - Args: root (str): Root directory of dataset. - transform (transform object, default=None): transform to process input data. - filter (Filter objects, default=None): filter out examples according - to specific conditions. - """ - - """Configuration for Imagenet dataset.""" - - def __new__(cls, root, transform=None, filter=None): - """Build a new object of TensorflowImageRecord class.""" - from tensorflow.python.platform import gfile # pylint: disable=no-name-in-module - - glob_pattern = os.path.join(root, "*-*-of-*") - file_names = gfile.Glob(glob_pattern) - if not file_names: - raise ValueError("Found no files in --root matching: {}".format(glob_pattern)) - - # pylint: disable=no-name-in-module - from tensorflow.python.data.experimental import parallel_interleave - - from neural_compressor.experimental.data.transforms.imagenet_transform import ParseDecodeImagenet - - ds = tf.data.TFRecordDataset.list_files(file_names, shuffle=False) - ds = ds.apply(parallel_interleave(tf.data.TFRecordDataset, cycle_length=len(file_names))) - - if transform is not None: - transform.transform_list.insert(0, ParseDecodeImagenet()) - else: - transform = ParseDecodeImagenet() - ds = ds.map(transform, num_parallel_calls=None) - ds = ds.prefetch(buffer_size=tf.data.experimental.AUTOTUNE) # this number can be tuned - return ds - - -@dataset_registry(dataset_type="VOCRecord", framework="tensorflow, tensorflow_itex", dataset_format="") -class TensorflowVOCRecord(IterableDataset): - """The Tensorflow PASCAL VOC 2012 database in tf record format. - - Please arrange data in this way: - root/val-00000-of-00004.tfrecord - root/val-00001-of-00004.tfrecord - ... - root/val-00003-of-00004.tfrecord - The file name needs to follow this pattern: 'val-*-of-*' - - Args: root (str): Root directory of dataset. - transform (transform object, default=None): transform to process input data. - filter (Filter objects, default=None): filter out examples according - to specific conditions. - """ - - def __new__(cls, root, transform=None, filter=None): - """Build a new object of TensorflowVOCRecord class.""" - from tensorflow.python.platform import gfile # pylint: disable=no-name-in-module - - glob_pattern = os.path.join(root, "%s-*" % "val") - file_names = gfile.Glob(glob_pattern) - if not file_names: - raise ValueError("Found no files in --root matching: {}".format(glob_pattern)) - - # pylint: disable=no-name-in-module - from tensorflow.python.data.experimental import parallel_interleave - - ds = tf.data.TFRecordDataset.list_files(file_names, shuffle=False) - ds = ds.apply(parallel_interleave(tf.data.TFRecordDataset, cycle_length=len(file_names))) - - if transform is not None: - ds = ds.map(transform, num_parallel_calls=None) - ds = ds.prefetch(buffer_size=tf.data.experimental.AUTOTUNE) # this number can be tuned - return ds diff --git a/neural_compressor/experimental/data/datasets/dummy_dataset.py b/neural_compressor/experimental/data/datasets/dummy_dataset.py deleted file mode 100644 index 0a4ddb8b952..00000000000 --- a/neural_compressor/experimental/data/datasets/dummy_dataset.py +++ /dev/null @@ -1,160 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ============================================================================== -"""Dummy dataset for dummy data generation on multiple framework backends.""" - -import logging - -import numpy as np - -from neural_compressor.utils.utility import LazyImport - -from .dataset import Dataset, dataset_registry - -mx = LazyImport("mxnet") -torch = LazyImport("torch") - -logger = logging.getLogger("neural_compressor") - - -@dataset_registry( - dataset_type="dummy", - framework="tensorflow, tensorflow_itex, \ - onnxrt_qlinearops, onnxrt_integerops, \ - pytorch, pytorch_ipex, pytorch_fx, \ - mxnet", - dataset_format="", -) -class DummyDataset(Dataset): - """Dataset used for dummy data generation. - - This Dataset is to construct a dataset from a specific shape. - The value range is calculated from: low * stand_normal(0, 1) + high. - (TODO) construct dummy data from real dataset or iteration of data. - """ - - def __init__(self, shape, low=-128.0, high=127.0, dtype="float32", label=True, transform=None, filter=None): - """Initialize `DummyDataset` class. - - Args: - shape (list or tuple): Support create multi shape tensors, use list of tuples - for each tuple in the list, will create a such size tensor. - low (list or float, default=-128.): Low out the tensor value range from [0, 1] - to [0, low] or [low, 0] if low < 0, if float, will implement all tensors with same low value. - high (list or float, default=127.): High the tensor value by add all tensor element - value high. If list, length of list should be same with shape list. - dtype (list or str, default='float32'): Support multi tensor dtype setting. - If list, length of list should be same with shape list. If str, all tensors will - use same dtype. dtype supports 'float32', 'float16', 'uint8', 'int8', 'int32', 'int64', 'bool'. - label (bool, default=True): Whether to return 0 as label. - transform (transform object, default=None): Dummy dataset does not need transform. - If transform is not None, it will ignore it. - filter (Filter objects, default=None): Filter out examples according to specific conditions. - """ - dtype_map = { - "float32": np.float32, - "float16": np.float16, - "uint8": np.uint8, - "int8": np.int8, - "int32": np.int32, - "int64": np.int64, - "bool": bool, - "string": str, - } - - np.random.seed(9527) - self.transform = transform - self.label = label - if len(shape) == 0: - logger.info("No data in the dummy dataset.") - elif isinstance(shape, list): - # list tensor should same first dimension n - n = shape[0][0] - assert all( - isinstance(elem, tuple) and elem[0] == n for elem in shape - ), "each tensor shape should be tuple and same first dimension" - - if isinstance(low, list): - assert len(low) == len(shape) and all( - isinstance(elem, float) for elem in low - ), "low list should have same length with shape with element data type float" - else: - low = (low * np.ones(len(shape))).astype(float) - - if isinstance(high, list): - assert len(high) == len(shape) and all( - isinstance(elem, float) for elem in high - ), "high list should have same length with shape with element data type float" - else: - high = (high * np.ones(len(shape))).astype(float) - - if isinstance(dtype, list): - assert len(dtype) == len(shape) and all( - elem in dtype_map.keys() for elem in dtype - ), "high list should have same length with shape with element data type float" - else: - dtype = [dtype for i in range(0, len(shape))] - - elif isinstance(shape, tuple): - shape = [shape] - if isinstance(low, float): - low = [low] - else: - assert ( - isinstance(low, list) and len(low) == 1 and isinstance(low[0], float) - ), "low should be float or list of float with length 1" - - if isinstance(high, float): - high = [high] - else: - assert ( - isinstance(high, list) and len(high) == 1 and isinstance(high[0], float) - ), "high should be float or list of float with length 1" - - if isinstance(dtype, str): - assert dtype in dtype_map.keys(), "dtype only support {}".format(dtype_map.keys()) - dtype = [dtype] - else: - assert ( - isinstance(dtype, list) and len(dtype) == 1 and dtype[0] in dtype_map.keys() - ), "dtype should be str or list of str in supported dtypes" - - self.dataset = [] - for idx in range(0, len(shape)): - tensor = np.random.uniform(low=low[idx], high=high[idx], size=shape[idx]) - tensor = tensor.astype(dtype_map[dtype[idx]]) - self.dataset.append(tensor) - - if len(self.dataset) == 1: - self.dataset = self.dataset[0] - else: - self.dataset = [elem for elem in zip(*self.dataset)] - - def __len__(self): - """Return the length of dataset.""" - return len(self.dataset) - - def __getitem__(self, index): - """Return the item of dataset according to the given index.""" - sample = self.dataset[index] - if self.transform is not None: - logger.warning("Dummy dataset does not need transform.") - - if self.label: - return sample, 0 - else: - return sample diff --git a/neural_compressor/experimental/data/datasets/dummy_dataset_v2.py b/neural_compressor/experimental/data/datasets/dummy_dataset_v2.py deleted file mode 100644 index b1b7758ffa9..00000000000 --- a/neural_compressor/experimental/data/datasets/dummy_dataset_v2.py +++ /dev/null @@ -1,308 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ============================================================================== -"""Dummy dataset for dummy_v2/sparse_dummy_v2 data generation on multiple framework backends.""" - -import sys -from functools import reduce - -import numpy as np - -from neural_compressor.utils.utility import LazyImport - -from .dataset import IterableDataset, dataset_registry - -mx = LazyImport("mxnet") -torch = LazyImport("torch") - - -@dataset_registry( - dataset_type="dummy_v2", - framework="tensorflow, tensorflow_itex, \ - onnxrt_qlinearops, onnxrt_integerops, \ - pytorch, pytorch_ipex, pytorch_fx, mxnet", - dataset_format="", -) -class DummyDataset(IterableDataset): - """Dataset used for dummy_v2 data generation. - - This Dataset is to construct a dataset from a input shape and label shape. - The value range is calculated from: low * stand_normal(0, 1) + high. - """ - - def __init__( - self, input_shape, label_shape=None, low=-128.0, high=127.0, dtype="float32", transform=None, filter=None - ): - """Initialize `DummyDataset` class. - - Args: - sample_size (int): Total size of the dummy samples. - input_shape (list or tuple): Create single or multi input tensors, - tuple represent the sample shape of the dataset, e.g. an image size should be - represented as (224, 224, 3), list contains multiple tuple and represent multi input tensors. - label_shape (list or tuple): Create single or multi label tensors, - tuple represent the label shape of the dataset, e.g. an label size should be - represented as (1, ), list contains multiple tuple and represent multi label tensors. - low (list or float, default=-128.): Low out the tensor value range from [0, 1] - to [0, low] or [low, 0] if low < 0. If float, will implement all tensors with same low value. - high (list or float, default=127.): High the tensor value by add all tensor element value high. - If list, length of list should be same with shape list. - dtype (list or str, default='float32'): Support multi tensor dtype setting. - If list, length of list should be same with shape list. - If str, all tensors will use same dtype. - dtype supports 'float32', 'float16', 'uint8', 'int8','int32', 'int64', 'bool'. - transform (transform object, default=None): dummy_v2 dataset does not need transform. - If transform is not None, it will ignore it. - filter (Filter objects, default=None): Filter out examples according to specific conditions. - """ - self.dtype_map = { - "float32": np.float32, - "float16": np.float16, - "uint8": np.uint8, - "int8": np.int8, - "int32": np.int32, - "int64": np.int64, - "bool": bool, - } - - np.random.seed(9527) - self.transform = transform - self.input_shape = input_shape - self.label_shape = label_shape - self.low = low - self.high = high - self.dtype = dtype - - if label_shape is None: - self.label_dim = 0 - elif isinstance(label_shape, tuple): - self.label_dim = 1 - else: - self.label_dim = len(label_shape) - - self.input_dim = 1 if isinstance(input_shape, tuple) else len(input_shape) - self.total_dim = self.input_dim + self.label_dim - - if isinstance(high, list): - assert len(high) == self.total_dim and all( - isinstance(elem, float) for elem in high - ), "high value list length should same with label dim + input_dim" - else: - self.high = (high * np.ones(self.total_dim)).astype(np.float32) - - if isinstance(low, list): - assert len(low) == self.total_dim and all( - isinstance(elem, float) for elem in low - ), "low value list length should same with label dim + input_dim" - else: - self.low = (low * np.ones(self.total_dim)).astype(np.float32) - - if isinstance(dtype, list): - assert len(dtype) == self.total_dim and all( - elem in self.dtype_map.keys() for elem in dtype - ), "dtype list length should same with label dim + input_dim" - else: - self.dtype = [self.dtype for i in range(0, self.total_dim)] - - if isinstance(input_shape, tuple): - self.input_shape = [input_shape] - - if isinstance(label_shape, tuple): - self.label_shape = [label_shape] - - def __iter__(self): - """Yield data in iterative order.""" - while True: - input_data = [] - for idx in range(0, self.input_dim): - tensor = np.random.uniform(low=self.low[idx], high=self.high[idx], size=self.input_shape[idx]) - tensor = tensor.astype(self.dtype_map[self.dtype[idx]]) - input_data.append(tensor) - - label = [] - for idx in range(0, self.label_dim): - shift_idx = self.input_dim + idx - tensor = np.random.uniform( - low=self.low[shift_idx], high=self.high[shift_idx], size=self.label_shape[idx] - ) - tensor = tensor.astype(self.dtype_map[self.dtype[shift_idx]]) - label.append(tensor) - - if len(input_data) == 1: - input_data = input_data[0] - - if len(label) == 1: - label = label[0] - - if len(label) > 0: - yield input_data, label - else: - yield input_data - - def __len__(self): - """Return the length of dataset.""" - return sys.maxsize - - -@dataset_registry( - dataset_type="sparse_dummy_v2", - framework="tensorflow, tensorflow_itex, \ - onnxrt_qlinearops, onnxrt_integerops, \ - pytorch, pytorch_ipex, pytorch_fx, mxnet", - dataset_format="", -) -class SparseDummyDataset(IterableDataset): - """Dataset used for sparse_dummy_v2 data generation. - - This Dataset is to construct a dataset from a input shape and label shape. - The value range is calculated from: low * stand_normal(0, 1) + high. - """ - - def __init__( - self, - dense_shape, - label_shape=None, - sparse_ratio=0.5, - low=-128.0, - high=127.0, - dtype="float32", - transform=None, - filter=None, - ): - """Initialize `SparseDummyDataset` class. - - Args: - sample_size (int): Total size of the dummy samples. - dense_shape (list or tuple): Create single or multi sparse tensors, tuple represent - the sample shape of the dataset, e.g. an image size should be represented as (224, 224, 3), - list contains multiple tuple and represent multi input tensors. - label_shape (list or tuple): Create single or multi label tensors, tuple represent - the label shape of the dataset, e.g. an label size should be represented as (1, ), - list contains multiple tuple and represent multi label tensors. - sparse_ratio (float, default=0.5): The ratio of sparsity, supports [0, 1]. - low (list or float, default=-128.): Low out the tensor value range from [0, 1] - to [0, low] or [low, 0] if low < 0. If float, will implement all tensors with same low value. - high (list or float, default=127.): High the tensor value by add all tensor element value high. - If list, length of list should be same with shape list. - dtype (list or str, default='float32'): Support multi tensor dtype setting. If list, - length of list should be same with shape list. If str, all tensors will use same dtype. - dtype supports 'float32', 'float16', 'uint8', 'int8', 'int32', 'int64', 'bool'. - transform (transform object, default=None): dummy_v2 dataset does not need transform. - If transform is not None, it will ignore it. - filter (Filter objects, default=None): Filter out examples according to specific conditions. - """ - self.dtype_map = { - "float32": np.float32, - "float16": np.float16, - "uint8": np.uint8, - "int8": np.int8, - "int32": np.int32, - "int64": np.int64, - "bool": bool, - } - - np.random.seed(9527) - self.transform = transform - self.dense_shape = dense_shape - self.label_shape = label_shape - self.sparse_ratio = sparse_ratio - self.low = low - self.high = high - self.dtype = dtype - - if isinstance(dense_shape, tuple): - self.dense_shape = [dense_shape] - - if label_shape is None: - self.label_dim = 0 - else: - if isinstance(label_shape, tuple): - self.label_shape = [label_shape] - if len(self.label_shape) == 1 and len(self.label_shape) != len(self.dense_shape): - self.label_shape = len(self.dense_shape) * self.label_shape - assert len(self.label_shape) == len( - self.dense_shape - ), "length of dense_shape should be euqal to length of label_shape" - self.label_dim = len(self.label_shape) - - self.input_dim = 1 if isinstance(dense_shape, tuple) else len(dense_shape) - self.total_dim = self.input_dim + self.label_dim - - if isinstance(sparse_ratio, list): - assert len(sparse_ratio) == self.input_dim and all( - isinstance(elem, float) for elem in sparse_ratio - ), "sparse_ratio list length should same with input_dim" - else: - self.sparse_ratio = (sparse_ratio * np.ones(self.input_dim)).astype(np.float32) - assert all([0 <= i <= 1 for i in self.sparse_ratio]), "sparse_ratio should be in [0,1]" - - if isinstance(high, list): - assert len(high) == self.total_dim and all( - isinstance(elem, float) for elem in high - ), "high value list length should same with label dim + input_dim" - else: - self.high = (high * np.ones(self.total_dim)).astype(np.float32) - - if isinstance(low, list): - assert len(low) == self.total_dim and all( - isinstance(elem, float) for elem in low - ), "low value list length should same with label dim + input_dim" - else: - self.low = (low * np.ones(self.total_dim)).astype(np.float32) - - if isinstance(dtype, list): - assert len(dtype) == self.total_dim and all( - elem in self.dtype_map.keys() for elem in dtype - ), "dtype list length should same with label dim + input_dim" - else: - self.dtype = [self.dtype for i in range(0, self.total_dim)] - - def __iter__(self): - """Yield data in iterative order.""" - while True: - input_data = [] - for idx, shape in enumerate(self.dense_shape): - dim = len(shape) - total = reduce(lambda x, y: x * y, shape) - sparse_num = round(total * (1 - self.sparse_ratio[idx])) - val = np.random.uniform(low=self.low[idx], high=self.high[idx], size=sparse_num) - val = val.astype(self.dtype_map[self.dtype[idx]]) - nums = np.arange(sparse_num) - indices = [] - dim_shape = [reduce(lambda x, y: x * y, shape[i:]) / shape[i] for i in range(len(shape))] - for num in nums: - indice = [] - for item in dim_shape: - indice.append(num // item) - num = num - indice[-1] * item if num - indice[-1] * item > 0 else num - indices.append(indice) - - if self.label_dim > 0: - shift_idx = self.input_dim + idx - tensor = np.random.uniform( - low=self.low[shift_idx], high=self.high[shift_idx], size=self.label_shape[idx] - ) - tensor = tensor.astype(self.dtype_map[self.dtype[shift_idx]]) - input_data.append([(np.array(indices), val), tensor]) - else: - input_data.append((np.array(indices), val)) - - yield input_data - - def __len__(self): - """Return the length of dataset.""" - return sys.maxsize diff --git a/neural_compressor/experimental/data/datasets/imagenet_dataset.py b/neural_compressor/experimental/data/datasets/imagenet_dataset.py deleted file mode 100644 index 3a47e4e9506..00000000000 --- a/neural_compressor/experimental/data/datasets/imagenet_dataset.py +++ /dev/null @@ -1,241 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# Copyright 2017 The TensorFlow Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ============================================================================== -"""Dataset for ImageNet data generation on multiple framework backends.""" - -import os -import re - -import numpy as np -from PIL import Image - -from neural_compressor.utils import logger -from neural_compressor.utils.utility import LazyImport - -from .dataset import Dataset, IterableDataset, dataset_registry - -tf = LazyImport("tensorflow") -mx = LazyImport("mxnet") -torch = LazyImport("torch") - - -@dataset_registry( - dataset_type="ImagenetRaw", - framework="onnxrt_qlinearops, \ - onnxrt_integerops", - dataset_format="", -) -class ImagenetRaw(Dataset): - """Configuration for ImageNet raw dataset. - - Please arrange data in this way: - data_path/img1.jpg - data_path/img2.jpg - ... - data_path/imgx.jpg - dataset will read name and label of each image from image_list file, - if user set image_list to None, it will read from data_path/val_map.txt automatically. - """ - - def __init__(self, data_path, image_list, transform=None, filter=None): - """Initialize `ImagenetRaw` class. - - Args: - data_path (str): Root directory of dataset. - image_list (str): Data file, record image_names and their labels. - transform (transform object, default=None): Transform to process input data. - filter (Filter objects, default=None): Filter out examples according to specific conditions. - """ - self.image_list = [] - self.label_list = [] - self.data_path = data_path - self.transform = transform - not_found = 0 - if image_list is None: - # by default look for val.txt - image_list = os.path.join(data_path, "val.txt") - - with open(image_list, "r") as f: - for s in f: - image_name, label = re.split(r"\s+", s.strip()) - src = os.path.join(data_path, image_name) - if not os.path.exists(src): - # if the image does not exists ignore it - not_found += 1 - continue - self.image_list.append(src) - self.label_list.append(int(label)) - - if not self.image_list: - raise ValueError("no images in image list found") - if not_found > 0: - print("reduced image list, %d images not found", not_found) - - def __getitem__(self, index): - """Return the item of dataset according to the given index.""" - image_path, label = self.image_list[index], self.label_list[index] - with Image.open(image_path) as image: - image = np.array(image.convert("RGB")) - if self.transform is not None: - image, label = self.transform((image, label)) - return (image, label) - - def __len__(self): - """Return the length of dataset.""" - return len(self.image_list) - - -@dataset_registry(dataset_type="ImagenetRaw", framework="pytorch", dataset_format="") -class PytorchImagenetRaw(ImagenetRaw): - """Dataset for ImageNet data generation on pytorch backend.""" - - def __getitem__(self, index): - """Return the item of dataset according to the given index.""" - image_path, label = self.image_list[index], self.label_list[index] - with Image.open(image_path) as image: - image = image.convert("RGB") - if self.transform is not None: - image, label = self.transform((image, label)) - image = np.array(image) - return (image, label) - - -@dataset_registry(dataset_type="ImagenetRaw", framework="mxnet", dataset_format="") -class MXNetImagenetRaw(ImagenetRaw): - """Dataset for ImageNet data generation on mxnet backend.""" - - def __getitem__(self, index): - """Return the item of dataset according to the given index.""" - image_path, label = self.image_list[index], self.label_list[index] - image = mx.image.imread(image_path) - if self.transform is not None: - image, label = self.transform((image, label)) - return (image, label) - - -@dataset_registry( - dataset_type="ImagenetRaw", - framework="tensorflow, \ - tensorflow_itex", - dataset_format="", -) -class TensorflowImagenetRaw(ImagenetRaw): - """Dataset for ImageNet data generation on tensorflow/inteltensorflow/tensorflow_itex backend.""" - - def __getitem__(self, index): - """Return the item of dataset according to the given index.""" - image_path, label = self.image_list[index], self.label_list[index] - with Image.open(image_path) as image: - image = np.array(image.convert("RGB")) - if self.transform is not None: - image, label = self.transform((image, label)) - if type(image).__name__ == "Tensor": - with tf.compat.v1.Session() as sess: - image = sess.run(image) - elif type(image).__name__ == "EagerTensor": - image = image.numpy() - return (image, label) - - -@dataset_registry(dataset_type="Imagenet", framework="tensorflow", dataset_format="") -class TensorflowImagenetDataset(IterableDataset): - """Configuration for Imagenet dataset.""" - - def __new__(cls, root, subset="validation", num_cores=28, transform=None, filter=None): - """New a imagenet dataset for tensorflow.""" - assert subset in ("validation", "train"), "only support subset (validation, train)" - logger.warning("This api is going to be deprecated, " "please use ImageRecord instead.") - - from tensorflow.python.platform import gfile - - glob_pattern = os.path.join(root, "%s-*-of-*" % subset) - file_names = gfile.Glob(glob_pattern) - if not file_names: - raise ValueError("Found no files in --root matching: {}".format(glob_pattern)) - - from tensorflow.python.data.experimental import parallel_interleave - - from neural_compressor.data.transforms.imagenet_transform import ParseDecodeImagenet - - ds = tf.data.TFRecordDataset.list_files(file_names, shuffle=False) - ds = ds.apply(parallel_interleave(tf.data.TFRecordDataset, cycle_length=num_cores)) - - if transform is not None: - transform.transform_list.insert(0, ParseDecodeImagenet()) - else: - transform = ParseDecodeImagenet() - ds = ds.map(transform, num_parallel_calls=None) - - ds = ds.prefetch(buffer_size=tf.data.experimental.AUTOTUNE) # this number can be tuned - return ds - - -@dataset_registry( - dataset_type="Imagenet", - framework="onnxrt_qlinearops, \ - onnxrt_integerops", - dataset_format="", -) -class ONNXRTImagenetDataset(Dataset): - """Configuration for Imagenet dataset.""" - - def __init__(self, root, subset="val", num_cores=28, transform=None, filter=None): - """Initialize `ONNXRTImagenetDataset` class.""" - self.val_dir = os.path.join(root, subset) - assert os.path.exists(self.val_dir), ( - "find no val dir in {}".format(root) + "please make sure there are train/val subfolders" - ) - import glob - - logger.warning("This api is going to be deprecated, " "please use ImageRecord instead.") - - self.transform = transform - self.image_list = [] - files = glob.glob(os.path.join(self.val_dir, "*")) - files.sort() - for idx, file in enumerate(files): - imgs = glob.glob(os.path.join(file, "*")) - for img in imgs: - self.image_list.append((img, idx)) - - def __len__(self): - """Return the number of images.""" - return len(self.image_list) - - def __getitem__(self, index): - """Return the item of dataset according to the given index.""" - from PIL import Image - - sample = self.image_list[index] - image = Image.open(sample[0]) - if self.transform is not None: - image, label = self.transform((image, sample[1])) - return (image, label) diff --git a/neural_compressor/experimental/data/datasets/style_transfer_dataset.py b/neural_compressor/experimental/data/datasets/style_transfer_dataset.py deleted file mode 100644 index 7c665190a01..00000000000 --- a/neural_compressor/experimental/data/datasets/style_transfer_dataset.py +++ /dev/null @@ -1,95 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ============================================================================== -"""Dataset used for style transfer task on multiple framework backends.""" - -import glob -import os - -import numpy as np - -from .dataset import Dataset, dataset_registry - - -@dataset_registry( - dataset_type="style_transfer", - framework="tensorflow, \ - tensorflow_itex", - dataset_format="", -) -class StyleTransferDataset(Dataset): - """Dataset used for style transfer task on tensorflow/inteltensorflow/tensorflow_itex backend. - - This Dataset is to construct a dataset from two specific image holders representing - content image folder and style image folder. - """ - - def __init__( - self, - content_folder, - style_folder, - crop_ratio=0.1, - resize_shape=(256, 256), - image_format="jpg", - transform=None, - filter=None, - ): - """Initialize `StyleTransferDataset` class. - - Args: - content_folder (str): Root directory of content images. - style_folder (str): Root directory of style images. - crop_ratio (float, default=0.1): Cropped ratio to each side. - resize_shape (tuple, default=(256, 256)): Target size of image. - image_format (str, default='jpg'): Target image format. - transform (transform object, default=None): Transform to process input data. - filter (Filter objects, default=None): Filter out examples according to specific conditions. - """ - self.transform = transform - self.content_folder = content_folder - self.style_folder = style_folder - self.resize_shape = resize_shape - self.crop_ratio = crop_ratio - self.content_images = glob.glob(os.path.join(content_folder, "*" + image_format)) - self.style_images = glob.glob(os.path.join(style_folder, "*" + image_format)) - self.image_list = [] - for content in self.content_images: - for style in self.style_images: - self.image_list.append((content, style)) - - def __len__(self): - """Return the length of dataset.""" - return len(self.image_list) - - def __getitem__(self, index): - """Return the item of dataset according to the given index.""" - from PIL import Image - - content_image, style_image = self.image_list[index] - content_image = Image.open(content_image) - style_image = Image.open(style_image) - width, height = style_image.size - crop_ratio = self.crop_ratio - crop_box = (crop_ratio * height, crop_ratio * width, (1 - crop_ratio) * height, (1 - crop_ratio) * width) - content_image = np.asarray(content_image.resize(self.resize_shape)) - style_image = np.asarray(style_image.resize(self.resize_shape)) - if content_image.max() > 1.0: - content_image = content_image / 255.0 - if style_image.max() > 1.0: - style_image = style_image / 255.0 - - return (content_image, style_image), 0 diff --git a/neural_compressor/experimental/data/filters/__init__.py b/neural_compressor/experimental/data/filters/__init__.py deleted file mode 100644 index 760ac6e8927..00000000000 --- a/neural_compressor/experimental/data/filters/__init__.py +++ /dev/null @@ -1,30 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Built-in filter.""" - -from .filter import FILTERS, Filter, filter_registry -from os.path import dirname, basename, isfile, join -import glob - -modules = glob.glob(join(dirname(__file__), "*.py")) - -for f in modules: - if isfile(f) and not f.startswith("__") and not f.endswith("__init__.py"): - __import__(basename(f)[:-3], globals(), locals(), level=1) - - -__all__ = ["FILTERS", "Filter", "filter_registry"] diff --git a/neural_compressor/experimental/data/filters/coco_filter.py b/neural_compressor/experimental/data/filters/coco_filter.py deleted file mode 100644 index 6172108918c..00000000000 --- a/neural_compressor/experimental/data/filters/coco_filter.py +++ /dev/null @@ -1,63 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Built-in COCO filter.""" - -from neural_compressor.utils.utility import LazyImport - -from .filter import Filter, filter_registry - -tf = LazyImport("tensorflow") - - -@filter_registry(filter_type="LabelBalanceCOCORecord", framework="tensorflow, tensorflow_itex") -class LabelBalanceCOCORecordFilter(Filter): - """The label balance filter for COCO Record.""" - - def __init__(self, size=1): - """Initialize the attribute of class.""" - self.size = size - - def __call__(self, image, label): - """Execute the filter. - - Args: - image: Not used. - label: label of a sample. - """ - return tf.math.equal(len(label[0]), self.size) - - -@filter_registry( - filter_type="LabelBalanceCOCORaw", - framework="tensorflow, \ - tensorflow_itex, pytorch, mxnet, onnxrt_qlinearops, onnxrt_integerops", -) -class LabelBalanceCOCORawFilter(Filter): - """The label balance filter for COCO raw data.""" - - def __init__(self, size=1): - """Initialize the attribute of class.""" - self.size = size - - def __call__(self, image, label): - """Execute the filter. - - Args: - image: Not used. - label: label of a sample. - """ - return len(label) == self.size diff --git a/neural_compressor/experimental/data/filters/filter.py b/neural_compressor/experimental/data/filters/filter.py deleted file mode 100644 index 0bdff5afdd4..00000000000 --- a/neural_compressor/experimental/data/filters/filter.py +++ /dev/null @@ -1,189 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""The base filter class for all frameworks.""" - -from abc import abstractmethod - -from neural_compressor.utils.utility import singleton - - -@singleton -class TensorflowFilters(object): - """The base filter class for Tensorflow framework.""" - - def __init__(self): - """Initialize the attribute of the class.""" - self.filters = {} - self.filters.update(TENSORFLOW_FILTERS) - - -@singleton -class ONNXRTQLFilters(object): - """The base filter class for ONNXRT framework QLinear mode.""" - - def __init__(self): - """Initialize the attribute of the class.""" - self.filters = {} - self.filters.update(ONNXRT_QL_FILTERS) - - -@singleton -class ONNXRTITFilters(object): - """The base filter class for ONNXRT framework IT mode.""" - - def __init__(self): - """Initialize the attribute of the class.""" - self.filters = {} - self.filters.update(ONNXRT_IT_FILTERS) - - -@singleton -class PyTorchFilters(object): - """The base filter class for PyTorch framework.""" - - def __init__(self): - """Initialize the attribute of the class.""" - self.filters = {} - self.filters.update(PYTORCH_FILTERS) - - -@singleton -class MXNetFilters(object): - """The base filter class for MXNet framework.""" - - def __init__(self): - """Initialize the attribute of the class.""" - self.filters = {} - self.filters.update(MXNET_FILTERS) - - -TENSORFLOW_FILTERS = {} -TENSORFLOW_ITEX_FILTERS = {} -ONNXRT_IT_FILTERS = {} -ONNXRT_QL_FILTERS = {} -PYTORCH_FILTERS = {} -MXNET_FILTERS = {} - -framework_filters = { - "tensorflow": TensorflowFilters, - "tensorflow_itex": TensorflowFilters, - "pytorch": PyTorchFilters, - "pytorch_ipex": PyTorchFilters, - "pytorch_fx": PyTorchFilters, - "mxnet": MXNetFilters, - "onnxrt_qlinearops": ONNXRTQLFilters, - "onnxrt_qdq": ONNXRTQLFilters, - "onnxruntime": ONNXRTQLFilters, - "onnxrt_integerops": ONNXRTITFilters, -} - -registry_filters = { - "tensorflow": TENSORFLOW_FILTERS, - "tensorflow_itex": TENSORFLOW_ITEX_FILTERS, - "pytorch": PYTORCH_FILTERS, - "pytorch_ipex": PYTORCH_FILTERS, - "pytorch_fx": PYTORCH_FILTERS, - "mxnet": MXNET_FILTERS, - "onnxrt_integerops": ONNXRT_IT_FILTERS, - "onnxrt_qdq": ONNXRT_QL_FILTERS, - "onnxruntime": ONNXRT_QL_FILTERS, - "onnxrt_qlinearops": ONNXRT_QL_FILTERS, -} - - -class FILTERS(object): - """The filter register for all frameworks. - - Args: - framework (str): frameworks in ["tensorflow", "tensorflow_itex", "mxnet", - "onnxrt_qdq", "pytorch", "pytorch_ipex", - "pytorch_fx", "onnxrt_integerops", "keras" - "onnxrt_qlinearops", "onnxruntime"]. - """ - - def __init__(self, framework): - """Initialize the attribute of class.""" - assert framework in [ - "tensorflow", - "tensorflow_itex", - "keras", - "mxnet", - "onnxrt_qdq", - "pytorch", - "pytorch_ipex", - "pytorch_fx", - "onnxrt_integerops", - "onnxrt_qlinearops", - "onnxruntime", - ], "framework support tensorflow pytorch mxnet onnxrt" - self.filters = framework_filters[framework]().filters - self.framework = framework - - def __getitem__(self, filter_type): - """Magic method. - - x[i] is roughly equivalent to type(x).__getitem__(x, index) - """ - assert filter_type in self.filters.keys(), "filter support {}".format(self.filters.keys()) - return self.filters[filter_type] - - -def filter_registry(filter_type, framework): - """Register all transform subclasses. - - Args: - filter_type (str): fILTER registration name. - framework (str): support 4 framework including 'tensorflow', 'pytorch', 'mxnet', 'onnxrt'. - cls (class): The class of register. - - Returns: - cls: The class of register. - """ - - def decorator_transform(cls): - """Decorate a class.""" - for single_framework in [fwk.strip() for fwk in framework.split(",")]: - assert single_framework in [ - "tensorflow", - "tensorflow_itex", - "pytorch", - "pytorch_ipex", - "pytorch_fx", - "mxnet", - "onnxrt_integerops", - "onnxrt_qdq", - "onnxrt_qlinearops", - "onnxruntime", - ], "The framework support tensorflow mxnet pytorch onnxrt" - if filter_type in registry_filters[single_framework].keys(): - raise ValueError("Cannot have two transforms with the same name") - registry_filters[single_framework][filter_type] = cls - return cls - - return decorator_transform - - -class Filter(object): - """The base class for transform. - - __call__ method is needed when write user specific transform. - """ - - @abstractmethod - def __call__(self, *args, **kwargs): - """Execute the filter.""" - raise NotImplementedError diff --git a/neural_compressor/experimental/data/transforms/__init__.py b/neural_compressor/experimental/data/transforms/__init__.py deleted file mode 100644 index 3154633584f..00000000000 --- a/neural_compressor/experimental/data/transforms/__init__.py +++ /dev/null @@ -1,31 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ============================================================================== -"""Neural Compressor Built-in transforms for multiple framework backends.""" - -from .transform import TRANSFORMS, BaseTransform, transform_registry -from os.path import dirname, basename, isfile, join -import glob - -modules = glob.glob(join(dirname(__file__), "*.py")) - -for f in modules: - if isfile(f) and not f.startswith("__") and not f.endswith("__init__.py"): - __import__(basename(f)[:-3], globals(), locals(), level=1) - - -__all__ = ["TRANSFORMS", "BaseTransform", "transform_registry"] diff --git a/neural_compressor/experimental/data/transforms/imagenet_transform.py b/neural_compressor/experimental/data/transforms/imagenet_transform.py deleted file mode 100644 index c5a1ca25eb4..00000000000 --- a/neural_compressor/experimental/data/transforms/imagenet_transform.py +++ /dev/null @@ -1,464 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# Copyright 2017 The TensorFlow Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ============================================================================== -"""Neural Compressor built-in imagenet transforms.""" - -import numpy as np - -from neural_compressor.utils import logger -from neural_compressor.utils.utility import LazyImport - -from .transform import BaseTransform, transform_registry - -tf = LazyImport("tensorflow") -cv2 = LazyImport("cv2") - - -@transform_registry(transform_type="QuantizedInput", process="preprocess", framework="tensorflow, tensorflow_itex") -class QuantizedInput(BaseTransform): - """Convert the dtype of input to quantize it. - - Args: - dtype(str): desired image dtype, support 'uint8', 'int8' - scale(float, default=None):scaling ratio of each point in image - - Returns: - tuple of processed image and label - """ - - def __init__(self, dtype, scale=None): - """Initialize `QuantizedInput` class.""" - self.dtype_map = {"uint8": tf.uint8, "int8": tf.int8} - assert dtype in self.dtype_map.keys(), "only support cast dtype {}".format(self.dtype_map.keys()) - self.dtype = dtype - self.scale = scale - - def __call__(self, sample): - """Convert the dtype of input.""" - # scale is not know when tuning, in this case this transform - # do nothing, it's only used when scale is set - if self.scale is None: - return sample - image, label = sample - image = image * self.scale - if self.dtype == "uint8": - image = image + 128 - image = tf.dtypes.cast(image, dtype=self.dtype_map[self.dtype]) - return image, label - - -@transform_registry( - transform_type="LabelShift", - process="postprocess", - framework="pytorch, tensorflow, tensorflow_itex,\ - onnxrt_qlinearops, onnxrt_integerops", -) -class LabelShift(BaseTransform): - """Convert label to label - label_shift. - - Args: - label_shift(int, default=0): number of label shift - - Returns: - tuple of processed image and label - """ - - def __init__(self, label_shift=0): - """Initialize `LabelShift` class.""" - self.label_shift = label_shift - - def __call__(self, sample): - """Convert label to label_shift.""" - images, labels = sample - if isinstance(labels, np.ndarray): - labels = labels - self.label_shift - elif isinstance(labels, list): - if isinstance(labels[0], tuple): - labels = [tuple(np.array(label) - self.label_shift) for label in labels] - elif isinstance(labels[0], np.ndarray): - labels = [label - self.label_shift for label in labels] - else: - labels = np.array(labels) - self.label_shift - labels = labels.tolist() - else: - labels = np.array(labels) - self.label_shift - return images, labels - - -class ParseDecodeImagenet: - """Parse features in Example proto. - - Returns: - tuple of parsed image and label - """ - - def __call__(self, sample): - """Parse features in example.""" - # Dense features in Example proto. - feature_map = { - "image/encoded": tf.io.FixedLenFeature([], dtype=tf.string, default_value=""), - "image/class/label": tf.io.FixedLenFeature([1], dtype=tf.int64, default_value=-1), - } - - sparse_float32 = tf.io.VarLenFeature(dtype=tf.float32) - # Sparse features in Example proto. - feature_map.update( - { - k: sparse_float32 - for k in [ - "image/object/bbox/xmin", - "image/object/bbox/ymin", - "image/object/bbox/xmax", - "image/object/bbox/ymax", - ] - } - ) - - features = tf.io.parse_single_example(serialized=sample, features=feature_map) - label = tf.cast(features["image/class/label"], dtype=tf.int32) - image = features["image/encoded"] - image = tf.image.decode_jpeg(image, channels=3, fancy_upscaling=False, dct_method="INTEGER_FAST") - return (image, label) - - -@transform_registry(transform_type="ParseDecodeImagenet", process="preprocess", framework="tensorflow") -class ParseDecodeImagenetTransform(BaseTransform): - """Imagenet decoding will be performed automatically from Neural Compressor v1.4. - - Returns: - sample - """ - - def __call__(self, sample): - """Convert `ParseDecodeImagenetTransform` feature.""" - logger.warning( - "This transform is going to be deprecated, " - "imagenet decoding will be performed automatically from Neural Compressor v1.4." - ) - return sample - - -@transform_registry(transform_type="ResizeCropImagenet", process="preprocess", framework="tensorflow") -class TensorflowResizeCropImagenetTransform(BaseTransform): - """Combination of a series of transforms which is applicable to images in Imagenet. - - Args: - height (int): Height of the result - width (int): Width of the result - random_crop (bool, default=False): whether to random crop - resize_side (int, default=256):desired shape after resize operation - random_flip_left_right (bool, default=False): whether to random flip left and right - mean_value (list, default=[0.0,0.0,0.0]):means for each channel - scale (float, default=1.0):std value - - Returns: - tuple of processed image and label - """ - - def __init__( - self, - height, - width, - random_crop=False, - resize_side=256, - resize_method="bilinear", - random_flip_left_right=False, - mean_value=[0.0, 0.0, 0.0], - scale=1.0, - data_format="channels_last", - subpixels="RGB", - ): - """Initialize `TensorflowResizeCropImagenetTransform` class.""" - self.height = height - self.width = width - self.mean_value = mean_value - self.scale = scale - self.random_crop = random_crop - self.random_flip_left_right = random_flip_left_right - self.resize_side = resize_side - self.resize_method = resize_method - self.data_format = data_format - self.subpixels = subpixels - - # sample is (images, labels) - def __call__(self, sample): - """Convert `TensorflowResizeCropImagenetTransform` feature.""" - image, label = sample - shape = tf.shape(input=image) - - height = ( - tf.cast(shape[0], dtype=tf.float32) - if self.data_format == "channels_last" - else tf.cast(shape[1], dtype=tf.float32) - ) - width = ( - tf.cast(shape[1], dtype=tf.float32) - if self.data_format == "channels_last" - else tf.cast(shape[2], dtype=tf.float32) - ) - scale = tf.cond( - pred=tf.greater(height, width), - true_fn=lambda: self.resize_side / width, - false_fn=lambda: self.resize_side / height, - ) - - scale = tf.cast(scale, dtype=tf.float32) - new_height = tf.cast(tf.math.rint(height * scale), dtype=tf.int32) - new_width = tf.cast(tf.math.rint(width * scale), dtype=tf.int32) - - if self.subpixels == "BGR" and self.data_format == "channels_first": - # 'RGB'->'BGR' - image = tf.cond( - tf.equal(tf.rank(image), 3), - lambda: tf.experimental.numpy.moveaxis(image[::-1, ...], 0, -1), - lambda: tf.experimental.numpy.moveaxis(image[:, ::-1, ...], 1, -1), - ) - elif self.subpixels == "BGR": - # 'RGB'->'BGR' - image = image[..., ::-1] - image = tf.expand_dims(image, 0) - image = tf.image.resize(image, [new_height, new_width], method=self.resize_method) - image = tf.squeeze(image) - shape = tf.shape(input=image) - if self.random_crop: - y0 = tf.random.uniform(shape=[], minval=0, maxval=(shape[0] - self.height + 1), dtype=tf.dtypes.int32) - x0 = tf.random.uniform(shape=[], minval=0, maxval=(shape[1] - self.width + 1), dtype=tf.dtypes.int32) - else: - y0 = (shape[0] - self.height) // 2 - x0 = (shape[1] - self.width) // 2 - - image = tf.image.crop_to_bounding_box(image, y0, x0, self.height, self.width) - image.set_shape([self.height, self.width, 3]) - if self.random_flip_left_right: - image = tf.image.random_flip_left_right(image) - means = tf.broadcast_to(self.mean_value, tf.shape(input=image)) - image = (image - means) * self.scale - return (image, label) - - -@transform_registry(transform_type="BilinearImagenet", process="preprocess", framework="tensorflow") -class BilinearImagenetTransform(BaseTransform): - """Combination of a series of transforms which is applicable to images in Imagenet. - - Args: - height: Height of the result - width:Width of the result - central_fraction(float, default=0.875):fraction of size to crop - mean_value(list, default=[0.0,0.0,0.0]):means for each channel - scale(float, default=1.0):std value - - Returns: - tuple of processed image and label - """ - - def __init__(self, height, width, central_fraction=0.875, mean_value=[0.0, 0.0, 0.0], scale=1.0): - """Initialize `BilinearImagenetTransform` class.""" - self.height = height - self.width = width - self.mean_value = mean_value - self.scale = scale - self.central_fraction = central_fraction - - # sample is (images, labels) - def __call__(self, sample): - """Convert `BilinearImagenetTransform` feature.""" - image, label = sample - if image.dtype is not tf.float32: - image = tf.image.convert_image_dtype(image, dtype=tf.float32) - # Crop the central region of the image containing 87.5% area of the original image. - if self.central_fraction: - image = tf.image.central_crop(image, central_fraction=self.central_fraction) - - if self.height and self.width: - # Resize the image to the specified height and width. - image = tf.expand_dims(image, 0) - image = tf.image.resize(image, [self.height, self.width], method=tf.image.ResizeMethod.BILINEAR) - image = tf.squeeze(image, [0]) - - image = tf.subtract(image, 0.5) - image = tf.multiply(image, 2.0) - means = tf.broadcast_to(self.mean_value, tf.shape(input=image)) - image = (image - means) * self.scale - return (image, label) - - -@transform_registry( - transform_type="BilinearImagenet", process="preprocess", framework="onnxrt_qlinearops, onnxrt_integerops" -) -class OnnxBilinearImagenetTransform(BaseTransform): - """Combination of a series of transforms which is applicable to images in Imagenet. - - Args: - height: Height of the result - width:Width of the result - central_fraction(float, default=0.875):fraction of size to crop - mean_value(list, default=[0.0,0.0,0.0]):means for each channel - scale(float, default=1.0):std value - - Returns: - tuple of processed image and label - """ - - def __init__(self, height, width, central_fraction=0.875, mean_value=[0.0, 0.0, 0.0], scale=1.0): - """Initialize `OnnxBilinearImagenetTransform` class.""" - self.height = height - self.width = width - self.mean_value = mean_value - self.scale = scale - self.central_fraction = central_fraction - - def __call__(self, sample): - """Convert `OnnxBilinearImagenetTransform` feature.""" - image, label = sample - if isinstance(image, np.ndarray): - image = image.astype("float32") / 255.0 - img_shape = image.shape - depth = img_shape[2] - img_hd = float(img_shape[0]) - bbox_h_start = int((img_hd - img_hd * self.central_fraction) / 2) - img_wd = float(img_shape[1]) - bbox_w_start = int((img_wd - img_wd * self.central_fraction) / 2) - - bbox_h_size = img_shape[0] - bbox_h_start * 2 - bbox_w_size = img_shape[1] - bbox_w_start * 2 - - image = image[bbox_h_start : bbox_h_start + bbox_h_size, bbox_w_start : bbox_w_start + bbox_w_size] - - if self.height and self.width: - image = cv2.resize(image, (self.width, self.height), interpolation=cv2.INTER_LINEAR) - - image = np.subtract(image, 0.5) - image = np.multiply(image, 2.0) - means = np.broadcast_to(self.mean_value, image.shape) - image = (image - means) * self.scale - image = image.astype(np.float32) - return (image, label) - - -@transform_registry( - transform_type="ResizeCropImagenet", process="preprocess", framework="onnxrt_qlinearops, onnxrt_integerops" -) -class ONNXResizeCropImagenetTransform(BaseTransform): - """Combination of a series of transforms which is applicable to images in Imagenet. - - Args: - height: Height of the result - width:Width of the result - central_fraction(float, default=0.875):fraction of size to crop - mean_value(list, default=[0.0,0.0,0.0]):means for each channel - scale(float, default=1.0):std value - - Returns: - tuple of processed image and label - """ - - def __init__( - self, - height, - width, - random_crop=False, - resize_side=256, - mean_value=[0.0, 0.0, 0.0], - std_value=[0.229, 0.224, 0.225], - resize_method="bilinear", - data_format="channels_last", - subpixels="RGB", - ): - """Initialize `ONNXResizeCropImagenetTransform` class.""" - self.height = height - self.width = width - self.mean_value = mean_value - self.std_value = std_value - self.random_crop = random_crop - self.resize_side = resize_side - self.resize_method = resize_method - self.data_format = data_format - self.subpixels = subpixels - - # sample is (images, labels) - def __call__(self, sample): - """Convert `ONNXResizeCropImagenetTransform` feature.""" - # TODO Support optional resize_method, data_format, subpixels for ONNX - image, label = sample - height, width = image.shape[0], image.shape[1] - scale = self.resize_side / width if height > width else self.resize_side / height - new_height = int(height * scale) - new_width = int(width * scale) - image = cv2.resize(image, (new_height, new_width)) - image = image / 255.0 - shape = image.shape - if self.random_crop: - y0 = np.random.randint(low=0, high=(shape[0] - self.height + 1)) - x0 = np.random.randint(low=0, high=(shape[1] - self.width + 1)) - else: - y0 = (shape[0] - self.height) // 2 - x0 = (shape[1] - self.width) // 2 - if len(image.shape) == 2: - image = np.array([image]) - image = np.repeat(image, 3, axis=0) - image = image.transpose(1, 2, 0) - image = image[y0 : y0 + self.height, x0 : x0 + self.width, :] - image = ((image - self.mean_value) / self.std_value).astype(np.float32) - return (image.transpose(2, 0, 1), label) - - -@transform_registry( - transform_type="ResizeWithAspectRatio", process="preprocess", framework="onnxrt_qlinearops, onnxrt_integerops" -) -class ResizeWithAspectRatio(BaseTransform): - """Resize the image with aspect ratio. - - Returns: - image and label - """ - - def __init__(self, height, width, scale=87.5, inter_pol=cv2.INTER_AREA): - """Initialize `ResizeWithAspectRatio` class.""" - self.height = height - self.width = width - self.scale = scale - self.inter_pol = inter_pol - - def __call__(self, sample): - """Convert `ResizeWithAspectRatio` feature.""" - (img, label) = sample - assert len(img.shape) == 3 - height, width, _ = img.shape - new_height = int(100.0 * self.height / self.scale) - new_width = int(100.0 * self.width / self.scale) - if height > width: - w = new_width - h = int(new_height * height / width) - else: - h = new_height - w = int(new_width * width / height) - img = cv2.resize(img, (w, h), interpolation=self.inter_pol) - return img, label diff --git a/neural_compressor/experimental/data/transforms/tokenization.py b/neural_compressor/experimental/data/transforms/tokenization.py deleted file mode 100644 index 38b2c5161a2..00000000000 --- a/neural_compressor/experimental/data/transforms/tokenization.py +++ /dev/null @@ -1,352 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# coding=utf-8 -# Copyright 2018 The Google AI Language Team Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ============================================================================== -"""Tokenization helper classes.""" - -from __future__ import absolute_import, division, print_function - -import collections -import re -import unicodedata - -import six - -from neural_compressor.utils.utility import LazyImport - -tf = LazyImport("tensorflow") - - -def convert_to_unicode(text): # pragma: no cover - """Convert `text` to Unicode (if it's not already), assuming utf-8 input.""" - if six.PY3: - if isinstance(text, str): - return text - elif isinstance(text, bytes): - return text.decode("utf-8", "ignore") - else: - raise ValueError("Unsupported string type: %s" % (type(text))) - elif six.PY2: - if isinstance(text, str): - return text.decode("utf-8", "ignore") - elif isinstance(text, unicode): # pylint: disable=undefined-variable # noqa: F821 - return text - else: - raise ValueError("Unsupported string type: %s" % (type(text))) - else: - raise ValueError("Not running on Python2 or Python 3?") - - -def load_vocab(vocab_file): - """Load a vocabulary file into a dictionary.""" - vocab = collections.OrderedDict() - index = 0 - with tf.io.gfile.GFile(vocab_file, "r") as reader: - while True: - token = convert_to_unicode(reader.readline()) - if not token: - break - token = token.strip() - vocab[token] = index - index += 1 - return vocab - - -def convert_by_vocab(vocab, items): - """Convert a sequence of [tokens|ids] using the vocab.""" - output = [] - for item in items: - output.append(vocab[item]) - return output - - -def whitespace_tokenize(text): - """Run basic whitespace cleaning and splitting on a piece of text.""" - text = text.strip() - if not text: - return [] - tokens = text.split() - return tokens - - -class FullTokenizer(object): - """Run end-to-end tokenziation.""" - - def __init__(self, vocab_file, do_lower_case=True): - """Construct a FullTokenizer. - - Args: - vocab_file: vocab file. - do_lower_case: Whether to lower case the input. - """ - self.vocab = load_vocab(vocab_file) - self.inv_vocab = {v: k for k, v in self.vocab.items()} - self.basic_tokenizer = BasicTokenizer(do_lower_case=do_lower_case) - self.wordpiece_tokenizer = WordpieceTokenizer(vocab=self.vocab) - - def tokenize(self, text): - """Tokenize text.""" - split_tokens = [] - for token in self.basic_tokenizer.tokenize(text): - for sub_token in self.wordpiece_tokenizer.tokenize(token): - split_tokens.append(sub_token) - - return split_tokens - - def convert_tokens_to_ids(self, tokens): - """Convert tokens to ids.""" - return convert_by_vocab(self.vocab, tokens) - - def convert_ids_to_tokens(self, ids): - """Convert ids to tokens.""" - return convert_by_vocab(self.inv_vocab, ids) - - -class BasicTokenizer(object): - """Run basic tokenization (punctuation splitting, lower casing, etc.).""" - - def __init__(self, do_lower_case=True): - """Construct a BasicTokenizer. - - Args: - do_lower_case: Whether to lower case the input. - """ - self.do_lower_case = do_lower_case - - def tokenize(self, text): - """Tokenizes a piece of text.""" - text = convert_to_unicode(text) - text = self._clean_text(text) - - # This was added on November 1st, 2018 for the multilingual and Chinese - # models. This is also applied to the English models now, but it doesn't - # matter since the English models were not trained on any Chinese data - # and generally don't have any Chinese data in them (there are Chinese - # characters in the vocabulary because Wikipedia does have some Chinese - # words in the English Wikipedia.). - text = self._tokenize_chinese_chars(text) - - orig_tokens = whitespace_tokenize(text) - split_tokens = [] - for token in orig_tokens: - if self.do_lower_case: - token = token.lower() - token = self._run_strip_accents(token) - split_tokens.extend(self._run_split_on_punc(token)) - - output_tokens = whitespace_tokenize(" ".join(split_tokens)) - return output_tokens - - def _run_strip_accents(self, text): - """Strip accents from a piece of text.""" - text = unicodedata.normalize("NFD", text) - output = [] - for char in text: - cat = unicodedata.category(char) - if cat == "Mn": - continue - output.append(char) - return "".join(output) - - def _run_split_on_punc(self, text): - """Split punctuation on a piece of text.""" - chars = list(text) - i = 0 - start_new_word = True - output = [] - while i < len(chars): - char = chars[i] - if _is_punctuation(char): - output.append([char]) - start_new_word = True - else: - if start_new_word: - output.append([]) - start_new_word = False - output[-1].append(char) - i += 1 - - return ["".join(x) for x in output] - - def _tokenize_chinese_chars(self, text): - """Add whitespace around any CJK character.""" - output = [] - for char in text: - cp = ord(char) - if self._is_chinese_char(cp): # pragma: no cover - output.append(" ") - output.append(char) - output.append(" ") - else: - output.append(char) - return "".join(output) - - def _is_chinese_char(self, cp): - """Check whether CP is the codepoint of a CJK character.""" - # This defines a "chinese character" as anything in the CJK Unicode block: - # https://en.wikipedia.org/wiki/CJK_Unified_Ideographs_(Unicode_block) - # - # Note that the CJK Unicode block is NOT all Japanese and Korean characters, - # despite its name. The modern Korean Hangul alphabet is a different block, - # as is Japanese Hiragana and Katakana. Those alphabets are used to write - # space-separated words, so they are not treated specially and handled - # like the all of the other languages. - if ( - (cp >= 0x4E00 and cp <= 0x9FFF) - or (cp >= 0x3400 and cp <= 0x4DBF) # - or (cp >= 0x20000 and cp <= 0x2A6DF) # - or (cp >= 0x2A700 and cp <= 0x2B73F) # - or (cp >= 0x2B740 and cp <= 0x2B81F) # - or (cp >= 0x2B820 and cp <= 0x2CEAF) # - or (cp >= 0xF900 and cp <= 0xFAFF) - or (cp >= 0x2F800 and cp <= 0x2FA1F) # - ): # - return True - - return False - - def _clean_text(self, text): - """Perform invalid character removal and whitespace cleanup on text.""" - output = [] - for char in text: - cp = ord(char) - if cp == 0 or cp == 0xFFFD or _is_control(char): - continue - if _is_whitespace(char): - output.append(" ") - else: - output.append(char) - return "".join(output) - - -class WordpieceTokenizer(object): - """Run WordPiece tokenziation.""" - - def __init__(self, vocab, unk_token="[UNK]", max_input_chars_per_word=200): - """Construct a WordpieceTokenizer. - - Args: - vocab: the given vocabulary. - unk_token: unknown token. - max_input_chars_per_word: max input chars number in any word. - """ - self.vocab = vocab - self.unk_token = unk_token - self.max_input_chars_per_word = max_input_chars_per_word - - def tokenize(self, text): - """Tokenize a piece of text into its word pieces. - - This uses a greedy longest-match-first algorithm to perform tokenization - using the given vocabulary. - For example: - input = "unaffable" - output = ["un", "##aff", "##able"] - Args: - text: A single token or whitespace separated tokens. This should have - already been passed through `BasicTokenizer. - - Returns: - A list of wordpiece tokens. - """ - text = convert_to_unicode(text) - - output_tokens = [] - for token in whitespace_tokenize(text): - chars = list(token) - if len(chars) > self.max_input_chars_per_word: # pragma: no cover - output_tokens.append(self.unk_token) - continue - - is_bad = False - start = 0 - sub_tokens = [] - while start < len(chars): - end = len(chars) - cur_substr = None - while start < end: - substr = "".join(chars[start:end]) - if start > 0: - substr = "##" + substr - if substr in self.vocab: - cur_substr = substr - break - end -= 1 - if cur_substr is None: - is_bad = True - break - sub_tokens.append(cur_substr) - start = end - - if is_bad: - output_tokens.append(self.unk_token) - else: - output_tokens.extend(sub_tokens) - return output_tokens - - -def _is_whitespace(char): - """Check whether `chars` is a whitespace character.""" - # \t, \n, and \r are technically control characters but we treat them - # as whitespace since they are generally considered as such. - if char == " " or char == "\t" or char == "\n" or char == "\r": - return True - cat = unicodedata.category(char) - if cat == "Zs": # pragma: no cover - return True - return False - - -def _is_control(char): # pragma: no cover - """Check whether `chars` is a control character.""" - # These are technically control characters but we count them as whitespace - # characters. - if char == "\t" or char == "\n" or char == "\r": - return False - cat = unicodedata.category(char) - if cat in ("Cc", "Cf"): - return True - return False - - -def _is_punctuation(char): # pragma: no cover - """Check whether `chars` is a punctuation character.""" - cp = ord(char) - # We treat all non-letter/number ASCII as punctuation. - # Characters such as "^", "$", and "`" are not in the Unicode - # Punctuation class but we treat them as punctuation anyways, for - # consistency. - if (cp >= 33 and cp <= 47) or (cp >= 58 and cp <= 64) or (cp >= 91 and cp <= 96) or (cp >= 123 and cp <= 126): - return True - cat = unicodedata.category(char) - if cat.startswith("P"): - return True - return False diff --git a/neural_compressor/experimental/data/transforms/transform.py b/neural_compressor/experimental/data/transforms/transform.py deleted file mode 100644 index 3bda7ed5ab7..00000000000 --- a/neural_compressor/experimental/data/transforms/transform.py +++ /dev/null @@ -1,2865 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ============================================================================== -"""Neural Compressor built-in Transforms on multiple framework backends.""" - -import collections -from abc import abstractmethod - -import numpy as np - -from neural_compressor.utils import logger -from neural_compressor.utils.utility import LazyImport, singleton - -torchvision = LazyImport("torchvision") -torch = LazyImport("torch") -tf = LazyImport("tensorflow") -mx = LazyImport("mxnet") -cv2 = LazyImport("cv2") - - -class Transforms(object): - """INC supports built-in preprocessing, postprocessing and general methods on different framework backends. - - Transforms base class provides the abstract methods. - Users can also register their own Transforms classes by inheriting this base class. - """ - - def __init__(self, process, concat_general=True): - """Initialize `Transforms` class. - - Args: - process (str): processing type, the value can be preprocess, postprocess or general - concat_general (Boolean): users can use general transform in both preprocess - or postprocess if set True - """ - transform_map = { - "preprocess": self._get_preprocess, - "postprocess": self._get_postprocess, - "general": self._get_general, - } - self.transforms = transform_map[process]() - if concat_general: - self.transforms.update(transform_map["general"]()) - - @abstractmethod - def _get_preprocess(self): - """Abstract method to get preprocessing method.""" - raise NotImplementedError - - @abstractmethod - def _get_postprocess(self): - """Abstract method to get postprocess method.""" - raise NotImplementedError - - @abstractmethod - def _get_general(self): - """Abstract method to get general method.""" - raise NotImplementedError - - -class TensorflowTransforms(Transforms): - """Tensorflow Transforms subclass.""" - - def _get_preprocess(self): - """Tensorflow get preprocess method. - - Returns: - preprocess: a dict including all the registered preprocess methods - """ - preprocess = { - "DecodeImage": TensorflowWrapFunction(tf.io.decode_jpeg), - "EncodeJpeg": TensorflowWrapFunction(tf.io.encode_jpeg), - } - # update the registry transforms - preprocess.update(TENSORFLOW_TRANSFORMS["preprocess"]) - return preprocess - - def _get_postprocess(self): - """Tensorflow get postprocess method. - - Returns: - postprocess: a dict including all the registered postprocess methods - """ - postprocess = {} - postprocess.update(TENSORFLOW_TRANSFORMS["postprocess"]) - return postprocess - - def _get_general(self): - """Tensorflow get general method. - - Returns: - general: a dict including all the registered general methods - """ - general = {} - general.update(TENSORFLOW_TRANSFORMS["general"]) - return general - - -class MXNetTransforms(Transforms): - """Mxnet Transforms subclass.""" - - def _get_preprocess(self): - """Mxnet get preprocess method. - - Returns: - preprocess: a dict including all the registered preprocess methods - """ - preprocess = { - "ToTensor": PytorchMxnetWrapFunction(mx.gluon.data.vision.transforms.ToTensor), - "CenterCrop": PytorchMxnetWrapFunction(mx.gluon.data.vision.transforms.CenterCrop), - "RandomHorizontalFlip": PytorchMxnetWrapFunction(mx.gluon.data.vision.transforms.RandomFlipLeftRight), - "RandomVerticalFlip": PytorchMxnetWrapFunction(mx.gluon.data.vision.transforms.RandomFlipTopBottom), - } - preprocess.update(MXNET_TRANSFORMS["preprocess"]) - return preprocess - - def _get_postprocess(self): - """Mxnet get postprocess method. - - Returns: - postprocess: a dict including all the registered postprocess methods - """ - postprocess = {} - postprocess.update(MXNET_TRANSFORMS["postprocess"]) - return postprocess - - def _get_general(self): - """Mxnet get general method. - - Returns: - general: a dict including all the registered general methods - """ - general = { - "Compose": mx.gluon.data.vision.transforms.Compose, - "Cast": PytorchMxnetWrapFunction(mx.gluon.data.vision.transforms.Cast), - } - general.update(MXNET_TRANSFORMS["general"]) - return general - - -class PyTorchTransforms(Transforms): - """Pytorch Transforms subclass.""" - - def _get_preprocess(self): - """Pytorch get preprocessing method. - - Returns: - preprocess: a dict including all the registered preprocess methods - """ - preprocess = { - "ToTensor": PytorchMxnetWrapFunction(torchvision.transforms.ToTensor), - "ToPILImage": PytorchMxnetWrapFunction(torchvision.transforms.ToPILImage), - "CenterCrop": PytorchMxnetWrapFunction(torchvision.transforms.CenterCrop), - "RandomCrop": PytorchMxnetWrapFunction(torchvision.transforms.RandomCrop), - "RandomHorizontalFlip": PytorchMxnetWrapFunction(torchvision.transforms.RandomHorizontalFlip), - "RandomVerticalFlip": PytorchMxnetWrapFunction(torchvision.transforms.RandomVerticalFlip), - "Pad": PytorchMxnetWrapFunction(torchvision.transforms.Pad), - "ColorJitter": PytorchMxnetWrapFunction(torchvision.transforms.ColorJitter), - } - preprocess.update(PYTORCH_TRANSFORMS["preprocess"]) - return preprocess - - def _get_postprocess(self): - """Pytorch get postprocess method. - - Returns: - postprocess: a dict including all the registered postprocess methods - """ - postprocess = {} - postprocess.update(PYTORCH_TRANSFORMS["postprocess"]) - return postprocess - - def _get_general(self): - """Pytorch get general method. - - Returns: - general: a dict including all the registered general methods - """ - general = { - "Compose": torchvision.transforms.Compose, - } - general.update(PYTORCH_TRANSFORMS["general"]) - return general - - -class ONNXRTQLTransforms(Transforms): - """Onnxrt_qlinearops Transforms subclass.""" - - def _get_preprocess(self): - """Onnxrt_qlinearops get preprocessing method. - - Returns: - preprocess: a dict including all the registered preprocess methods - """ - preprocess = {} - preprocess.update(ONNXRT_QL_TRANSFORMS["preprocess"]) - return preprocess - - def _get_postprocess(self): - """Onnxrt_qlinearops get postprocess method. - - Returns: - postprocess: a dict including all the registered postprocess methods - """ - postprocess = {} - postprocess.update(ONNXRT_QL_TRANSFORMS["postprocess"]) - return postprocess - - def _get_general(self): - """Onnxrt_qlinearops get general method. - - Returns: - general: a dict including all the registered general methods - """ - general = {} - general.update(ONNXRT_QL_TRANSFORMS["general"]) - return general - - -class ONNXRTITTransforms(Transforms): - """Onnxrt_integerops Transforms subclass.""" - - def _get_preprocess(self): - """Onnxrt_integerops get preprocessing method. - - Returns: - preprocess: a dict including all the registered preprocess methods - """ - preprocess = {} - preprocess.update(ONNXRT_IT_TRANSFORMS["preprocess"]) - return preprocess - - def _get_postprocess(self): - """Onnxrt_integerops get postprocess method. - - Returns: - postprocess: a dict including all the registered postprocess methods - """ - postprocess = {} - postprocess.update(ONNXRT_IT_TRANSFORMS["postprocess"]) - return postprocess - - def _get_general(self): - """Onnxrt_integerops get general method. - - Returns: - general: a dict including all the registered general methods - """ - general = {} - general.update(ONNXRT_IT_TRANSFORMS["general"]) - return general - - -framework_transforms = { - "tensorflow": TensorflowTransforms, - "tensorflow_itex": TensorflowTransforms, - "mxnet": MXNetTransforms, - "pytorch": PyTorchTransforms, - "pytorch_ipex": PyTorchTransforms, - "pytorch_fx": PyTorchTransforms, - "onnxrt_qlinearops": ONNXRTQLTransforms, - "onnxrt_integerops": ONNXRTITTransforms, - "onnxruntime": ONNXRTQLTransforms, - "onnxrt_qdq": ONNXRTQLTransforms, -} - -# transform registry will register transforms into these dicts -TENSORFLOW_TRANSFORMS = {"preprocess": {}, "postprocess": {}, "general": {}} -TENSORFLOW_ITEX_TRANSFORMS = {"preprocess": {}, "postprocess": {}, "general": {}} -MXNET_TRANSFORMS = {"preprocess": {}, "postprocess": {}, "general": {}} -PYTORCH_TRANSFORMS = {"preprocess": {}, "postprocess": {}, "general": {}} -ONNXRT_QL_TRANSFORMS = {"preprocess": {}, "postprocess": {}, "general": {}} -ONNXRT_IT_TRANSFORMS = {"preprocess": {}, "postprocess": {}, "general": {}} - -registry_transforms = { - "tensorflow": TENSORFLOW_TRANSFORMS, - "tensorflow_itex": TENSORFLOW_ITEX_TRANSFORMS, - "mxnet": MXNET_TRANSFORMS, - "pytorch": PYTORCH_TRANSFORMS, - "pytorch_ipex": PYTORCH_TRANSFORMS, - "pytorch_fx": PYTORCH_TRANSFORMS, - "onnxrt_qlinearops": ONNXRT_QL_TRANSFORMS, - "onnxrt_qdq": ONNXRT_QL_TRANSFORMS, - "onnxruntime": ONNXRT_QL_TRANSFORMS, - "onnxrt_integerops": ONNXRT_IT_TRANSFORMS, -} - - -class TRANSFORMS(object): - """Transforms collection class. - - Provide register method to register new Transforms - and provide __getitem__ method to get Transforms according to Transforms type. - """ - - def __init__(self, framework, process): - """Initialize `TRANSFORMS` class. - - Args: - framework (str): different framework type like tensorflow, pytorch and so on - process (str): process type, the value can be preprocess, postprocess or general - """ - assert framework in ( - "tensorflow", - "tensorflow_itex", - "keras", - "onnxruntime", - "pytorch", - "pytorch_ipex", - "pytorch_fx", - "onnxrt_qdq", - "onnxrt_qlinearops", - "onnxrt_integerops", - "mxnet", - ), "framework support tensorflow pytorch mxnet onnxrt" - assert process in ("preprocess", "postprocess", "general"), "process support preprocess postprocess, general" - self.transforms = framework_transforms[framework](process).transforms - self.framework = framework - self.process = process - - def __getitem__(self, transform_type): - """Get Transform according to Transforms type. - - Args: - transform_type (str): the value can be preprocess, postprocess or general - - Returns: - Transforms: the registered Transforms - """ - assert transform_type in self.transforms.keys(), "transform support {}".format(self.transforms.keys()) - return self.transforms[transform_type] - - def register(self, name, transform_cls): - """Register new Transform according to Transforms type. - - Args: - name (str): process name - transform_cls (class): process function wrapper class - """ - assert ( - name not in registry_transforms[self.framework][self.process].keys() - ), "register transform name already exists." - registry_transforms[self.framework][self.process].update({name: transform_cls}) - - -def transform_registry(transform_type, process, framework): - """Class decorator used to register all transform subclasses. - - Args: - transform_type (str): Transform registration name - process (str): support 3 process including 'preprocess', 'postprocess', 'general' - framework (str): support 4 framework including 'tensorflow', 'pytorch', 'mxnet', 'onnxrt' - cls (class): The class of register. - - Returns: - cls: The class of register. - """ - - def decorator_transform(cls): - for single_framework in [fwk.strip() for fwk in framework.split(",")]: - assert single_framework in [ - "tensorflow", - "tensorflow_itex", - "mxnet", - "pytorch", - "pytorch_ipex", - "pytorch_fx", - "onnxrt_qlinearops", - "onnxrt_qdq", - "onnxrt_integerops", - "onnxruntime", - ], "The framework support tensorflow mxnet pytorch onnxrt" - if transform_type in registry_transforms[single_framework][process].keys(): - raise ValueError("Cannot have two transforms with the same name") - registry_transforms[single_framework][process][transform_type] = cls - return cls - - return decorator_transform - - -class BaseTransform(object): - """The base class for transform.""" - - @abstractmethod - def __call__(self, *args, **kwargs): - """__call__ method is needed when write user specific transform.""" - raise NotImplementedError - - -class TensorflowWrapFunction(object): - """Tensorflow wrapper function class.""" - - def __init__(self, transform_func): - """Initialize `TensorflowWrapFunction` class. - - Args: - transform_func (function): tensorflow transform function - """ - self.transform_func = transform_func - - def __call__(self, **kwargs): - """__call__ method. - - Returns: - TensorflowTransform class - """ - return TensorflowTransform(self.transform_func, **kwargs) - - -class TensorflowTransform(BaseTransform): - """Tensorflow transform class, the subclass of BaseTransform.""" - - def __init__(self, transform_func, **kwargs): - """Initialize `TensorflowTransform` class. - - Args: - transform_func (function): tensorflow transform function - """ - self.kwargs = kwargs - self.transform_func = transform_func - - def __call__(self, sample): - """__call__ method. - - Returns: - a tuple of image and label which get from tensorflow transform processing - """ - image, label = sample - image = self.transform_func(image, **self.kwargs) - return (image, label) - - -class PytorchMxnetWrapFunction(object): - """Pytorch and MXNet wrapper function class.""" - - def __init__(self, transform_func): - """Initialize `PytorchMxnetWrapFunction` class. - - Args: - transform_func (function): pytorch or mxnet transform function - """ - self.transform_func = transform_func - - def __call__(self, **args): - """__call__ method. - - Returns: - PytorchMxnetTransform class - """ - return PytorchMxnetTransform(self.transform_func(**args)) - - -class PytorchMxnetTransform(BaseTransform): - """Pytorch and Mxnet transform class, the subclass of BaseTransform.""" - - def __init__(self, transform_func): - """Initialize `PytorchMxnetTransform` class. - - Args: - transform_func (function): pytorch or mxnet transform function - """ - self.transform_func = transform_func - - def __call__(self, sample): - """__call__ method. - - Returns: - a tuple of image and label which get from pytorch or mxnet transform processing - """ - image, label = sample - image = self.transform_func(image) - return (image, label) - - -interpolation_map = { - "nearest": cv2.INTER_NEAREST, - "bilinear": cv2.INTER_LINEAR, - "bicubic": cv2.INTER_CUBIC, -} - -interpolation_pytorch_map = { - "nearest": 0, - "bilinear": 2, - "bicubic": 3, -} - -interpolation_mxnet_map = { - "nearest": 0, - "bilinear": 1, - "bicubic": 2, -} - - -def get_torchvision_map(interpolation): - """Get torchvision interpolation map.""" - try: - from torchvision.transforms.functional import InterpolationMode - - interpolation_torchvision_map = { - 0: InterpolationMode.NEAREST, - 2: InterpolationMode.BILINEAR, - 3: InterpolationMode.BICUBIC, - } - return interpolation_torchvision_map[interpolation] - except: # pragma: no cover - return interpolation - - -@transform_registry( - transform_type="Compose", - process="general", - framework="onnxrt_qlinearops, onnxrt_integerops, tensorflow, \ - tensorflow_itex", -) -class ComposeTransform(BaseTransform): - """Composes several transforms together. - - Args: - transform_list (list of Transform objects): list of transforms to compose - - Returns: - sample (tuple): tuple of processed image and label - """ - - def __init__(self, transform_list): - """Initialize `ComposeTransform` class.""" - self.transform_list = transform_list - - def __call__(self, sample): - """Call transforms in transform_list.""" - for transform in self.transform_list: - sample = transform(sample) - return sample - - -@transform_registry(transform_type="CropToBoundingBox", process="preprocess", framework="pytorch") -class CropToBoundingBox(BaseTransform): - """Crops an image to a specified bounding box. - - Args: - offset_height (int): Vertical coordinate of the top-left corner of the result in the input - offset_width (int): Horizontal coordinate of the top-left corner of the result in the input - target_height (int): Height of the result - target_width (int): Width of the result - - Returns: - tuple of processed image and label - """ - - def __init__(self, offset_height, offset_width, target_height, target_width): - """Initialize `CropToBoundingBox` class.""" - self.offset_height = offset_height - self.offset_width = offset_width - self.target_height = target_height - self.target_width = target_width - - def __call__(self, sample): - """Call torchvision.transforms.functional.crop.""" - image, label = sample - image = torchvision.transforms.functional.crop( - image, self.offset_height, self.offset_width, self.target_height, self.target_width - ) - return (image, label) - - -@transform_registry(transform_type="CropToBoundingBox", process="preprocess", framework="mxnet") -class MXNetCropToBoundingBox(CropToBoundingBox): - """Crops an image to a specified bounding box. - - Args: - offset_height (int): Vertical coordinate of the top-left corner of the result in the input - offset_width (int): Horizontal coordinate of the top-left corner of the result in the input - target_height (int): Height of the result - target_width (int): Width of the result - - Returns: - tuple of processed image and label - """ - - def __call__(self, sample): - """Call mx.image.fixed_crop.""" - image, label = sample - image = mx.image.fixed_crop(image, self.offset_height, self.offset_width, self.target_height, self.target_width) - return (image, label) - - -@transform_registry( - transform_type="CropToBoundingBox", process="preprocess", framework="onnxrt_qlinearops, onnxrt_integerops" -) -class ONNXRTCropToBoundingBox(CropToBoundingBox): - """Crops an image to a specified bounding box. - - Args: - offset_height (int): Vertical coordinate of the top-left corner of the result in the input - offset_width (int): Horizontal coordinate of the top-left corner of the result in the input - target_height (int): Height of the result - target_width (int): Width of the result - - Returns: - tuple of processed image and label - """ - - def __call__(self, sample): - """Crop the image in sample.""" - image, label = sample - image = image[ - self.offset_height : self.offset_height + self.target_height, - self.offset_width : self.offset_width + self.target_width, - :, - ] - return (image, label) - - -@transform_registry(transform_type="CropToBoundingBox", process="preprocess", framework="tensorflow, tensorflow_itex") -class TensorflowCropToBoundingBox(CropToBoundingBox): - """Crops an image to a specified bounding box. - - Args: - offset_height (int): Vertical coordinate of the top-left corner of the result in the input - offset_width (int): Horizontal coordinate of the top-left corner of the result in the input - target_height (int): Height of the result - target_width (int): Width of the result - - Returns: - tuple of processed image and label - """ - - def __call__(self, sample): - """Crop the image in sample.""" - image, label = sample - if isinstance(image, tf.Tensor): - image = tf.image.crop_to_bounding_box( - image, self.offset_height, self.offset_width, self.target_height, self.target_width - ) - else: - image = image[ - self.offset_height : self.offset_height + self.target_height, - self.offset_width : self.offset_width + self.target_width, - :, - ] - return (image, label) - - -@transform_registry( - transform_type="ResizeWithRatio", - process="preprocess", - framework="onnxrt_qlinearops, onnxrt_integerops, pytorch, mxnet", -) -class ResizeWithRatio(BaseTransform): - """Resize image with aspect ratio and pad it to max shape(optional). - - If the image is padded, the label will be processed at the same time. - The input image should be np.array. - - Args: - min_dim (int, default=800): - Resizes the image such that its smaller dimension == min_dim - max_dim (int, default=1365): - Ensures that the image longest side doesn't exceed this value - padding (bool, default=False): - If true, pads image with zeros so its size is max_dim x max_dim - - Returns: - tuple of processed image and label - """ - - def __init__(self, min_dim=800, max_dim=1365, padding=False, constant_value=0): - """Initialize `ResizeWithRatio` class.""" - self.min_dim = min_dim - self.max_dim = max_dim - self.padding = padding - self.constant_value = constant_value - - def __call__(self, sample): - """Resize the image with ratio in sample.""" - image, label = sample - height, width = image.shape[:2] - scale = 1 - if self.min_dim: - scale = max(1, self.min_dim / min(height, width)) - if self.max_dim: - image_max = max(height, width) - if round(image_max * scale) > self.max_dim: - scale = self.max_dim / image_max - if scale != 1: - image = cv2.resize(image, (round(height * scale), round(width * scale))) - - bbox, str_label, int_label, image_id = label - - if self.padding: - h, w = image.shape[:2] - pad_param = [ - [(self.max_dim - h) // 2, self.max_dim - h - (self.max_dim - h) // 2], - [(self.max_dim - w) // 2, self.max_dim - w - (self.max_dim - w) // 2], - [0, 0], - ] - if not isinstance(bbox, np.ndarray): - bbox = np.array(bbox) - resized_box = bbox * [height, width, height, width] * scale - moved_box = resized_box + [ - (self.max_dim - h) // 2, - (self.max_dim - w) // 2, - (self.max_dim - h) // 2, - (self.max_dim - w) // 2, - ] - bbox = moved_box / [self.max_dim, self.max_dim, self.max_dim, self.max_dim] - image = np.pad(image, pad_param, mode="constant", constant_values=self.constant_value) - return image, (bbox, str_label, int_label, image_id) - - -@transform_registry(transform_type="ResizeWithRatio", process="preprocess", framework="tensorflow, tensorflow_itex") -class TensorflowResizeWithRatio(BaseTransform): - """Resize image with aspect ratio and pad it to max shape(optional). - - If the image is padded, the label will be processed at the same time. - The input image should be np.array or tf.Tensor. - - Args: - min_dim (int, default=800): - Resizes the image such that its smaller dimension == min_dim - max_dim (int, default=1365): - Ensures that the image longest side doesn't exceed this value - padding (bool, default=False): - If true, pads image with zeros so its size is max_dim x max_dim - - Returns: - tuple of processed image and label - """ - - def __init__(self, min_dim=800, max_dim=1365, padding=False, constant_value=0): - """Initialize `TensorflowResizeWithRatio` class.""" - self.min_dim = min_dim - self.max_dim = max_dim - self.padding = padding - self.constant_value = constant_value - - def __call__(self, sample): - """Resize the image with ratio in sample.""" - image, label = sample - if isinstance(image, tf.Tensor): - shape = tf.shape(input=image) - height = tf.cast(shape[0], dtype=tf.float32) - width = tf.cast(shape[1], dtype=tf.float32) - scale = 1 - if self.min_dim: - scale = tf.maximum(1.0, tf.cast(self.min_dim / tf.math.minimum(height, width), dtype=tf.float32)) - if self.max_dim: - image_max = tf.cast(tf.maximum(height, width), dtype=tf.float32) - scale = tf.cond( - pred=tf.greater(tf.math.round(image_max * scale), self.max_dim), - true_fn=lambda: self.max_dim / image_max, - false_fn=lambda: scale, - ) - image = tf.image.resize(image, (tf.math.round(height * scale), tf.math.round(width * scale))) - bbox, str_label, int_label, image_id = label - - if self.padding: - shape = tf.shape(input=image) - h = tf.cast(shape[0], dtype=tf.float32) - w = tf.cast(shape[1], dtype=tf.float32) - pad_param = [ - [(self.max_dim - h) // 2, self.max_dim - h - (self.max_dim - h) // 2], - [(self.max_dim - w) // 2, self.max_dim - w - (self.max_dim - w) // 2], - [0, 0], - ] - resized_box = bbox * [height, width, height, width] * scale - moved_box = resized_box + [ - (self.max_dim - h) // 2, - (self.max_dim - w) // 2, - (self.max_dim - h) // 2, - (self.max_dim - w) // 2, - ] - bbox = moved_box / [self.max_dim, self.max_dim, self.max_dim, self.max_dim] - image = tf.pad(image, pad_param, constant_values=self.constant_value) - else: - transform = ResizeWithRatio(self.min_dim, self.max_dim, self.padding) - image, (bbox, str_label, int_label, image_id) = transform(sample) - return image, (bbox, str_label, int_label, image_id) - - -@transform_registry(transform_type="Transpose", process="preprocess", framework="onnxrt_qlinearops, onnxrt_integerops") -class Transpose(BaseTransform): - """Transpose image according to perm. - - Args: - perm (list): A permutation of the dimensions of input image - - Returns: - tuple of processed image and label - """ - - def __init__(self, perm): - """Initialize `Transpose` class.""" - self.perm = perm - - def __call__(self, sample): - """Transpose the image according to perm in sample.""" - image, label = sample - assert len(image.shape) == len(self.perm), "Image rank doesn't match Perm rank" - image = np.transpose(image, axes=self.perm) - return (image, label) - - -@transform_registry(transform_type="Transpose", process="preprocess", framework="tensorflow, tensorflow_itex") -class TensorflowTranspose(Transpose): - """Transpose image according to perm. - - Args: - perm (list): A permutation of the dimensions of input image - - Returns: - tuple of processed image and label - """ - - def __call__(self, sample): - """Transpose the image according to perm in sample.""" - image, label = sample - assert len(image.shape) == len(self.perm), "Image rank doesn't match Perm rank" - if isinstance(image, tf.Tensor): - image = tf.transpose(image, perm=self.perm) - else: - image = np.transpose(image, axes=self.perm) - return (image, label) - - -@transform_registry(transform_type="Transpose", process="preprocess", framework="mxnet") -class MXNetTranspose(Transpose): - """Transpose image according to perm. - - Args: - perm (list): A permutation of the dimensions of input image - - Returns: - tuple of processed image and label - """ - - def __call__(self, sample): - """Transpose the image according to perm in sample.""" - image, label = sample - assert len(image.shape) == len(self.perm), "Image rank doesn't match Perm rank" - image = mx.ndarray.transpose(image, self.perm) - return (image, label) - - -@transform_registry(transform_type="Transpose", process="preprocess", framework="pytorch") -class PyTorchTranspose(Transpose): - """Transpose image according to perm. - - Args: - perm (list): A permutation of the dimensions of input image - - Returns: - tuple of processed image and label - """ - - def __call__(self, sample): - """Transpose the image according to perm in sample.""" - image, label = sample - assert len(image.shape) == len(self.perm), "Image rank doesn't match Perm rank" - image = image.permute(self.perm) - return (image, label) - - -@transform_registry( - transform_type="RandomVerticalFlip", process="preprocess", framework="onnxrt_qlinearops, onnxrt_integerops" -) -class RandomVerticalFlip(BaseTransform): - """Vertically flip the given image randomly. - - Returns: - tuple of processed image and label - """ - - def __call__(self, sample): - """Vertically flip the image in sample.""" - image, label = sample - if np.random.rand(1)[0] > 0.5: - image = np.flipud(image) - return (image, label) - - -@transform_registry(transform_type="RandomVerticalFlip", process="preprocess", framework="tensorflow, tensorflow_itex") -class TensorflowRandomVerticalFlip(BaseTransform): - """Vertically flip the given image randomly. - - Returns: - tuple of processed image and label - """ - - def __call__(self, sample): - """Vertically flip the image in sample.""" - image, label = sample - if isinstance(image, tf.Tensor): - image = tf.image.random_flip_up_down(image) - else: - if np.random.rand(1)[0] > 0.5: - image = np.flipud(image) - return (image, label) - - -@transform_registry( - transform_type="RandomHorizontalFlip", process="preprocess", framework="onnxrt_qlinearops, onnxrt_integerops" -) -class RandomHorizontalFlip(BaseTransform): - """Horizontally flip the given image randomly. - - Returns: - tuple of processed image and label - """ - - def __call__(self, sample): - """Horizontally flip the image in sample.""" - image, label = sample - if np.random.rand(1)[0] > 0.5: - image = np.fliplr(image) - return (image, label) - - -@transform_registry( - transform_type="RandomHorizontalFlip", process="preprocess", framework="tensorflow, tensorflow_itex" -) -class TensorflowRandomHorizontalFlip(BaseTransform): - """Horizontally flip the given image randomly. - - Returns: - tuple of processed image and label - """ - - def __call__(self, sample): - """Horizontally flip the image in sample.""" - image, label = sample - if isinstance(image, tf.Tensor): - image = tf.image.random_flip_left_right(image) - else: - if np.random.rand(1)[0] > 0.5: - image = np.fliplr(image) - return (image, label) - - -@transform_registry( - transform_type="ToArray", - process="preprocess", - framework="onnxrt_qlinearops, onnxrt_integerops, tensorflow, \ - tensorflow_itex, pytorch, mxnet", -) -class ToArray(BaseTransform): - """Convert PIL Image or NDArray to numpy array. - - Returns: - tuple of processed image and label - """ - - def __call__(self, sample): - """Convert image in sample to numpy array.""" - from PIL import Image - - image, label = sample - if isinstance(image, Image.Image): - image = np.array(image) - elif isinstance(image, mx.ndarray.NDArray): # pylint: disable=no-member - image = image.asnumpy() - else: - raise ValueError("Unknown image type!") - return (image, label) - - -np_dtype_map = { - "int8": np.int8, - "uint8": np.uint8, - "complex64": np.complex64, - "uint16": np.uint16, - "int32": np.int32, - "uint32": np.uint32, - "int64": np.int64, - "uint64": np.uint64, - "float32": np.float32, - "float16": np.float16, - "float64": np.float64, - "bool": bool, - "string": str, - "complex128": np.complex128, - "int16": np.int16, -} - - -@transform_registry(transform_type="Cast", process="general", framework="tensorflow, tensorflow_itex") -class CastTFTransform(BaseTransform): - """Convert image to given dtype. - - Args: - dtype (str, default='float32'): A dtype to convert image to - - Returns: - tuple of processed image and label - """ - - def __init__(self, dtype="float32"): - """Initialize `CastTFTransform` class.""" - self.tf_dtype_map = { - "int16": tf.int16, - "uint8": tf.uint8, - "uint16": tf.uint16, - "uint32": tf.uint32, - "uint64": tf.uint64, - "complex64": tf.complex64, - "int32": tf.int32, - "int64": tf.int64, - "float32": tf.float32, - "float16": tf.float16, - "float64": tf.float64, - "bool": tf.bool, - "string": tf.string, - "int8": tf.int8, - "complex128": tf.complex128, - } - - assert dtype in self.tf_dtype_map.keys(), "Unknown dtype" - self.dtype = dtype - - def __call__(self, sample): - """Convert image in sample to given dtype.""" - image, label = sample - if isinstance(image, tf.Tensor): - image = tf.image.convert_image_dtype(image, dtype=self.tf_dtype_map[self.dtype]) - else: - image = image.astype(np_dtype_map[self.dtype]) - return (image, label) - - -@transform_registry(transform_type="Cast", process="general", framework="onnxrt_qlinearops, onnxrt_integerops") -class CastONNXTransform(BaseTransform): - """Convert image to given dtype. - - Args: - dtype (str, default='float32'): A dtype to convert image to - - Returns: - tuple of processed image and label - """ - - def __init__(self, dtype="float32"): - """Initialize `CastONNXTransform` class.""" - assert dtype in np_dtype_map.keys(), "Unknown dtype" - self.dtype = dtype - - def __call__(self, sample): - """Convert image in sample to given dtype.""" - image, label = sample - image = image.astype(np_dtype_map[self.dtype]) - return (image, label) - - -@transform_registry(transform_type="Cast", process="general", framework="pytorch") -class CastPyTorchTransform(BaseTransform): - """Convert image to given dtype. - - Args: - dtype (str, default='float32'): A dtype to convert image to - - Returns: - tuple of processed image and label - """ - - def __init__(self, dtype="float32"): - """Initialize `CastPyTorchTransform` class.""" - dtype_map = { - "int8": torch.int8, - "uint8": torch.uint8, - "complex128": torch.complex128, - "int32": torch.int32, - "int64": torch.int64, - "complex64": torch.complex64, - "bfloat16": torch.bfloat16, - "float64": torch.float64, - "bool": torch.bool, - "float16": torch.float16, - "int16": torch.int16, - "float32": torch.float32, - } - assert dtype in dtype_map.keys(), "Unknown dtype" - self.dtype = dtype_map[dtype] - - def __call__(self, sample): - """Convert image in sample to given dtype.""" - image, label = sample - image = image.type(self.dtype) - return (image, label) - - -@transform_registry(transform_type="CenterCrop", process="preprocess", framework="tensorflow, tensorflow_itex") -class CenterCropTFTransform(BaseTransform): - """Crops the given image at the center to the given size. - - Args: - size (list or int): Size of the result - - Returns: - tuple of processed image and label - """ - - def __init__(self, size): - """Initialize `CenterCropTFTransform` class.""" - if isinstance(size, int): - self.size = size, size - elif isinstance(size, list): - if len(size) == 1: - self.size = size[0], size[0] - elif len(size) == 2: - self.size = size[0], size[1] - - def __call__(self, sample): - """Crops image in sample to the given size.""" - image, label = sample - if isinstance(image, tf.Tensor): - if len(image.shape) == 3: - height, width = image.shape[0:2] - elif len(image.shape) == 4: - height, width = image.shape[1:3] - else: - raise ValueError("Unknown image shape") - if height < self.size[0] or width < self.size[1]: - raise ValueError("Target size shouldn't be lager than image size") - y0 = (height - self.size[0]) // 2 - x0 = (width - self.size[1]) // 2 - image = tf.image.crop_to_bounding_box(image, y0, x0, self.size[0], self.size[1]) - else: - transform = CenterCropTransform(self.size) - image, label = transform(sample) - return (image, label) - - -@transform_registry(transform_type="PaddedCenterCrop", process="preprocess", framework="tensorflow, tensorflow_itex") -class PaddedCenterCropTransform(BaseTransform): - """Crops the given image at the center to the given size with padding. - - Args: - size (list or int): Size of the result - crop_padding (int): crop padding number - - Returns: - tuple of processed image and label - """ - - def __init__(self, size, crop_padding=0): - """Initialize `PaddedCenterCropTransform` class.""" - if isinstance(size, int): - self.image_size = size - elif isinstance(size, list): - if len(size) == 1: - self.image_size = size[0] - elif len(size) == 2: - if size[0] != size[1]: - raise ValueError("'crop height must eaqual to crop width'") - self.image_size = size[0] - self.crop_padding = crop_padding - - def __call__(self, sample): - """Crops image in sample to the given size with padding.""" - image, label = sample - h, w = image.shape[0], image.shape[1] - - padded_center_crop_size = int((self.image_size / (self.image_size + self.crop_padding)) * min(h, w)) - - y0 = (h - padded_center_crop_size + 1) // 2 - x0 = (w - padded_center_crop_size + 1) // 2 - image = image[y0 : y0 + padded_center_crop_size, x0 : x0 + padded_center_crop_size, :] - return (image, label) - - -@transform_registry(transform_type="Resize", process="preprocess", framework="tensorflow, tensorflow_itex") -class ResizeTFTransform(BaseTransform): - """Resize the input image to the given size. - - Args: - size (list or int): Size of the result - interpolation (str, default='bilinear'):Desired interpolation type, - support 'bilinear', 'nearest', 'bicubic' - - Returns: - tuple of processed image and label - """ - - def __init__(self, size, interpolation="bilinear"): - """Initialize `ResizeTFTransform` class.""" - if isinstance(size, int): - self.size = size, size - elif isinstance(size, list): - if len(size) == 1: - self.size = size[0], size[0] - elif len(size) == 2: - self.size = size[0], size[1] - self.interpolation = interpolation - - if self.interpolation not in ["bilinear", "nearest", "bicubic"]: - raise ValueError("Unsupported interpolation type!") - - def __call__(self, sample): - """Resize the input image in sample to the given size.""" - image, label = sample - if isinstance(image, tf.Tensor): - image = tf.image.resize(image, self.size, method=self.interpolation) - else: - image = cv2.resize(image, self.size, interpolation=interpolation_map[self.interpolation]) - return (image, label) - - -@transform_registry(transform_type="Resize", process="preprocess", framework="pytorch") -class ResizePytorchTransform(BaseTransform): - """Resize the input image to the given size. - - Args: - size (list or int): Size of the result - interpolation (str, default='bilinear'):Desired interpolation type, - support 'bilinear', 'nearest', 'bicubic' - - Returns: - tuple of processed image and label - """ - - def __init__(self, size, interpolation="bilinear"): - """Initialize `ResizePytorchTransform` class.""" - self.size = size - if interpolation in interpolation_pytorch_map.keys(): - self.interpolation = get_torchvision_map(interpolation_pytorch_map[interpolation]) - else: - raise ValueError("Undefined interpolation type") - - def __call__(self, sample): - """Resize the input image in sample to the given size.""" - image, label = sample - transformer = torchvision.transforms.Resize(size=self.size, interpolation=self.interpolation) - return (transformer(image), label) - - -@transform_registry(transform_type="RandomCrop", process="preprocess", framework="tensorflow, tensorflow_itex") -class RandomCropTFTransform(BaseTransform): - """Crop the image at a random location to the given size. - - Args: - size (list or tuple or int): Size of the result - - Returns: - tuple of processed image and label - """ - - def __init__(self, size): - """Initialize `RandomCropTFTransform` class.""" - if isinstance(size, int): - self.size = size, size - elif isinstance(size, list) or isinstance(size, tuple): - if len(size) == 1: - self.size = size[0], size[0] - elif len(size) == 2: - self.size = size[0], size[1] - - def __call__(self, sample): - """Crop the image in sample to the given size.""" - image, label = sample - if isinstance(image, tf.Tensor): - if len(image.shape) == 3: - height, width = image.shape[0:2] - elif len(image.shape) == 4: - height, width = image.shape[1:3] - - if self.size[0] > height or self.size[1] > width: - raise ValueError("Crop size must be smaller than image size") - - if self.size[0] == height and self.size[1] == width: - return (image, label) - - height = tf.cast(height, dtype=tf.float32) - width = tf.cast(width, dtype=tf.float32) - offset_height = (height - self.size[0]) / 2 - offset_width = (width - self.size[1]) / 2 - offset_height = tf.cast(offset_height, dtype=tf.int32) - offset_width = tf.cast(offset_width, dtype=tf.int32) - - image = tf.image.crop_to_bounding_box(image, offset_height, offset_width, self.size[0], self.size[1]) - else: - transform = RandomCropTransform(self.size) - image, label = transform(sample) - return (image, label) - - -@transform_registry(transform_type="RandomResizedCrop", process="preprocess", framework="pytorch") -class RandomResizedCropPytorchTransform(BaseTransform): - """Crop the given image to random size and aspect ratio. - - Args: - size (list or int): - Size of the result - scale (tuple or list, default=(0.08, 1.0)): - range of size of the origin size cropped - ratio (tuple or list, default=(3. / 4., 4. / 3.)): - range of aspect ratio of the origin aspect ratio cropped - interpolation (str, default='bilinear'): - Desired interpolation type, support 'bilinear', 'nearest', 'bicubic' - - Returns: - tuple of processed image and label - """ - - def __init__(self, size, scale=(0.08, 1.0), ratio=(3.0 / 4.0, 4.0 / 3.0), interpolation="bilinear"): - """Initialize `RandomResizedCropPytorchTransform` class.""" - self.size = size - self.scale = scale - self.ratio = ratio - - if interpolation in interpolation_pytorch_map.keys(): - self.interpolation = get_torchvision_map(interpolation_pytorch_map[interpolation]) - else: - raise ValueError("Undefined interpolation type") - - if scale[0] > scale[1] or ratio[0] > ratio[1]: - raise ValueError("Scale and ratio should be of kind (min, max)") - - def __call__(self, sample): - """Crop the image in sample to the random size.""" - image, label = sample - transformer = torchvision.transforms.RandomResizedCrop( - size=self.size, scale=self.scale, ratio=self.ratio, interpolation=self.interpolation - ) - return (transformer(image), label) - - -@transform_registry(transform_type="RandomResizedCrop", process="preprocess", framework="mxnet") -class RandomResizedCropMXNetTransform(BaseTransform): - """Crop the given image to random size and aspect ratio. - - Args: - size (list or int): - Size of the result - scale (tuple or list, default=(0.08, 1.0)): - range of size of the origin size cropped - ratio (tuple or list, default=(3. / 4., 4. / 3.)): - range of aspect ratio of the origin aspect ratio cropped - interpolation (str, default='bilinear'): - Desired interpolation type, support 'bilinear', 'nearest', 'bicubic' - - Returns: - tuple of processed image and label - """ - - def __init__(self, size, scale=(0.08, 1.0), ratio=(3.0 / 4.0, 4.0 / 3.0), interpolation="bilinear"): - """Initialize `RandomResizedCropMXNetTransform` class.""" - if isinstance(size, int): - self.size = size, size - elif isinstance(size, list): - if len(size) == 1: - self.size = size[0], size[0] - elif len(size) == 2: - self.size = size[1], size[0] - self.scale = scale - self.ratio = ratio - - if interpolation in interpolation_mxnet_map.keys(): - self.interpolation = interpolation_mxnet_map[interpolation] - else: - raise ValueError("Undefined interpolation type") - - if scale[0] > scale[1] or ratio[0] > ratio[1]: - raise ValueError("Scale and ratio should be of kind (min, max)") - - def __call__(self, sample): - """Crop the image in sample to the random size.""" - image, label = sample - transformer = mx.gluon.data.vision.transforms.RandomResizedCrop( - size=self.size, scale=self.scale, ratio=self.ratio, interpolation=self.interpolation - ) - return (transformer(image), label) - - -@transform_registry(transform_type="RandomResizedCrop", process="preprocess", framework="tensorflow, tensorflow_itex") -class RandomResizedCropTFTransform(BaseTransform): - """Crop the given image to random size and aspect ratio. - - Args: - size (list or int): - Size of the result - scale (tuple or list, default=(0.08, 1.0)): - range of size of the origin size cropped - ratio (tuple or list, default=(3. / 4., 4. / 3.)): - range of aspect ratio of the origin aspect ratio cropped - interpolation (str, default='bilinear'): - Desired interpolation type, support 'bilinear', 'nearest' - - Returns: - tuple of processed image and label - """ - - def __init__(self, size, scale=(0.08, 1.0), ratio=(3.0 / 4.0, 4.0 / 3.0), interpolation="bilinear"): - """Initialize `RandomResizedCropTFTransform` class.""" - if isinstance(size, int): - self.size = size, size - elif isinstance(size, list): - if len(size) == 1: - self.size = size[0], size[0] - elif len(size) == 2: - self.size = size[0], size[1] - - self.scale = scale - self.ratio = ratio - self.interpolation = interpolation - if self.interpolation not in ["bilinear", "nearest"]: - raise ValueError("Unsupported interpolation type!") - if scale[0] > scale[1] or ratio[0] > ratio[1]: - raise ValueError("Scale and ratio should be of kind (min, max)") - - def get_params(self, image, scale, ratio): - """Get the image parameters: position, height and width.""" - shape = image.shape - height = tf.cast(shape[0], dtype=tf.float32) - width = tf.cast(shape[1], dtype=tf.float32) - src_area = height * width - - for _ in range(10): - target_area = np.random.uniform(scale[0], scale[1]) * src_area - log_ratio = (np.log(ratio[0]), np.log(ratio[1])) - new_ratio = np.exp(np.random.uniform(log_ratio[0], log_ratio[1])) - - new_w = tf.math.round(tf.math.sqrt(tf.math.multiply(target_area, new_ratio))) - new_h = tf.math.round(tf.math.sqrt(tf.math.divide(target_area, new_ratio))) - - x0, y0 = tf.case( - [ - ( - tf.math.logical_and(tf.math.greater(width, new_w), tf.math.greater(height, new_h)), - lambda: ( - tf.random.uniform(shape=[], maxval=tf.math.subtract(width, new_w)), - tf.random.uniform(shape=[], maxval=tf.math.subtract(height, new_h)), - ), - ) - ], - default=lambda: (-1.0, -1.0), - ) - if x0 != -1.0 and y0 != -1.0: - return y0, x0, new_h, new_w - - in_ratio = width / height - new_w, new_h = tf.case( - [ - (tf.math.greater(min(ratio), in_ratio), lambda: (width, tf.math.round(width / min(ratio)))), - (tf.math.greater(in_ratio, max(ratio)), lambda: (height, tf.math.round(height * max(ratio)))), - ], - default=lambda: (width, height), - ) - - y0 = (height - new_h) / 2 - x0 = (width - new_w) / 2 - return y0, x0, new_h, new_w - - def __call__(self, sample): - """Crop the image in sample to the random size.""" - image, label = sample - if isinstance(image, tf.Tensor): - y0, x0, h, w = self.get_params(image, self.scale, self.ratio) - squeeze = False - if len(image.shape) == 3: - squeeze = True - image = tf.expand_dims(image, axis=0) - height, width = image.shape[1:3] - height = tf.cast(height, dtype=tf.float32) - width = tf.cast(width, dtype=tf.float32) - box_indices = tf.range(0, image.shape[0], dtype=tf.int32) - boxes = [y0 / height, x0 / width, (y0 + h) / height, (x0 + w) / width] - boxes = tf.broadcast_to(boxes, [image.shape[0], 4]) - image = tf.image.crop_and_resize(image, boxes, box_indices, self.size, self.interpolation) - if squeeze: - image = tf.squeeze(image, axis=0) - else: - transform = RandomResizedCropTransform(self.size, self.scale, self.ratio, self.interpolation) - image, label = transform(sample) - return (image, label) - - -@transform_registry(transform_type="Normalize", process="preprocess", framework="tensorflow, tensorflow_itex") -class NormalizeTFTransform(BaseTransform): - """Normalize a image with mean and standard deviation. - - Args: - mean (list, default=[0.0]): - means for each channel, if len(mean)=1, mean will be broadcasted to each channel, - otherwise its length should be same with the length of image shape - std (list, default=[1.0]): - stds for each channel, if len(std)=1, std will be broadcasted to each channel, - otherwise its length should be same with the length of image shape - - Returns: - tuple of processed image and label - """ - - def __init__(self, mean=[0.0], std=[1.0], rescale=None): - """Initialize `NormalizeTFTransform` class.""" - self.mean = mean - self.std = std - self.rescale = rescale - for item in self.std: - if item < 10**-6: - raise ValueError("Std should be greater than 0") - - def __call__(self, sample): - """Normalize the image in sample.""" - image, label = sample - if isinstance(image, tf.Tensor): - orig_dtype = image.dtype - mean = tf.broadcast_to(self.mean, tf.shape(input=image)) - mean = tf.cast(mean, dtype=image.dtype) - std = tf.broadcast_to(self.std, tf.shape(input=image)) - std = tf.cast(std, dtype=image.dtype) - image = (image - mean) / std - image = tf.cast(image, dtype=orig_dtype) - else: - transform = NormalizeTransform(self.mean, self.std) - image, label = transform(sample) - if self.rescale: - image /= self.rescale[0] - image -= self.rescale[1] - return (image, label) - - -@transform_registry(transform_type="KerasRescale", process="preprocess", framework="tensorflow, tensorflow_itex") -class RescaleKerasPretrainTransform(BaseTransform): - """Scale the values of image to [0,1]. - - Returns: - tuple of processed image and label - """ - - def __init__(self, rescale=None): - """Initialize `RescaleKerasPretrainTransform` class.""" - self.rescale = rescale - - def __call__(self, sample): - """Scale the values of the image in sample.""" - image, label = sample - if image.dtype == np.dtype("uint8"): - self.rescale = np.array(self.rescale).astype("uint8") - if len(self.rescale) == 2: - image = image / self.rescale[0] - image = image - self.rescale[1] - return (image, label) - - -@transform_registry(transform_type="Rescale", process="preprocess", framework="tensorflow, tensorflow_itex") -class RescaleTFTransform(BaseTransform): - """Scale the values of image to [0,1]. - - Returns: - tuple of processed image and label - """ - - def __call__(self, sample): - """Scale the values of the image in sample.""" - image, label = sample - if isinstance(image, tf.Tensor): - image = tf.cast(image, tf.float32) / 255.0 - else: - image = image.astype("float32") / 255.0 - return (image, label) - - -@transform_registry(transform_type="Rescale", process="preprocess", framework="onnxrt_qlinearops, onnxrt_integerops") -class RescaleTransform(BaseTransform): - """Scale the values of image to [0,1]. - - Returns: - tuple of processed image and label - """ - - def __call__(self, sample): - """Scale the values of the image in sample.""" - image, label = sample - if isinstance(image, np.ndarray): - image = image.astype("float32") / 255.0 - return (image, label) - - -@transform_registry( - transform_type="AlignImageChannel", - process="preprocess", - framework="tensorflow, tensorflow_itex, \ - onnxrt_qlinearops, onnxrt_integerops, mxnet", -) -class AlignImageChannelTransform(BaseTransform): - """Align image channel, now just support [H,W]->[H,W,dim], [H,W,4]->[H,W,3] and [H,W,3]->[H,W]. - - Input image must be np.ndarray. - - Returns: - tuple of processed image and label - """ - - def __init__(self, dim=3): - """Initialize `AlignImageChannelTransform` class.""" - logger.warning("This transform is going to be deprecated") - if dim < 1 or dim > 4: - raise ValueError("Unsupported image dim!") - self.dim = dim - - def __call__(self, sample): - """Align channel of the image in sample.""" - image, label = sample - if len(image.shape) == 2: - image = np.dstack([image] * self.dim) - if isinstance(image, np.ndarray) and image.shape[-1] != self.dim: - if image.shape[-1] == 4 and self.dim == 3: - image = cv2.cvtColor(image, cv2.COLOR_RGBA2RGB) - elif image.shape[-1] == 3 and self.dim == 1: - image = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY) - image = np.expand_dims(image, axis=-1) - else: - raise ValueError("Unsupported conversion!") - return (image, label) - - -@transform_registry(transform_type="AlignImageChannel", process="preprocess", framework="pytorch") -class PyTorchAlignImageChannel(BaseTransform): - """Align image channel, now just support [H,W,4]->[H,W,3] and [H,W,3]->[H,W]. - - Input image must be PIL Image. - - Returns: - tuple of processed image and label - """ - - def __init__(self, dim=3): - """Initialize `PyTorchAlignImageChannel` class.""" - logger.warning("This transform is going to be deprecated") - if dim != 1 and dim != 3: - raise ValueError("Unsupported image dim!") - self.dim = dim - - def __call__(self, sample): - """Align channel of the image in sample.""" - from PIL import Image - - image, label = sample - assert isinstance(image, Image.Image), "Input image must be PIL Image" - if self.dim == 3: - image = image.convert("RGB") - elif self.dim == 1: - image = image.convert("L") - else: - raise ValueError("Unsupported conversion!") - return (image, label) - - -@transform_registry(transform_type="ToNDArray", process="preprocess", framework="mxnet") -class ToNDArrayTransform(BaseTransform): - """Convert np.array to NDArray. - - Returns: - tuple of processed image and label - """ - - def __call__(self, sample): - """Convert np.array of the image in sample.""" - image, label = sample - image = mx.nd.array(image) - return image, label - - -@transform_registry(transform_type="Resize", process="preprocess", framework="mxnet") -class ResizeMXNetTransform(BaseTransform): - """Resize the input image to the given size. - - Args: - size (list or int): Size of the result - interpolation (str, default='bilinear'):Desired interpolation type, - support 'bilinear', 'nearest', 'bicubic' - - Returns: - tuple of processed image and label - """ - - def __init__(self, size, interpolation="bilinear"): - """Initialize `ResizeMXNetTransform` class.""" - if isinstance(size, int): - self.size = size, size - elif isinstance(size, list): - if len(size) == 1: - self.size = size[0], size[0] - elif len(size) == 2: - self.size = size[1], size[0] - - if interpolation in interpolation_mxnet_map.keys(): - self.interpolation = interpolation_mxnet_map[interpolation] - else: - raise ValueError("Undefined interpolation type") - - def __call__(self, sample): - """Resize the input image in sample to the given size.""" - image, label = sample - transformer = mx.gluon.data.vision.transforms.Resize(size=self.size, interpolation=self.interpolation) - return (transformer(image), label) - - -@transform_registry(transform_type="Resize", process="preprocess", framework="onnxrt_qlinearops, onnxrt_integerops") -class ResizeTransform(BaseTransform): - """Resize the input image to the given size. - - Args: - size (list or int): Size of the result - interpolation (str, default='bilinear'):Desired interpolation type, - support 'bilinear', 'nearest', 'bicubic'. - - Returns: - tuple of processed image and label - """ - - def __init__(self, size, interpolation="bilinear"): - """Initialize `ResizeTransform` class.""" - if isinstance(size, int): - self.size = size, size - elif isinstance(size, list): - if len(size) == 1: - self.size = size[0], size[0] - elif len(size) == 2: - self.size = size[0], size[1] - - if interpolation in interpolation_map.keys(): - self.interpolation = interpolation_map[interpolation] - else: - raise ValueError("Undefined interpolation type") - - def __call__(self, sample): - """Resize the input image in sample to the given size.""" - image, label = sample - image = cv2.resize(image, self.size, interpolation=self.interpolation) - if len(image.shape) == 2: - image = np.expand_dims(image, -1) - return (image, label) - - -@transform_registry(transform_type="CropResize", process="preprocess", framework="tensorflow, tensorflow_itex") -class CropResizeTFTransform(BaseTransform): - """Crop the input image with given location and resize it. - - Args: - x (int):Left boundary of the cropping area - y (int):Top boundary of the cropping area - width (int):Width of the cropping area - height (int):Height of the cropping area - size (list or int): resize to new size after cropping - interpolation (str, default='bilinear'):Desired interpolation type, - support 'bilinear', 'nearest', 'bicubic' - - Returns: - tuple of processed image and label - """ - - def __init__(self, x, y, width, height, size, interpolation="bilinear"): - """Initialize `CropResizeTFTransform` class.""" - if interpolation not in ["bilinear", "nearest", "bicubic"]: - raise ValueError("Unsupported interpolation type!") - self.interpolation = interpolation - self.x = x - self.y = y - self.width = width - self.height = height - if isinstance(size, int): - self.size = size, size - elif isinstance(size, list): - if len(size) == 1: - self.size = size[0], size[0] - elif len(size) == 2: - self.size = size[0], size[1] - - def __call__(self, sample): - """Resize the input image in sample with given location.""" - image, label = sample - if isinstance(image, tf.Tensor): - image = tf.image.crop_to_bounding_box(image, self.y, self.x, self.height, self.width) - image = tf.image.resize(image, self.size, method=self.interpolation) - else: - transform = CropResizeTransform(self.x, self.y, self.width, self.height, self.size, self.interpolation) - image, label = transform(sample) - return (image, label) - - -@transform_registry(transform_type="CropResize", process="preprocess", framework="pytorch") -class PyTorchCropResizeTransform(BaseTransform): - """Crop the input image with given location and resize it. - - Args: - x (int):Left boundary of the cropping area - y (int):Top boundary of the cropping area - width (int):Width of the cropping area - height (int):Height of the cropping area - size (list or int): resize to new size after cropping - interpolation (str, default='bilinear'):Desired interpolation type, - support 'bilinear', 'nearest', 'bicubic' - - Returns: - tuple of processed image and label - """ - - def __init__(self, x, y, width, height, size, interpolation="bilinear"): - """Initialize `PyTorchCropResizeTransform` class.""" - if interpolation in interpolation_pytorch_map.keys(): - self.interpolation = get_torchvision_map(interpolation_pytorch_map[interpolation]) - else: - raise ValueError("Undefined interpolation type") - self.x = x - self.y = y - self.width = width - self.height = height - self.size = size - - def __call__(self, sample): - """Resize the input image in sample with given location.""" - image, label = sample - image = image.crop((self.x, self.y, self.x + self.width, self.y + self.height)) - transformer = torchvision.transforms.Resize(size=self.size, interpolation=self.interpolation) - return (transformer(image), label) - - -@transform_registry(transform_type="CropResize", process="preprocess", framework="mxnet") -class MXNetCropResizeTransform(BaseTransform): - """Crop the input image with given location and resize it. - - Args: - x (int):Left boundary of the cropping area - y (int):Top boundary of the cropping area - width (int):Width of the cropping area - height (int):Height of the cropping area - size (list or int): resize to new size after cropping - interpolation (str, default='bilinear'):Desired interpolation type, - support 'bilinear', 'nearest', 'bicubic' - - Returns: - tuple of processed image and label - """ - - def __init__(self, x, y, width, height, size, interpolation="bilinear"): - """Initialize `MXNetCropResizeTransform` class.""" - if interpolation in interpolation_mxnet_map.keys(): - self.interpolation = interpolation_mxnet_map[interpolation] - else: - raise ValueError("Undefined interpolation type") - self.x = x - self.y = y - self.width = width - self.height = height - self.size = size - - def __call__(self, sample): - """Resize the input image in sample with given location.""" - image, label = sample - transformer = mx.gluon.data.vision.transforms.CropResize( - self.x, self.y, self.width, self.height, self.size, self.interpolation - ) - return (transformer(image), label) - - -@transform_registry(transform_type="CropResize", process="preprocess", framework="onnxrt_qlinearops, onnxrt_integerops") -class CropResizeTransform(BaseTransform): - """Crop the input image with given location and resize it. - - Args: - x (int):Left boundary of the cropping area - y (int):Top boundary of the cropping area - width (int):Width of the cropping area - height (int):Height of the cropping area - size (list or int): resize to new size after cropping - interpolation (str, default='bilinear'):Desired interpolation type, - support 'bilinear', 'nearest', 'bicubic' - - Returns: - tuple of processed image and label - """ - - def __init__(self, x, y, width, height, size, interpolation="bilinear"): - """Initialize `CropResizeTransform` class.""" - if interpolation in interpolation_map.keys(): - self.interpolation = interpolation_map[interpolation] - else: - raise ValueError("Undefined interpolation type") - self.x = x - self.y = y - self.width = width - self.height = height - if isinstance(size, int): - self.size = size, size - elif isinstance(size, list) or isinstance(size, tuple): - if len(size) == 1: - self.size = size[0], size[0] - elif len(size) == 2: - self.size = size[0], size[1] - - def __call__(self, sample): - """Crop the input image in sample with given location.""" - image, label = sample - image = image[self.y : self.y + self.height, self.x : self.x + self.width, :] - image = cv2.resize(image, self.size, interpolation=self.interpolation) - return (image, label) - - -@transform_registry(transform_type="CenterCrop", process="preprocess", framework="onnxrt_qlinearops, onnxrt_integerops") -class CenterCropTransform(BaseTransform): - """Crops the given image at the center to the given size. - - Args: - size (list or int): Size of the result - - Returns: - tuple of processed image and label - """ - - def __init__(self, size): - """Initialize `CenterCropTransform` class.""" - if isinstance(size, int): - self.height, self.width = size, size - elif isinstance(size, list) or isinstance(size, tuple): - if len(size) == 1: - self.height, self.width = size[0], size[0] - elif len(size) == 2: - self.height, self.width = size[0], size[1] - - def __call__(self, sample): - """Crop the input image in sample at the center to the given size.""" - image, label = sample - h, w = image.shape[0], image.shape[1] - if h + 1 < self.height or w + 1 < self.width: - raise ValueError( - "Required crop size {} is larger then input image size {}".format((self.height, self.width), (h, w)) - ) - - if self.height == h and self.width == w: - return (image, label) - - y0 = (h - self.height) // 2 - x0 = (w - self.width) // 2 - image = image[y0 : y0 + self.height, x0 : x0 + self.width, :] - return (image, label) - - -@transform_registry(transform_type="Normalize", process="preprocess", framework="mxnet") -class MXNetNormalizeTransform(BaseTransform): - """Normalize a image with mean and standard deviation. - - Args: - mean (list, default=[0.0]): - means for each channel, if len(mean)=1, mean will be broadcasted to each channel, - otherwise its length should be same with the length of image shape - std (list, default=[1.0]): - stds for each channel, if len(std)=1, std will be broadcasted to each channel, - otherwise its length should be same with the length of image shape - - Returns: - tuple of processed image and label - """ - - def __init__(self, mean=[0.0], std=[1.0]): - """Initialize `MXNetNormalizeTransform` class.""" - self.mean = mean - self.std = std - for item in self.std: - if item < 10**-6: - raise ValueError("Std should be greater than 0") - - def __call__(self, sample): - """Normalize the image in sample.""" - image, label = sample - axes = [len(image.shape) - 1] - axes.extend(list(np.arange(len(image.shape) - 1))) - image = mx.ndarray.transpose(image, axes) - assert len(self.mean) == image.shape[0], "Mean channel must match image channel" - transformer = mx.gluon.data.vision.transforms.Normalize(self.mean, self.std) - image = transformer(image) - axes = list(np.arange(1, len(image.shape))) - axes.extend([0]) - image = mx.ndarray.transpose(image, axes) - return (image, label) - - -@transform_registry(transform_type="Normalize", process="preprocess", framework="pytorch") -class PyTorchNormalizeTransform(MXNetNormalizeTransform): - """Normalize a image with mean and standard deviation. - - Args: - mean (list, default=[0.0]): - means for each channel, if len(mean)=1, mean will be broadcasted to each channel, - otherwise its length should be same with the length of image shape - std (list, default=[1.0]): - stds for each channel, if len(std)=1, std will be broadcasted to each channel, - otherwise its length should be same with the length of image shape - - Returns: - tuple of processed image and label - """ - - def __call__(self, sample): - """Normalize the image in sample.""" - image, label = sample - transformer = torchvision.transforms.Normalize(self.mean, self.std) - image = transformer(image) - return (image, label) - - -@transform_registry(transform_type="Normalize", process="preprocess", framework="onnxrt_qlinearops, onnxrt_integerops") -class NormalizeTransform(BaseTransform): - """Normalize a image with mean and standard deviation. - - Args: - mean (list, default=[0.0]): - means for each channel, if len(mean)=1, mean will be broadcasted to each channel, - otherwise its length should be same with the length of image shape - std (list, default=[1.0]): - stds for each channel, if len(std)=1, std will be broadcasted to each channel, - otherwise its length should be same with the length of image shape - - Returns: - tuple of processed image and label - """ - - def __init__(self, mean=[0.0], std=[1.0]): - """Initialize `NormalizeTransform` class.""" - self.mean = mean - self.std = std - for item in self.std: - if item < 10**-6: - raise ValueError("Std should be greater than 0") - - def __call__(self, sample): - """Normalize the image in sample.""" - image, label = sample - assert len(self.mean) == image.shape[-1], "Mean channel must match image channel" - image = (image - self.mean) / self.std - return (image, label) - - -@transform_registry( - transform_type="RandomCrop", process="preprocess", framework="mxnet, onnxrt_qlinearops, onnxrt_integerops" -) -class RandomCropTransform(BaseTransform): - """Crop the image at a random location to the given size. - - Args: - size (list or tuple or int): Size of the result - - Returns: - tuple of processed image and label - """ - - def __init__(self, size): - """Initialize `RandomCropTransform` class.""" - if isinstance(size, int): - self.height, self.width = size, size - elif isinstance(size, list) or isinstance(size, tuple): - if len(size) == 1: - self.height, self.width = size[0], size[0] - elif len(size) == 2: - self.height, self.width = size[0], size[1] - - def __call__(self, sample): - """Crop the image in sample to the given size.""" - image, label = sample - h, w = image.shape[0], image.shape[1] - if h + 1 < self.height or w + 1 < self.width: - raise ValueError( - "Required crop size {} is larger then input image size {}".format((self.height, self.width), (h, w)) - ) - - if self.height == h and self.width == w: - return (image, label) - - rand_h = np.random.randint(0, h - self.height + 1) - rand_w = np.random.randint(0, w - self.width + 1) - if len(image.shape) == 2: - image = image[rand_h : rand_h + self.height, rand_w : rand_w + self.width] - else: - image = image[rand_h : rand_h + self.height, rand_w : rand_w + self.width, :] - return (image, label) - - -@transform_registry( - transform_type="RandomResizedCrop", process="preprocess", framework="onnxrt_qlinearops, onnxrt_integerops" -) -class RandomResizedCropTransform(BaseTransform): - """Crop the given image to random size and aspect ratio. - - Args: - size (list or int): - Size of the result - scale (tuple or list, default=(0.08, 1.0)): - range of size of the origin size cropped - ratio (tuple or list, default=(3. / 4., 4. / 3.)): - range of aspect ratio of the origin aspect ratio cropped - interpolation (str, default='bilinear'): - Desired interpolation type, support 'bilinear', 'nearest' - - Returns: - tuple of processed image and label - """ - - def __init__(self, size, scale=(0.08, 1.0), ratio=(3.0 / 4.0, 4.0 / 3.0), interpolation="bilinear"): - """Initialize `RandomResizedCropTransform` class.""" - if isinstance(size, int): - self.size = size, size - elif isinstance(size, list) or isinstance(size, tuple): - if len(size) == 1: - self.size = size[0], size[0] - elif len(size) == 2: - self.size = size[0], size[1] - - self.scale = scale - self.ratio = ratio - - if interpolation in interpolation_map.keys(): - self.interpolation = interpolation_map[interpolation] - else: - raise ValueError("Undefined interpolation type") - - if scale[0] > scale[1] or ratio[0] > ratio[1]: - raise ValueError("Scale and ratio should be of kind (min, max)") - - def get_params(self, image, scale, ratio): - """Get the image parameters: position, height and width.""" - h, w = image.shape[0], image.shape[1] - src_area = h * w - - for _ in range(10): - target_area = np.random.uniform(scale[0], scale[1]) * src_area - log_ratio = (np.log(ratio[0]), np.log(ratio[1])) - new_ratio = np.exp(np.random.uniform(log_ratio[0], log_ratio[1])) - - new_w = int(np.round(np.sqrt(target_area * new_ratio))) - new_h = int(np.round(np.sqrt(target_area / new_ratio))) - - if new_w < w and new_h < h: - x0 = np.random.randint(0, w - new_w) - y0 = np.random.randint(0, h - new_h) - return y0, x0, new_h, new_w - - in_ratio = float(w) / float(h) - if in_ratio < min(ratio): - new_w = w - new_h = int(round(new_w / min(ratio))) - elif in_ratio > max(ratio): - new_h = h - new_w = int(round(new_h * max(ratio))) - else: - new_w = w - new_h = h - y0 = (h - new_h) // 2 - x0 = (w - new_w) // 2 - return y0, x0, new_h, new_w - - def __call__(self, sample): - """Crop the image in sample to random size.""" - image, label = sample - y0, x0, h, w = self.get_params(image, self.scale, self.ratio) - crop_img = image[y0 : y0 + h, x0 : x0 + w, :] - image = cv2.resize(crop_img, self.size, interpolation=self.interpolation) - return (image, label) - - -def _compute_softmax(scores): - """Compute softmax probability over raw logits.""" - import math - - if not scores: - return [] - - max_score = None - for score in scores: - if max_score is None or score > max_score: - max_score = score - - exp_scores = [] - total_sum = 0.0 - for score in scores: - x = math.exp(score - max_score) - exp_scores.append(x) - total_sum += x - - probs = [] - for score in exp_scores: - probs.append(score / total_sum) - return probs - - -def _get_best_indexes(logits, n_best_size): - """Get the n-best logits from a list.""" - index_and_score = sorted(enumerate(logits), key=lambda x: x[1], reverse=True) - - best_indexes = [] - for i in range(len(index_and_score)): - if i >= n_best_size: - break - best_indexes.append(index_and_score[i][0]) - return best_indexes - - -def get_final_text(pred_text, orig_text, do_lower_case): - """Project the tokenized prediction back to the original text.""" - import six - - from . import tokenization - - def _strip_spaces(text): - ns_chars = [] - ns_to_s_map = collections.OrderedDict() - for i, c in enumerate(text): - if c == " ": - continue - ns_to_s_map[len(ns_chars)] = i - ns_chars.append(c) - ns_text = "".join(ns_chars) - return (ns_text, ns_to_s_map) - - tokenizer = tokenization.BasicTokenizer(do_lower_case=do_lower_case) - tok_text = " ".join(tokenizer.tokenize(orig_text)) - start_position = tok_text.find(pred_text) - if start_position == -1: - return orig_text - end_position = start_position + len(pred_text) - 1 - - (orig_ns_text, orig_ns_to_s_map) = _strip_spaces(orig_text) - (tok_ns_text, tok_ns_to_s_map) = _strip_spaces(tok_text) - - if len(orig_ns_text) != len(tok_ns_text): - return orig_text - - tok_s_to_ns_map = {} - for i, tok_index in six.iteritems(tok_ns_to_s_map): - tok_s_to_ns_map[tok_index] = i - - orig_start_position = None - if start_position in tok_s_to_ns_map: - ns_start_position = tok_s_to_ns_map[start_position] - if ns_start_position in orig_ns_to_s_map: - orig_start_position = orig_ns_to_s_map[ns_start_position] - - if orig_start_position is None: - return orig_text - - orig_end_position = None - if end_position in tok_s_to_ns_map: - ns_end_position = tok_s_to_ns_map[end_position] - if ns_end_position in orig_ns_to_s_map: - orig_end_position = orig_ns_to_s_map[ns_end_position] - - if orig_end_position is None: - return orig_text - - output_text = orig_text[orig_start_position : (orig_end_position + 1)] - return output_text - - -class SquadExample(object): - """A single training/test example for simple sequence classification. - - For examples without an answer, the start and end position are -1. - """ - - def __init__( - self, - qas_id, - question_text, - doc_tokens, - orig_answer_text=None, - start_position=None, - end_position=None, - is_impossible=False, - ): - """Initialize `SquadExample` class.""" - self.qas_id = qas_id - self.question_text = question_text - self.doc_tokens = doc_tokens - self.orig_answer_text = orig_answer_text - self.start_position = start_position - self.end_position = end_position - self.is_impossible = is_impossible - - -class InputFeatures(object): - """A single set of features of data.""" - - def __init__( - self, - unique_id, - example_index, - doc_span_index, - tokens, - token_to_orig_map, - token_is_max_context, - input_ids, - input_mask, - segment_ids, - start_position=None, - end_position=None, - is_impossible=None, - ): - """Initialize `InputFeatures` class.""" - self.unique_id = unique_id - self.example_index = example_index - self.doc_span_index = doc_span_index - self.tokens = tokens - self.token_to_orig_map = token_to_orig_map - self.token_is_max_context = token_is_max_context - self.input_ids = input_ids - self.input_mask = input_mask - self.segment_ids = segment_ids - self.start_position = start_position - self.end_position = end_position - self.is_impossible = is_impossible - - -def read_squad_examples(input_file): - """Read a SQuAD json file into a list of SquadExample.""" - import json - - with tf.io.gfile.GFile(input_file, "r") as reader: - input_data = json.load(reader)["data"] - - def is_whitespace(c): - if c == " " or c == "\t" or c == "\r" or c == "\n" or ord(c) == 0x202F: - return True - return False - - examples = [] - for entry in input_data: - for paragraph in entry["paragraphs"]: - paragraph_text = paragraph["context"] - doc_tokens = [] - char_to_word_offset = [] - prev_is_whitespace = True - for c in paragraph_text: - if is_whitespace(c): - prev_is_whitespace = True - else: - if prev_is_whitespace: - doc_tokens.append(c) - else: - doc_tokens[-1] += c - prev_is_whitespace = False - char_to_word_offset.append(len(doc_tokens) - 1) - - for qa in paragraph["qas"]: - qas_id = qa["id"] - question_text = qa["question"] - start_position = None - end_position = None - orig_answer_text = None - is_impossible = False - example = SquadExample( - qas_id=qas_id, - question_text=question_text, - doc_tokens=doc_tokens, - orig_answer_text=orig_answer_text, - start_position=start_position, - end_position=end_position, - is_impossible=is_impossible, - ) - examples.append(example) - return examples - - -def _check_is_max_context(doc_spans, cur_span_index, position): - """Check if this is the 'max context' doc span for the token.""" - best_score = None - best_span_index = None - for span_index, doc_span in enumerate(doc_spans): - end = doc_span.start + doc_span.length - 1 - if position < doc_span.start: - continue - if position > end: - continue - num_left_context = position - doc_span.start - num_right_context = end - position - score = min(num_left_context, num_right_context) + 0.01 * doc_span.length - if best_score is None or score > best_score: - best_score = score - best_span_index = span_index - - return cur_span_index == best_span_index - - -def convert_examples_to_features(examples, tokenizer, max_seq_length, doc_stride, max_query_length, output_fn): - """Load a data file into a list of `InputBatch`s.""" - unique_id = 1000000000 - for example_index, example in enumerate(examples): - query_tokens = tokenizer.tokenize(example.question_text) - if len(query_tokens) > max_query_length: - query_tokens = query_tokens[0:max_query_length] - - tok_to_orig_index = [] - orig_to_tok_index = [] - all_doc_tokens = [] - for i, token in enumerate(example.doc_tokens): - orig_to_tok_index.append(len(all_doc_tokens)) - sub_tokens = tokenizer.tokenize(token) - for sub_token in sub_tokens: - tok_to_orig_index.append(i) - all_doc_tokens.append(sub_token) - - tok_start_position = None - tok_end_position = None - - # The -3 accounts for [CLS], [SEP] and [SEP] - max_tokens_for_doc = max_seq_length - len(query_tokens) - 3 - - # We can have documents that are longer than the maximum sequence length. - # To deal with this we do a sliding window approach, where we take chunks - # of the up to our max length with a stride of `doc_stride`. - _DocSpan = collections.namedtuple("DocSpan", ["start", "length"]) # pylint: disable=invalid-name - doc_spans = [] - start_offset = 0 - while start_offset < len(all_doc_tokens): - length = len(all_doc_tokens) - start_offset - if length > max_tokens_for_doc: - length = max_tokens_for_doc - doc_spans.append(_DocSpan(start=start_offset, length=length)) - if start_offset + length == len(all_doc_tokens): - break - start_offset += min(length, doc_stride) - for doc_span_index, doc_span in enumerate(doc_spans): - tokens = [] - token_to_orig_map = {} - token_is_max_context = {} - segment_ids = [] - tokens.append("[CLS]") - segment_ids.append(0) - for token in query_tokens: - tokens.append(token) - segment_ids.append(0) - tokens.append("[SEP]") - segment_ids.append(0) - - for i in range(doc_span.length): - split_token_index = doc_span.start + i - token_to_orig_map[len(tokens)] = tok_to_orig_index[split_token_index] - - is_max_context = _check_is_max_context(doc_spans, doc_span_index, split_token_index) - token_is_max_context[len(tokens)] = is_max_context - tokens.append(all_doc_tokens[split_token_index]) - segment_ids.append(1) - tokens.append("[SEP]") - segment_ids.append(1) - - input_ids = tokenizer.convert_tokens_to_ids(tokens) - - # The mask has 1 for real tokens and 0 for padding tokens. Only real - # tokens are attended to. - input_mask = [1] * len(input_ids) - - # Zero-pad up to the sequence length. - while len(input_ids) < max_seq_length: - input_ids.append(0) - input_mask.append(0) - segment_ids.append(0) - - assert len(input_ids) == max_seq_length - assert len(input_mask) == max_seq_length - assert len(segment_ids) == max_seq_length - - start_position = None - end_position = None - - feature = InputFeatures( - unique_id=unique_id, - example_index=example_index, - doc_span_index=doc_span_index, - tokens=tokens, - token_to_orig_map=token_to_orig_map, - token_is_max_context=token_is_max_context, - input_ids=input_ids, - input_mask=input_mask, - segment_ids=segment_ids, - start_position=start_position, - end_position=end_position, - is_impossible=example.is_impossible, - ) - # Run callback - output_fn(feature) - unique_id += 1 - - -@transform_registry(transform_type="Collect", process="postprocess", framework="tensorflow") -class CollectTransform(BaseTransform): - """Postprocess the predictions, collect data.""" - - def __init__(self, length=10833): - """Initialize `CollectTransform` class.""" - self.length = length - self.unique_id = [] - self.start_logits = [] - self.end_logits = [] - self.all_sample = (None, None) - self.idx = 1000000000 - - def __call__(self, sample): - """Collect postprocess data.""" - all_results, label = sample - result_list = [np.expand_dims(result, 0) for result in all_results] - for result in result_list: - if len(self.unique_id) < self.length: - result = result.transpose(2, 0, 1) - self.unique_id.append(self.idx) - self.start_logits.append(result[0]) - self.end_logits.append(result[1]) - self.idx += 1 - if len(self.unique_id) == self.length: - self.all_sample = ([self.unique_id, self.start_logits, self.end_logits], label) - return self.all_sample - - -@transform_registry(transform_type="SquadV1", process="postprocess", framework="tensorflow, tensorflow_itex") -class TFSquadV1PostTransform(BaseTransform): - """Postprocess the predictions of bert on SQuAD. - - Args: - label_file (str): path of label file - vocab_file(str): path of vocabulary file - n_best_size (int, default=20): - The total number of n-best predictions to generate in nbest_predictions.json - max_seq_length (int, default=384): - The maximum total input sequence length after WordPiece tokenization. - Sequences longer than this will be truncated, shorter than this will be padded - max_query_length (int, default=64): - The maximum number of tokens for the question. - Questions longer than this will be truncated to this length - max_answer_length (int, default=30): - The maximum length of an answer that can be generated. This is needed because - the start and end predictions are not conditioned on one another - do_lower_case (bool, default=True): - Whether to lower case the input text. - Should be True for uncased models and False for cased models - doc_stride (int, default=128): - When splitting up a long document into chunks, - how much stride to take between chunks - - Returns: - tuple of processed prediction and label - """ - - def __init__( - self, - label_file, - vocab_file, - n_best_size=20, - max_seq_length=384, - max_query_length=64, - max_answer_length=30, - do_lower_case=True, - doc_stride=128, - ): - """Initialize `TFSquadV1PostTransform` class.""" - from . import tokenization - - self.eval_examples = read_squad_examples(label_file) - tokenizer = tokenization.FullTokenizer(vocab_file=vocab_file, do_lower_case=do_lower_case) - - self.eval_features = [] - - def append_feature(feature): - self.eval_features.append(feature) - - convert_examples_to_features( - examples=self.eval_examples, - tokenizer=tokenizer, - max_seq_length=max_seq_length, - doc_stride=doc_stride, - max_query_length=max_query_length, - output_fn=append_feature, - ) - - self.n_best_size = n_best_size - self.max_answer_length = max_answer_length - self.do_lower_case = do_lower_case - self.RawResult = collections.namedtuple("RawResult", ["unique_id", "start_logits", "end_logits"]) - - def process_result(self, results): - """Get the processed results.""" - processed_results = [] - # notice the result list sequence - for unique_id, start_logits, end_logits in zip(*results): - processed_results.append( - self.RawResult( - unique_id=int(unique_id), - start_logits=[float(x) for x in start_logits.flat], - end_logits=[float(x) for x in end_logits.flat], - ) - ) - - return processed_results - - def get_postprocess_result(self, sample): - """Get the post processed results.""" - if sample == (None, None): - return (None, None) - all_results, label = sample - all_results = self.process_result(all_results) - example_index_to_features = collections.defaultdict(list) - for feature in self.eval_features: - example_index_to_features[feature.example_index].append(feature) - - unique_id_to_result = {} - for result in all_results: - unique_id_to_result[result.unique_id] = result - - _PrelimPrediction = collections.namedtuple( # pylint: disable=invalid-name - "PrelimPrediction", ["feature_index", "start_index", "end_index", "start_logit", "end_logit"] - ) - - all_predictions = collections.OrderedDict() - for example_index, example in enumerate(self.eval_examples): - features = example_index_to_features[example_index] - - prelim_predictions = [] - # keep track of the minimum score of null start+end of position 0 - score_null = 1000000 # large and positive - min_null_feature_index = 0 # the paragraph slice with min mull score - null_start_logit = 0 # the start logit at the slice with min null score - null_end_logit = 0 # the end logit at the slice with min null score - for feature_index, feature in enumerate(features): - # skip the case that is not predicted - if feature.unique_id not in unique_id_to_result: - all_predictions[example.qas_id] = "*#skip this example#*" - continue - result = unique_id_to_result[feature.unique_id] - start_indexes = _get_best_indexes(result.start_logits, self.n_best_size) - end_indexes = _get_best_indexes(result.end_logits, self.n_best_size) - - for start_index in start_indexes: - for end_index in end_indexes: - # We could hypothetically create invalid predictions, e.g., predict - # that the start of the span is in the question. We throw out all - # invalid predictions. - if start_index >= len(feature.tokens): - continue - if end_index >= len(feature.tokens): - continue - if start_index not in feature.token_to_orig_map: - continue - if end_index not in feature.token_to_orig_map: - continue - if not feature.token_is_max_context.get(start_index, False): - continue - if end_index < start_index: - continue - length = end_index - start_index + 1 - if length > self.max_answer_length: - continue - prelim_predictions.append( - _PrelimPrediction( - feature_index=feature_index, - start_index=start_index, - end_index=end_index, - start_logit=result.start_logits[start_index], - end_logit=result.end_logits[end_index], - ) - ) - - prelim_predictions = sorted( - prelim_predictions, key=lambda x: (x.start_logit + x.end_logit), reverse=True - ) - _NbestPrediction = collections.namedtuple( # pylint: disable=invalid-name - "NbestPrediction", ["text", "start_logit", "end_logit"] - ) - - seen_predictions = {} - nbest = [] - for pred in prelim_predictions: - if len(nbest) >= self.n_best_size: - break - feature = features[pred.feature_index] - if pred.start_index > 0: # this is a non-null prediction - tok_tokens = feature.tokens[pred.start_index : (pred.end_index + 1)] - orig_doc_start = feature.token_to_orig_map[pred.start_index] - orig_doc_end = feature.token_to_orig_map[pred.end_index] - orig_tokens = example.doc_tokens[orig_doc_start : (orig_doc_end + 1)] - tok_text = " ".join(tok_tokens) - - # De-tokenize WordPieces that have been split off. - tok_text = tok_text.replace(" ##", "") - tok_text = tok_text.replace("##", "") - - # Clean whitespace - tok_text = tok_text.strip() - tok_text = " ".join(tok_text.split()) - orig_text = " ".join(orig_tokens) - - final_text = get_final_text(tok_text, orig_text, self.do_lower_case) - if final_text in seen_predictions: - continue - - seen_predictions[final_text] = True - else: - final_text = "" - seen_predictions[final_text] = True - - nbest.append( - _NbestPrediction(text=final_text, start_logit=pred.start_logit, end_logit=pred.end_logit) - ) - - # In very rare edge cases we could have no valid predictions. So we - # just create a nonce prediction in this case to avoid failure. - if not nbest: - nbest.append(_NbestPrediction(text="empty", start_logit=0.0, end_logit=0.0)) - - assert len(nbest) >= 1 - - total_scores = [] - best_non_null_entry = None - for entry in nbest: - total_scores.append(entry.start_logit + entry.end_logit) - if not best_non_null_entry: - if entry.text: - best_non_null_entry = entry - probs = _compute_softmax(total_scores) - - nbest_json = [] - for i, entry in enumerate(nbest): - output = collections.OrderedDict() - output["text"] = entry.text - output["probability"] = probs[i] - output["start_logit"] = entry.start_logit - output["end_logit"] = entry.end_logit - nbest_json.append(output) - - assert len(nbest_json) >= 1 - all_predictions[example.qas_id] = nbest_json[0]["text"] - return (all_predictions, label) - - def __call__(self, sample): - """Call the get_postprocess_result.""" - return self.get_postprocess_result(sample) - - -@transform_registry(transform_type="ModelZooCollect", process="postprocess", framework="tensorflow, tensorflow_itex") -class TFModelZooCollectTransform(CollectTransform): - """Postprocess the predictions of model zoo, collect data.""" - - def __call__(self, sample): - """Collect postprocess data.""" - all_results, label = sample - all_results = zip(all_results[0], all_results[1]) - for start_logits, end_logits in all_results: - if len(self.unique_id) < self.length: - self.unique_id.append(self.idx) - self.start_logits.append(start_logits) - self.end_logits.append(end_logits) - self.idx += 1 - if len(self.unique_id) == self.length: - self.all_sample = ([self.unique_id, self.start_logits, self.end_logits], label) - return self.all_sample - - -@transform_registry( - transform_type="SquadV1ModelZoo", - process="postprocess", - framework="tensorflow, \ - tensorflow_itex", -) -class TFSquadV1ModelZooPostTransform(TFSquadV1PostTransform): - """Postprocess the predictions of bert on SQuADV1.1. - - See class TFSquadV1PostTransform for more details - """ - - def __init__( - self, - label_file, - vocab_file, - n_best_size=20, - max_seq_length=384, - max_query_length=64, - max_answer_length=30, - do_lower_case=True, - doc_stride=128, - ): - """Initialize `TFSquadV1ModelZooPostTransform` class.""" - super().__init__( - label_file, - vocab_file, - n_best_size, - max_seq_length, - max_query_length, - max_answer_length, - do_lower_case, - doc_stride, - ) - self.length = len(self.eval_features) - self.collect_data = TFModelZooCollectTransform(length=self.length) - - def __call__(self, sample): - """Collect data and get postprocess results.""" - sample = self.collect_data(sample) - return self.get_postprocess_result(sample) - - -@transform_registry(transform_type="ParseDecodeVoc", process="preprocess", framework="tensorflow, tensorflow_itex") -class ParseDecodeVocTransform(BaseTransform): - """Parse features in Example proto. - - Returns: - tuple of parsed image and labels - """ - - # fmt: off - def __call__(self, sample): - """Parse decode voc.""" - # Currently only supports jpeg and png. - # Need to use this logic because the shape is not known for - # tf.image.decode_image and we rely on this info to - # extend label if necessary. - # fmt: on - def _decode_image(content, channels): - """Decode the image with content.""" - return tf.cond( - tf.image.is_jpeg(content), - lambda: tf.image.decode_jpeg(content, channels), - lambda: tf.image.decode_png(content, channels)) - - features = { - 'image/encoded': - tf.compat.v1.FixedLenFeature((), tf.string, default_value=''), - 'image/filename': - tf.compat.v1.FixedLenFeature((), tf.string, default_value=''), - 'image/format': - tf.compat.v1.FixedLenFeature((), tf.string, default_value='jpeg'), - 'image/height': - tf.compat.v1.FixedLenFeature((), tf.int64, default_value=0), - 'image/width': - tf.compat.v1.FixedLenFeature((), tf.int64, default_value=0), - 'image/segmentation/class/encoded': - tf.compat.v1.FixedLenFeature((), tf.string, default_value=''), - 'image/segmentation/class/format': - tf.compat.v1.FixedLenFeature((), tf.string, default_value='png'), - } - - parsed_features = tf.compat.v1.parse_single_example(sample, features) - - image = _decode_image(parsed_features['image/encoded'], channels=3) - - label = None - label = _decode_image( - parsed_features['image/segmentation/class/encoded'], channels=1) - - sample = { - 'image': image, - } - - label.set_shape([None, None, 1]) - - sample['labels_class'] = label - - return sample['image'], sample['labels_class'] diff --git a/neural_compressor/experimental/distillation.py b/neural_compressor/experimental/distillation.py deleted file mode 100644 index 1519626eb34..00000000000 --- a/neural_compressor/experimental/distillation.py +++ /dev/null @@ -1,513 +0,0 @@ -"""Distillation class.""" - -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import copy - -from deprecated import deprecated - -from neural_compressor.compression.distillation.criterions import Criterions -from neural_compressor.experimental.common import Optimizers - -from ..adaptor import FRAMEWORKS -from ..conf.config import DistillationConf -from ..conf.pythonic_config import Config, DotDict -from ..model import BaseModel -from ..utils import logger -from ..utils.create_obj_from_config import create_dataloader, create_eval_func, create_train_func -from .common import Model -from .component import Component - - -@deprecated(version="2.0") -class Distillation(Component): - """Distillation class derived from Component class. - - Distillation class abstracted the pipeline of knowledge distillation, - transfer the knowledge of the teacher model to the student model. - - Args: - conf_fname_or_obj (string or obj): The path to the YAML configuration file or - Distillation_Conf containing accuracy goal, distillation objective and related - dataloaders etc. - - Attributes: - _epoch_ran: A integer indicating how much epochs ran. - eval_frequency: The frequency for doing evaluation of the student model - in terms of epoch. - best_score: The best metric of the student model in the training. - best_model: The best student model found in the training. - """ - - def __init__(self, conf_fname_or_obj=None): - """Initialize the attributes.""" - super(Distillation, self).__init__() - if isinstance(conf_fname_or_obj, DistillationConf): - self.conf = conf_fname_or_obj - elif isinstance(conf_fname_or_obj, Config): - self.conf = DistillationConf() - self.conf.map_pyconfig_to_cfg(conf_fname_or_obj) - else: - self.conf = DistillationConf(conf_fname_or_obj) - self._init_with_conf() - - self._teacher_model = None - self._criterion = None - self._optimizer = None - self._epoch_ran = 0 - self.eval_frequency = 1 - self.best_score = 0 - self.best_model = None - self._train_cfg = None - - def _on_train_begin(self, dataloader=None): - """Operations called on the beginning of the training. - - Called before training, evaluate the teacher model and the student model. - """ - assert self._model, "student_model must be set." - if self._eval_func is not None: - if self.teacher_model: - score = self._eval_func( - self.teacher_model if getattr(self._eval_func, "builtin", None) else self.teacher_model.model - ) - logger.info("teacher model score is {}.".format(str(score))) - - score = self._eval_func(self._model if getattr(self._eval_func, "builtin", None) else self._model.model) - logger.info("initial model score is {}.".format(str(score))) - if self.eval_frequency > 0: - self.best_score = score - if self.framework == "pytorch": - self.best_model = copy.deepcopy(self._model) - else: - self.best_model = self._model - - def _on_step_begin(self, batch_id): - """Operations called on the beginning of batches.""" - if self.criterion is not None and hasattr(self.criterion, "clear_features"): - self.criterion.clear_features() - - def _on_after_compute_loss(self, input, student_output, student_loss, teacher_output=None): - """Set or compute output of teacher model. - - Called after student model forward, calculate the output of the teacher model - with the same input of the student model. - - Args: - input (tensor or list or dict): The input of the student model. - student_output (tensor): The output logits of the student model. - student_loss (tensor or float): The original loss of the student model. - teacher_output (tensor, optional): The output logits of the teacher model. - """ - if self.criterion is None: - self.create_criterion() - assert self.criterion, "criterion must be set in yaml config file." - if teacher_output is None: - assert self.teacher_model, "teacher_model must be set." - teacher_output = self.criterion.teacher_model_forward(input, teacher_model=self.teacher_model._model) - return self.criterion.loss_cal_sloss(student_output, teacher_output, student_loss) - - def on_post_forward(self, input, teacher_output=None): # pragma: no cover - """Set or compute output of teacher model. - - Deprecated. - """ - assert False, ( - "This method is deprecated. please use `on_after_compute_loss` instead." - "on_after_compute_loss(input, student_output, student_loss, teacher_output=None)" - ) - - def _on_epoch_end(self): - """Operations called on the end of every epochs. - - Called on the end of every epochs, evaluate the student model - and record the best one regularly. - """ - self._epoch_ran += 1 - if self._eval_func is not None and self.eval_frequency > 0 and self._epoch_ran % self.eval_frequency == 0: - score = self._eval_func(self._model if getattr(self._eval_func, "builtin", None) else self._model.model) - logger.info("model score of epoch {} is {}.".format(self._epoch_ran, str(score))) - if ( - isinstance(score, list) and all([s > b_s for s, b_s in zip(score, self.best_score)]) - ) or score > self.best_score: - self.best_score = score - if self.framework == "pytorch": - self.best_model = copy.deepcopy(self._model) - else: - self.best_model = self._model - - def init_train_cfg(self): - """Initialize the training configuration.""" - if self._train_cfg is None: - # train section of distillation section in yaml file should be configured. - self._train_cfg = self.cfg.distillation.train - assert self._train_cfg, ( - "train field of distillation section in yaml file must " - "be configured for distillation if train_func is NOT set." - ) - - def create_criterion(self): - """Create the criterion for training.""" - self.init_train_cfg() - if self.criterion is None: - assert "criterion" in self._train_cfg.keys(), ( - "criterion part in train field of distillation section in yaml file " - "must be configured for distillation if criterion is NOT set." - ) - - if isinstance(self._train_cfg.criterion, DotDict): - criterion_cfg = self._train_cfg.criterion - else: - criterion_cfg = self._train_cfg.criterion.config - - assert ( - len(criterion_cfg) == 1 - ), "There must be exactly one loss in " "criterion part, instead got {} loss.".format(len(criterion_cfg)) - loss = [i for i in criterion_cfg.keys()][0] - loss_cfg = criterion_cfg[loss] - criterion_builder = Criterions(self.framework)[loss](loss_cfg) - criterion_tuple = criterion_builder() - if self.teacher_model and self.student_model: - if self.framework == "tensorflow": # new, for tf - teacher_model = self.teacher_model._model - student_model = self.student_model._model - else: # for pytorch and other frameworks - teacher_model = self.teacher_model.model - student_model = self.student_model.model - criterion_tuple[1]["student_model"] = student_model - criterion_tuple[1]["teacher_model"] = teacher_model - self.criterion = criterion_tuple[0](**criterion_tuple[1]) - else: - logger.warning("Use user defined criterion, " "ignoring the criterion setting in yaml file.") - - self._train_cfg.criterion = self.criterion - - def create_optimizer(self): - """Create the optimizer for training.""" - self.init_train_cfg() - if self.optimizer is None: - assert "optimizer" in self._train_cfg.keys(), ( - "optimizer part in train field of distillation section in yaml file " - "must be configured for distillation if optimizer is NOT set." - ) - optimizer_cfg = self._train_cfg.optimizer - assert ( - len(optimizer_cfg) == 1 - ), "There must be exactly one optimizer in " "optimizer part, instead got {} optimizer.".format( - len(optimizer_cfg) - ) - optimizer_name = list(optimizer_cfg.keys())[0] - optimizer_cfg_ = optimizer_cfg[optimizer_name] - optimizer_builder = Optimizers(self.framework)[optimizer_name](optimizer_cfg_) - optimizer_tuple = optimizer_builder() - if self.framework == "tensorflow": - self.optimizer = optimizer_tuple[0](**optimizer_tuple[1]) - elif self.framework == "pytorch": - # pylint: disable=no-member - self.optimizer = optimizer_tuple[0](self.model.model.parameters(), **optimizer_tuple[1]) - else: - logger.warning("Use user defined optimizer, " "ignoring the optimizer setting in yaml file.") - - self._train_cfg.optimizer = self.optimizer - - def prepare(self): - """Prepare hooks.""" - self.generate_hooks() - self.create_criterion() - - def pre_process(self): - """Preprocessing before the disillation pipeline. - - Initialize necessary parts for distillation pipeline. - """ - framework_specific_info = { - "device": self.cfg.device, - "random_seed": self.cfg.tuning.random_seed, - "workspace_path": self.cfg.tuning.workspace.path, - "q_dataloader": None, - "format": "default", - "backend": "default", - } - - if self.framework == "tensorflow": - framework_specific_info.update({"inputs": self.cfg.model.inputs, "outputs": self.cfg.model.outputs}) - - self.adaptor = FRAMEWORKS[self.framework](framework_specific_info) - - self.generate_hooks() - assert isinstance(self._model, BaseModel), "need set neural_compressor Model for distillation...." - - if ( - self._train_dataloader is None - and self._train_func is None - and self.cfg.distillation.train.dataloader is not None - ): - train_dataloader_cfg = self.cfg.distillation.train.dataloader - - self._train_dataloader = create_dataloader(self.framework, train_dataloader_cfg) - - if ( - self.cfg.evaluation - and self.cfg.evaluation.accuracy - and self.cfg.evaluation.accuracy.dataloader - and self._eval_dataloader is None - and self._eval_func is None - ): - eval_dataloader_cfg = self.cfg.evaluation.accuracy.dataloader - assert eval_dataloader_cfg is not None, ( - "dataloader field of evaluation " - "in yaml file should be configured as eval_dataloader property is NOT set!" - ) - - self._eval_dataloader = create_dataloader(self.framework, eval_dataloader_cfg) - - if self._train_func is None: - if self.criterion is None: - self.create_criterion() - self.create_optimizer() - if self._train_dataloader is not None: - self._train_func = create_train_func( - self.framework, self.train_dataloader, self.adaptor, self._train_cfg, hooks=self.hooks - ) - if self.cfg.evaluation and self.eval_dataloader and self._eval_func is None: - # eval section in yaml file should be configured. - eval_cfg = self.cfg.evaluation - assert eval_cfg, ( - "eval field of distillation section in yaml file must " - "be configured for distillation if eval_func is NOT set." - ) - self._eval_func = create_eval_func( - self.framework, - self.eval_dataloader, - self.adaptor, - eval_cfg.accuracy.metric, - eval_cfg.accuracy.postprocess, - fp32_baseline=False, - ) - - def execute(self): - """Do distillation pipeline. - - First train the student model with the teacher model, after training, - evaluating the best student model if any. - - Returns: - Best distilled model found. - """ - self._train_func(self._model if getattr(self._train_func, "builtin", None) else self._model.model) - if self.criterion is not None and hasattr(self.criterion, "remove_all_hooks"): - self.criterion.remove_all_hooks() - logger.info("Model distillation is done.") - if self._eval_func is not None: - logger.info("Start to evaluate the distilled model.") - self._model = self.best_model if self.best_model else self._model - score = self._eval_func(self._model if getattr(self._eval_func, "builtin", None) else self._model.model) - - logger.info("distilled model score is {}.".format(str(score))) - return self._model - - def generate_hooks(self): - """Register hooks for distillation. - - Register necessary hooks for distillation pipeline. - """ - self.register_hook("on_train_begin", self._on_train_begin) - self.register_hook("on_step_begin", self._on_step_begin) - self.register_hook("on_after_compute_loss", self._on_after_compute_loss) - self.register_hook("on_epoch_end", self._on_epoch_end) - - def __call__(self): - """Do distillation workflow. - - This interface currently only works on pytorch - and provides three usages: - a) Fully yaml configuration: User specifies all the info through yaml, - including dataloaders used in training and evaluation phases - and distillation settings. - - For this usage, only student_model and teacher_model parameter is mandatory. - - b) Partial yaml configuration: User specifies dataloaders used in training - and evaluation phase by code. - The tool provides built-in dataloaders and evaluators, user just need provide - a dataset implemented __iter__ or __getitem__ methods and invoke dataloader() - with dataset as input parameter to create neural_compressor dataloader before calling this - function. - - After that, User specifies fp32 "model", training dataset "train_dataloader" - and evaluation dataset "eval_dataloader". - - For this usage, student_model, teacher_model, train_dataloader and eval_dataloader - parameters are mandatory. - - c) Partial yaml configuration: User specifies dataloaders used in training phase - by code. - This usage is quite similar with b), just user specifies a custom "eval_func" - which encapsulates the evaluation dataset by itself. - The trained and distilled model is evaluated with "eval_func". - The "eval_func" tells the tuner whether the distilled model meets - the accuracy criteria. If not, the Tuner starts a new training and tuning flow. - - For this usage, student_model, teacher_model, train_dataloader and eval_func - parameters are mandatory. - - Returns: - distilled model: best distilled model found, otherwise return None - """ - return super(Distillation, self).__call__() - - fit = __call__ - - @property - def criterion(self): - """Getter of criterion. - - Returns: - The criterion used in the distillation process. - """ - return self._criterion - - @criterion.setter - def criterion(self, user_criterion): - """Setter of criterion used in the distillation process. - - Set the user defined criterion. When using built-in train_func, user can - specify the customized criterion through this setter. - - Args: - user_criterion (criterion object): User defined criterion. - """ - self._criterion = user_criterion - - @property - def optimizer(self): - """Getter of optimizer. - - Returns: - The optimizer used in the distillation process. - """ - return self._optimizer - - @optimizer.setter - def optimizer(self, user_optimizer): - """Setter of optimizer used in the distillation process. - - Set the user defined optimizer. When using built-in train_func, user can - specify the customized optimizer through this setter. - - Args: - user_optimizer (criterion object): User defined optimizer. - """ - self._optimizer = user_optimizer - - @property - def teacher_model(self): - """Getter of the teacher model. - - Returns: - The teacher model used in the distillation process. - """ - return self._teacher_model - - @teacher_model.setter - def teacher_model(self, user_model): - """Set the user model and dispatch to framework specific internal model object. - - Args: - user_model: user are supported to set model from original framework model format - (eg, tensorflow frozen_pb or path to a saved model), - but not recommended. Best practice is to set from a initialized - neural_compressor.experimental.common.Model. - If tensorflow model is used, model's inputs/outputs will be - auto inferenced, but sometimes auto inferenced - inputs/outputs will not meet your requests, - set them manually in config yaml file. - Another corner case is slim model of tensorflow, - be careful of the name of model configured in yaml file, - make sure the name is in supported slim model list. - """ - if not isinstance(user_model, BaseModel): - logger.warning("Force convert framework model to neural_compressor model.") - self._teacher_model = Model(user_model) - else: - self._teacher_model = user_model - - @property - def student_model(self): - """Getter of the student model. - - Returns: - The student model used in the distillation process. - """ - return self._model - - @student_model.setter - def student_model(self, user_model): - """Set the user model and dispatch to framework specific internal model object. - - Args: - user_model: user are supported to set model from original framework model format - (eg, tensorflow frozen_pb or path to a saved model), - but not recommended. Best practice is to set from a initialized - neural_compressor.experimental.common.Model. - If tensorflow model is used, model's inputs/outputs will be - auto inferenced, but sometimes auto inferenced - inputs/outputs will not meet your requests, - set them manually in config yaml file. - Another corner case is slim model of tensorflow, - be careful of the name of model configured in yaml file, - make sure the name is in supported slim model list. - """ - if not isinstance(user_model, BaseModel): - logger.warning("Force convert framework model to neural_compressor model.") - self._model = Model(user_model) - else: - self._model = user_model - - @property - def train_cfg(self): - """Getter of the train configuration. - - Returns: - The train configuration used in the distillation process. - """ - return self._train_cfg - - @property - def evaluation_distributed(self): - """Getter to know whether need distributed evaluation dataloader.""" - return self._evaluation_distributed - - @evaluation_distributed.setter - def evaluation_distributed(self, distributed): - """Setter to know whether need distributed evaluation dataloader.""" - self._evaluation_distributed = distributed - - @property - def train_distributed(self): - """Getter to know whether need distributed training dataloader.""" - return self._train_distributed - - @train_distributed.setter - def train_distributed(self, distributed): - """Setter to know whether need distributed training dataloader.""" - self._train_distributed = distributed - - def __repr__(self): - """Class representation.""" - return "Distillation" diff --git a/neural_compressor/experimental/export/__init__.py b/neural_compressor/experimental/export/__init__.py deleted file mode 100644 index 56c3604ffe8..00000000000 --- a/neural_compressor/experimental/export/__init__.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Intel Neural Compressor Export.""" - -from .torch2onnx import torch_to_fp32_onnx, torch_to_int8_onnx -from .qlinear2qdq import onnx_qlinear_to_qdq -from .tf2onnx import tf_to_fp32_onnx, tf_to_int8_onnx diff --git a/neural_compressor/experimental/export/qlinear2qdq.py b/neural_compressor/experimental/export/qlinear2qdq.py deleted file mode 100644 index d9edce935cd..00000000000 --- a/neural_compressor/experimental/export/qlinear2qdq.py +++ /dev/null @@ -1,88 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# pragma: no cover -"""Helper functions to export onnx model from QLinearops to QDQ.""" -from deprecated import deprecated - -from neural_compressor.adaptor.ox_utils.util import find_by_name -from neural_compressor.utils import logger -from neural_compressor.utils.utility import LazyImport - -numpy_helper = LazyImport("onnx.numpy_helper") - - -@deprecated(version="2.0") -def check_model(model): - """Check optype for input model. - - Args: - model (ModelProto): onnx model. - """ - has_integerop = False - has_qlinearop = False - for node in model.graph.node: - if node.op_type.endswith("Integer"): - has_integerop = True - elif node.op_type.startswith("QLinear"): - has_qlinearop = True - elif node.op_type in ["QAttention", "QGemm", "QEmbedLayerNormalization"]: - has_qlinearop = True - elif node.op_type in ["Gather"]: - input_data = find_by_name(node.input[0], model.graph.initializer) - if input_data is not None and numpy_helper.to_array(input_data).dtype in ["int8", "uint8"]: - has_qlinearop = True - if has_integerop: - logger.info("This model has Integer ops, these ops will be skipped.") - if has_qlinearop: - return True - else: - logger.info("This model has no QLinear ops, save the original model.") - return False - - -@deprecated(version="2.0") -def onnx_qlinear_to_qdq( - model, - input_name_to_nodes, -): - """Export ONNX QLinearops model into QDQ model. - - Args: - model (ModelProto): int8 onnx model. - input_name_to_nodes (dict): the mapping of tensor name and its destination nodes. - """ - from neural_compressor.adaptor.ox_utils.operators import QOPERATORS - - add_nodes = [] - remove_nodes = [] - inits = [] - if check_model(model): - for node in model.graph.node: - if node.op_type in QOPERATORS: - if node.output[0] not in input_name_to_nodes: - continue - children = [] - for out in node.output: - children.extend(input_name_to_nodes[node.output[0]]) - converter = QOPERATORS[node.op_type](node, children, model.graph.initializer) - done, add_node, init = converter.convert() - if done: - add_nodes.extend(add_node) - inits.extend(init) - remove_nodes.append(node) - return add_nodes, remove_nodes, inits diff --git a/neural_compressor/experimental/export/tf2onnx.py b/neural_compressor/experimental/export/tf2onnx.py deleted file mode 100644 index 111d203f083..00000000000 --- a/neural_compressor/experimental/export/tf2onnx.py +++ /dev/null @@ -1,125 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2022 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# pragma: no cover -"""Helper functions to export model from TensorFlow to ONNX.""" - -import re - -from deprecated import deprecated - -from neural_compressor.utils import logger -from neural_compressor.utils.utility import LazyImport - -t2o = LazyImport("tf2onnx") - - -@deprecated(version="2.0") -def _split_nodename_and_shape(name): - """Split input name with shape into name and shape.""" - # pattern for a node name - inputs = [] - shapes = {} - # input takes in most cases the format name:0, where 0 is the output number - # in some cases placeholders don't have a rank which onnx can't handle so we let uses override the shape - # by appending the same, ie : [1,28,28,3] - name_pattern = r"(?:([\w\d/\-\._:]+)(\[[\-\d,]+\])?),?" - splits = re.split(name_pattern, name) - for i in range(1, len(splits), 3): - inputs.append(splits[i] + ":0") - if splits[i + 1] is not None: - shape = [int(n) for n in splits[i + 1][1:-1].split(",")] - shape = [n if n >= 0 else None for n in shape] - shapes[splits[i] + ":0"] = shape - if not shapes: - shapes = None - return inputs, shapes - - -@deprecated(version="2.0") -def tf_to_fp32_onnx(graph_def, save_path, opset_version=14, input_names=None, output_names=None, inputs_as_nchw=None): - """Export FP32 Tensorflow model into FP32 ONNX model using tf2onnx tool. - - Args: - graph_def (graph_def to convert): fp32 graph_def. - save_path (str): save path of ONNX model. - opset_version (int, optional): opset version. Defaults to 14. - input_names (list, optional): input names. Defaults to None. - output_names (list, optional): output names. Defaults to None. - inputs_as_nchw (list, optional): transpose the input. Defaults to None. - """ - shape_override = None - if isinstance(input_names, str): - input_names, shape_override = _split_nodename_and_shape(input_names) - else: - input_names[:] = [o + ":0" for o in input_names] - output_names[:] = [o + ":0" for o in output_names] - t2o.convert.from_graph_def( - graph_def=graph_def, - input_names=input_names, - output_names=output_names, - inputs_as_nchw=inputs_as_nchw, - shape_override=shape_override, - opset=opset_version, - output_path=save_path, - ) - info = "The FP32 ONNX Model exported to path: {0}".format(save_path) - logger.info("*" * len(info)) - logger.info(info) - logger.info("*" * len(info)) - - -@deprecated(version="2.0") -def tf_to_int8_onnx( - int8_model, save_path, opset_version: int = 14, input_names=None, output_names=None, inputs_as_nchw=None -): - """Export INT8 Tensorflow model into INT8 ONNX model. - - Args: - int8_model (tensorflow ITEX QDQ model): int8 model. - save_path (str): save path of ONNX model. - opset_version (int, optional): opset version. Defaults to 14. - input_names (list, optional): input names. Defaults to None. - output_names (list, optional): output names. Defaults to None. - inputs_as_nchw (list, optional): transpose the input. Defaults to None. - """ - shape_override = None - if isinstance(input_names, str): - input_names, shape_override = _split_nodename_and_shape(input_names) - else: - input_names[:] = [o + ":0" for o in input_names] - output_names[:] = [o + ":0" for o in output_names] - onnx_convert_graph = "./converted_graph.onnx" - from neural_compressor.adaptor.tf_utils.tf2onnx_converter import TensorflowQDQToOnnxQDQConverter - - TensorflowQDQToOnnxQDQConverter( - int8_model, input_names, output_names, shape_override, inputs_as_nchw, opset_version - ).convert(onnx_convert_graph) - - import onnxruntime as ort - - sess_options = ort.SessionOptions() - sess_options.graph_optimization_level = ort.GraphOptimizationLevel.ORT_ENABLE_ALL - sess_options.optimized_model_filepath = save_path - import onnx - - model = onnx.load(onnx_convert_graph) - ort.InferenceSession(model.SerializeToString(), sess_options) - info = "The INT8 ONNX Model is exported to path: {0}".format(save_path) - logger.info("*" * len(info)) - logger.info(info) - logger.info("*" * len(info)) diff --git a/neural_compressor/experimental/export/torch2onnx.py b/neural_compressor/experimental/export/torch2onnx.py deleted file mode 100644 index 143b5dab5a8..00000000000 --- a/neural_compressor/experimental/export/torch2onnx.py +++ /dev/null @@ -1,434 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# pragma: no cover -"""Helper functions to export model from PyTorch/TensorFlow to ONNX.""" - -import os -from collections import UserDict - -from deprecated import deprecated - -from neural_compressor.adaptor.torch_utils.util import input2tuple -from neural_compressor.utils import logger -from neural_compressor.utils.utility import LazyImport - -torch = LazyImport("torch") -onnx = LazyImport("onnx") -ort = LazyImport("onnxruntime") -ortq = LazyImport("onnxruntime.quantization") - - -@deprecated(version="2.0") -def _prepare_inputs(pt_model, input_names, example_inputs): - """Prepare input_names and example_inputs.""" - if isinstance(example_inputs, dict) or isinstance(example_inputs, UserDict): - input_names = input_names or list(example_inputs.keys()) - if isinstance(example_inputs, UserDict): - example_inputs = dict(example_inputs) - # match input_names with inspected input_order, especially for bert in hugginface. - elif input_names and len(input_names) > 1: - import inspect - - input_order = inspect.signature(pt_model.forward).parameters.keys() - flag = [name in input_order for name in input_names] # whether should be checked - if all(flag): - new_input_names = [] - new_example_inputs = [] - for name in input_order: - if name in input_names: - new_input_names.append(name) - id = input_names.index(name) - new_example_inputs.append(example_inputs[id]) - input_names = new_input_names - example_inputs = new_example_inputs - example_inputs = input2tuple(example_inputs) - return input_names, example_inputs - - -@deprecated(version="2.0") -def get_node_mapping( - fp32_model, - fp32_onnx_path, -): - """Get PyTorch module and ONNX node mapping. - - Args: - fp32_model (torch.nn.Module): quantization configuration from PyTorch. - fp32_onnx_path (str): path to fp32 onnx model. - - Returns: - module_node_mapping: op mapping from PyTorch to ONNX. - """ - - def check_data(op_type, data, module_dict): - for name, value in module_dict.items(): - if value.shape == data.shape: - if (value == data).all(): - module_dict.pop(name) - return name - return None - - module_dict = {} - for name, module in fp32_model.named_modules(): - if ( - "Conv" in str(module.__class__.__name__) - or "Embedding" in str(module.__class__.__name__) - or "Linear" in str(module.__class__.__name__) - ): - if hasattr(module, "weight"): - value = module.weight.detach().cpu().numpy() - module_dict[name] = value - - module_node_mapping = {} - fp32_onnx_model = onnx.load(fp32_onnx_path) - initializer_data = {tensor.name: tensor for tensor in fp32_onnx_model.graph.initializer} - from onnx import numpy_helper - - for node in fp32_onnx_model.graph.node: - if node.op_type in op_types_to_quantize: - if node.op_type == "MatMul" and node.input[1] in initializer_data: - data = numpy_helper.to_array(initializer_data[node.input[1]]).T - elif node.op_type == "Gather" and node.input[0] in initializer_data: - data = numpy_helper.to_array(initializer_data[node.input[0]]) - elif node.op_type in ["Gemm"]: - data = numpy_helper.to_array(initializer_data[node.input[1]]) - else: # pragma: no cover - continue - pt_name = check_data(node.op_type, data, module_dict) - if pt_name: - module_node_mapping[pt_name] = node.name - return module_node_mapping - - -@deprecated(version="2.0") -def get_quantizable_onnx_ops(int8_model, module_node_mapping): - """Get quantizable onnx ops. - - Args: - int8_model (torch.nn.Module): PyTorch int8 model. - module_node_mapping (dict): op mapping from PyTorch to ONNX. - - Returns: - quantize_nodes: all onnx node that should be quantized. - """ - quantize_nodes = [] - for name, module in int8_model.named_modules(): - if ( - "Conv" in str(module.__class__.__name__) - or "Embedding" in str(module.__class__.__name__) - or "Linear" in str(module.__class__.__name__) - ): - if hasattr(module, "weight") and callable(module.weight): - if module.weight().dtype in [torch.qint8, torch.quint8]: - if name.split(".module")[0] in module_node_mapping: - node = module_node_mapping[name.split(".module")[0]] - quantize_nodes.append(node) - return quantize_nodes - - -@deprecated(version="2.0") -def dynamic_quant_export( - pt_fp32_model, - pt_int8_model, - save_path, - example_inputs, - q_config, - opset_version, - dynamic_axes, - input_names, - output_names, - weight_type, -): - """Export dynamic quantized model. - - Args: - pt_fp32_model (torch.nn.module): PyTorch FP32 model. - pt_int8_model (torch.nn.module): PyTorch INT8 model. - save_path (str): save path of ONNX model. - example_inputs (dict|list|tuple|torch.Tensor): used to trace torch model. - q_config (dict): containing quantization configuration. - opset_version (int, optional): opset version. Defaults to 14. - dynamic_axes (dict, optional): dynamic axes. Defaults to - {"input": {0: "batch_size"}, "output": {0: "batch_size"}}. - input_names (dict, optional): input names. Defaults to None. - output_names (dict, optional): output names. Defaults to None. - weight_type (str, optional): data types of weight of ONNX model - (only needed for exporting dynamic quantized model). Defaults to 'S8'. - """ - global op_types_to_quantize - op_types_to_quantize = ["MatMul", "Gemm", "Gather"] - - # pylint: disable=E1101 - fp32_onnx_path = save_path + ".tmp" if save_path else "int8-model.onnx.tmp" - torch_to_fp32_onnx( - pt_fp32_model, - fp32_onnx_path, - example_inputs, - opset_version=opset_version, - input_names=input_names, - output_names=output_names, - dynamic_axes=dynamic_axes, - verbose=False, - ) - - module_node_mapping = get_node_mapping(pt_fp32_model, fp32_onnx_path) - quantize_nodes = get_quantizable_onnx_ops(pt_int8_model, module_node_mapping) - - REDUCE_RANGE = q_config["reduce_range"] - if REDUCE_RANGE: - logger.info("Reduce range is {}".format(str(REDUCE_RANGE))) - - logger.info("Quantization format is not available when executing dynamic quantization.") - - if weight_type.upper() == "S8": - weight_type = ortq.QuantType.QInt8 - elif weight_type.upper() == "U8": - weight_type = ortq.QuantType.QUInt8 - else: - assert False, "Right now, we don't support weight type: {}, " "please use S8/U8.".format(weight_type) - - ortq.quantize_dynamic( - fp32_onnx_path, - save_path, - per_channel=True, - reduce_range=REDUCE_RANGE, - weight_type=weight_type, - nodes_to_quantize=quantize_nodes, - nodes_to_exclude=[], - extra_options={}, - ) - - os.remove(fp32_onnx_path) - - -@deprecated(version="2.0") -def static_quant_export( - pt_int8_model, - save_path, - example_inputs, - q_config, - opset_version, - dynamic_axes, - input_names, - output_names, - quant_format, -): - """Export static quantized model. - - Args: - pt_int8_model (torch.nn.module): PyTorch INT8 model. - save_path (str): save path of ONNX model. - example_inputs (dict|list|tuple|torch.Tensor): used to trace torch model. - q_config (dict): containing quantization configuration. - opset_version (int, optional): opset version. Defaults to 14. - dynamic_axes (dict, optional): dynamic axes. Defaults to - {"input": {0: "batch_size"}, "output": {0: "batch_size"}}. - input_names (dict, optional): input names. Defaults to None. - output_names (dict, optional): output names. Defaults to None. - quant_format (str, optional): _quantization format of ONNX model. Defaults to 'QDQ'. - """ - input_names, example_inputs = _prepare_inputs(pt_int8_model, input_names, example_inputs) - - def model_wrapper(model_fn): - # export doesn't support a dictionary output, so manually turn it into a tuple - # refer to https://discuss.tvm.apache.org/t/how-to-deal-with-prim-dictconstruct/11978 - def wrapper(*args, **kwargs): - output = model_fn(*args, **kwargs) - if isinstance(output, dict): - return tuple(v for v in output.values() if v is not None) - else: - return output - - return wrapper - - pt_int8_model.forward = model_wrapper(pt_int8_model.forward) - - with torch.no_grad(): - try: - torch.onnx.export( - pt_int8_model, - input2tuple(example_inputs), - save_path, - opset_version=opset_version, - input_names=input_names, - output_names=output_names, - dynamic_axes=dynamic_axes, - ) - except TypeError: - config_name = ( - "QuantizationAwareTrainingConfig" - if q_config["approach"] == "quant_aware_training" - else "PostTrainingQuantConfig" - ) - logger.error( - "Export failed, possibly because unsupported quantized ops. Check " - "neural-compressor/docs/source/export.md#supported-quantized-ops " - "for supported ops." - ) - logger.error( - "Please fallback unsupported quantized ops by setting 'op_type_dict' or " - "'op_name_dict' in '{}' config. ".format(config_name) - ) - raise TypeError("Export failed with TypeError.") - except Exception as e: - raise e - - if quant_format != "QDQ": - sess_options = ort.SessionOptions() - sess_options.graph_optimization_level = ort.GraphOptimizationLevel.ORT_ENABLE_EXTENDED - sess_options.optimized_model_filepath = save_path - ort.InferenceSession(save_path, sess_options) - - -@deprecated(version="2.0") -def torch_to_fp32_onnx( - pt_fp32_model, - save_path, - example_inputs, - opset_version=14, - dynamic_axes={"input": {0: "batch_size"}, "output": {0: "batch_size"}}, - input_names=None, - output_names=None, - do_constant_folding=True, - verbose=True, -): - """Export FP32 PyTorch model into FP32 ONNX model. - - Args: - pt_fp32_model (torch.nn.module): PyTorch FP32 model. - save_path (str): save path of ONNX model. - example_inputs (dict|list|tuple|torch.Tensor): used to trace torch model. - opset_version (int, optional): opset version. Defaults to 14. - dynamic_axes (dict, optional): dynamic axes. Defaults to - {"input": {0: "batch_size"}, "output": {0: "batch_size"}}. - input_names (dict, optional): input names. Defaults to None. - output_names (dict, optional): output names. Defaults to None. - do_constant_folding (bool, optional): do constant folding or not. Defaults to True. - verbose (bool, optional): dump verbose or not. Defaults to True. - """ - from neural_compressor.utils.pytorch import is_int8_model - - assert is_int8_model(pt_fp32_model) is False, ( - "The fp32 model is replaced during quantization. " - + "please customize a eval_func when quantizing, if not, such as `lambda x: 1`." - ) - - input_names, example_inputs = _prepare_inputs(pt_fp32_model, input_names, example_inputs) - - with torch.no_grad(): - torch.onnx.export( - pt_fp32_model, - example_inputs, - save_path, - opset_version=opset_version, - input_names=input_names, - output_names=output_names, - dynamic_axes=dynamic_axes, - do_constant_folding=do_constant_folding, - ) - - if verbose: - info = "The FP32 ONNX Model exported to path: {0}".format(save_path) - logger.info("*" * len(info)) - logger.info(info) - logger.info("*" * len(info)) - - -@deprecated(version="2.0") -def torch_to_int8_onnx( - pt_fp32_model, - pt_int8_model, - save_path, - example_inputs, - q_config, - opset_version=14, - dynamic_axes={"input": {0: "batch_size"}, "output": {0: "batch_size"}}, - input_names=None, - output_names=None, - quant_format: str = "QDQ", - weight_type: str = "S8", - verbose=True, -): - """Export INT8 PyTorch model into INT8 ONNX model. - - Args: - pt_fp32_model (torch.nn.module): PyTorch FP32 model. - pt_int8_model (torch.nn.module): PyTorch INT8 model. - save_path (str): save path of ONNX model. - example_inputs (dict|list|tuple|torch.Tensor): used to trace torch model. - q_config (dict): containing quantization configuration. - opset_version (int, optional): opset version. Defaults to 14. - dynamic_axes (dict, optional): dynamic axes. Defaults to - {"input": {0: "batch_size"}, "output": {0: "batch_size"}}. - input_names (dict, optional): input names. Defaults to None. - output_names (dict, optional): output names. Defaults to None. - quant_format (str, optional): _quantization format of ONNX model. Defaults to 'QDQ'. - weight_type (str, optional): data types of weight of ONNX model - (only needed for exporting dynamic quantized model). Defaults to 'S8'. - verbose (bool, optional): dump verbose or not. Defaults to True. - """ - from neural_compressor.utils.pytorch import is_int8_model - - assert is_int8_model(pt_int8_model), ( - "The exported model is not INT8 model, " "please reset 'dtype' to 'FP32' or check your model." - ) - - assert q_config is not None, "'q_config' is needed when export an INT8 model." - - quant_format = quant_format.upper() - if quant_format == "QDQ" and opset_version < 13: # pragma: no cover - opset_version = 13 - logger.warning( - "QDQ format requires opset_version >= 13, " + "we reset opset_version={} here".format(opset_version) - ) - - if q_config["approach"] == "post_training_dynamic_quant": - # dynamic quantization export follow these steps: - # "1. export FP32 PyTorch model to FP32 ONNX model. " - # "2. use FP32 ONNX model as the input model for post training dynamic quantization." - # TODO: will be removed once torch supports dynamic quantization export - dynamic_quant_export( - pt_fp32_model, - pt_int8_model, - save_path, - example_inputs, - q_config, - opset_version, - dynamic_axes, - input_names, - output_names, - weight_type, - ) - else: - static_quant_export( - pt_int8_model, - save_path, - example_inputs, - q_config, - opset_version, - dynamic_axes, - input_names, - output_names, - quant_format, - ) - - if verbose: - info = "The INT8 ONNX Model exported to path: {0}".format(save_path) - logger.info("*" * len(info)) - logger.info(info) - logger.info("*" * len(info)) diff --git a/neural_compressor/experimental/graph_optimization.py b/neural_compressor/experimental/graph_optimization.py deleted file mode 100644 index 5c7cc73f366..00000000000 --- a/neural_compressor/experimental/graph_optimization.py +++ /dev/null @@ -1,447 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Graph Optimization Entry.""" - -import os -import pickle -import random -import sys -import tempfile - -import numpy as np -import yaml -from deprecated import deprecated - -from ..conf.config import Graph_Optimization_Conf -from ..conf.dotdict import DotDict, deep_get, deep_set -from ..model import BaseModel -from ..model.model import get_model_fwk_name -from ..utils import logger -from ..utils.create_obj_from_config import create_dataloader -from ..utils.utility import CpuInfo, time_limit -from .common import Model as NCModel -from .strategy import EXP_STRATEGIES - - -@deprecated(version="2.0") -class Graph_Optimization: - """Graph_Optimization class. - - automatically searches for optimal quantization recipes for low - precision model inference, achieving best tuning objectives like inference performance - within accuracy loss constraints. - Tuner abstracts out the differences of quantization APIs across various DL frameworks - and brings a unified API for automatic quantization that works on frameworks including - tensorflow, pytorch and mxnet. - Since DL use cases vary in the accuracy metrics (Top-1, MAP, ROC etc.), loss criteria - (<1% or <0.1% etc.) and tuning objectives (performance, memory footprint etc.). - Tuner class provides a flexible configuration interface via YAML for users to specify - these parameters. - - Args: - conf_fname_or_obj (string or obj): The path to the YAML configuration file or - Graph_Optimization_Conf class containing accuracy goal, tuning objective and - preferred calibration & quantization tuning space etc. - """ - - def __init__(self, conf_fname_or_obj=None): - """Initialize `Graph Optimization` class.""" - self.conf_name = conf_fname_or_obj - self._model = None - self._eval_dataloader = None - self._eval_func = None - self._precisions = "fp32" - self._input = [] - self._output = [] - self.conf = None - - if isinstance(conf_fname_or_obj, Graph_Optimization_Conf): - self.conf = conf_fname_or_obj - else: - self.conf = Graph_Optimization_Conf(conf_fname_or_obj) - cfg = self.conf.usr_cfg - if cfg.model.framework != "NA": - self.framework = cfg.model.framework.lower() - - cfg.tuning.strategy.name = "automixedprecision" - seed = cfg.tuning.random_seed - random.seed(seed) - np.random.seed(seed) - - def __call__(self): - """Call graph optimization process. - - This interface works on all the DL frameworks that neural_compressor supports - and provides three usages: - a) Fully yaml configuration: User specifies all the info through yaml, - including dataloaders used in calibration and evaluation phases - and quantization tuning settings. - For this usage, only model parameter is mandatory. - b) Partial yaml configuration: User specifies dataloaders used in calibration - and evaluation phase by code. - The tool provides built-in dataloaders and evaluators, user just need provide - a dataset implemented __iter__ or __getitem__ methods and invoke dataloader() - with dataset as input parameter to create neural_compressor dataloader before calling this - function. - After that, User specifies fp32 "model", calibration dataset "calib_dataloader" - and evaluation dataset "eval_dataloader". - The calibrated and converted model is evaluated with "eval_dataloader" - with evaluation metrics specified in the configuration file. The evaluation tells - the tuner whether the converted model meets the accuracy criteria. If not, - the tuner starts a new calibration and tuning flow. - For this usage, model, calib_dataloader and eval_dataloader parameters are mandatory. - c) Partial yaml configuration: User specifies dataloaders used in calibration phase - by code. - This usage is quite similar with b), just user specifies a custom "eval_func" - which encapsulates the evaluation dataset by itself. - The calibrated and converted model is evaluated with "eval_func". - The "eval_func" tells the tuner whether the converted model meets - the accuracy criteria. If not, the Tuner starts a new calibration and tuning flow. - For this usage, model, calib_dataloader and eval_func parameters are mandatory. - - Returns: - converted model: best converted model found, otherwise return None - """ - assert isinstance(self._model, BaseModel), "need set your Model for quantization...." - - cfg = self.conf.usr_cfg - if self.framework == "tensorflow": - self._model.name = cfg.model.name - self._model.output_tensor_names = cfg.model.outputs - self._model.input_tensor_names = cfg.model.inputs - self._model.workspace_path = cfg.tuning.workspace.path - - if ( - "bf16" in self._precisions - or (cfg.mixed_precision and "bf16" in cfg.mixed_precision.precisions) - or (cfg.graph_optimization and "bf16" in cfg.graph_optimization.precisions) - ): - cfg.use_bf16 = True - else: - logger.warning("Only TensorFlow graph optimization is supported at current stage.") - sys.exit(0) - - # when eval_func is set, will be directly used and eval_dataloader can be None - if self._eval_func is None: - if self._eval_dataloader is None: - eval_dataloader_cfg = deep_get(cfg, "evaluation.accuracy.dataloader") - if eval_dataloader_cfg is None: - self._eval_func = None - else: - self._eval_dataloader = create_dataloader(self.framework, eval_dataloader_cfg) - - strategy = cfg.tuning.strategy.name.lower() - - assert strategy in EXP_STRATEGIES, "Tuning strategy {} is NOT supported".format(strategy) - - _resume = None - # check if interrupted tuning procedure exists. if yes, it will resume the - # whole auto tune process. - self.resume_file = ( - os.path.abspath(os.path.expanduser(cfg.tuning.workspace.resume)) - if cfg.tuning.workspace and cfg.tuning.workspace.resume - else None - ) - if self.resume_file: - assert os.path.exists(self.resume_file), "The specified resume file {} doesn't exist!".format( - self.resume_file - ) - with open(self.resume_file, "rb") as f: - _resume = pickle.load(f).__dict__ - - self.strategy = EXP_STRATEGIES[strategy]( - self._model, self.conf, None, None, self._eval_dataloader, self._eval_func, _resume - ) - - try: - with time_limit(self.conf.usr_cfg.tuning.exit_policy.timeout): - self.strategy.traverse() - except KeyboardInterrupt: - pass - except Exception as e: - logger.info("Unexpected exception {} happened during turing.".format(repr(e))) - finally: - if self.strategy.best_qmodel: - logger.info( - "Specified timeout or max trials is reached! " - "Found a converted model which meet accuracy goal. Exit." - ) - self.strategy.deploy_config() - else: - logger.info( - "Specified timeout or max trials is reached! " - "Not found any converted model which meet accuracy goal. Exit." - ) - - logger.info("Graph optimization is done. Please invoke model.save() to save " "optimized model to disk.") - - return self.strategy.best_qmodel - - fit = __call__ - - def dataset(self, dataset_type, *args, **kwargs): - """Get dataset.""" - from .data import Datasets - - return Datasets(self.framework)[dataset_type](*args, **kwargs) - - def set_config_by_model(self, model_obj): - """Set model config.""" - if model_obj.framework() != "tensorflow": - logger.warning("Only TensorFlow graph optimization is supported at current stage.") - sys.exit(0) - self.conf.usr_cfg.model.framework = model_obj.framework() - - if self._precisions == ["bf16"] and not CpuInfo().bf16: - if os.getenv("FORCE_BF16") == "1": - logger.warning( - "Graph optimization will generate bf16 graph although " - "the hardware doesn't support bf16 instruction." - ) - else: - logger.warning("Graph optimization exits due to the hardware " "doesn't support bf16 instruction.") - sys.exit(0) - - self.conf.usr_cfg.graph_optimization.precisions = ( - self._precisions if isinstance(self._precisions, list) else [self._precisions] - ) - self.conf.usr_cfg.model.inputs = self._input - if isinstance(self._output, str) and "," in self._output: - self.conf.usr_cfg.model.outputs = [s.strip() for s in self._output.split(",")] - else: - self.conf.usr_cfg.model.outputs = self._output - - @property - def precisions(self): - """Get precision.""" - return self._precisions - - @precisions.setter - def precisions(self, customized_precisions): - if isinstance(customized_precisions, list): - self._precisions = sorted([i.strip() for i in customized_precisions]) - elif isinstance(customized_precisions, str): - self._precisions = sorted([i.strip() for i in customized_precisions.split(",")]) - - @property - def input(self): - """Get input.""" - return self._input - - @input.setter - def input(self, customized_input): - self._input = customized_input - - @property - def output(self): - """Get output.""" - return self._output - - @output.setter - def output(self, customized_output): - self._output = customized_output - - @property - def eval_dataloader(self): - """Get eval_dataloader.""" - return self._eval_dataloader - - @eval_dataloader.setter - def eval_dataloader(self, dataloader): - """Set Data loader for evaluation. - - It is iterable and the batched data should consists of a tuple like (input, label), - when eval_dataloader is set, user should configure postprocess(optional) and metric - in yaml file or set postprocess and metric cls. Notice evaluation dataloader will be - used to generate data for model inference, make sure the input data can be feed to model. - - Args: - dataloader(generator): user are supported to set a user defined dataloader - which meet the requirements that can yield tuple of - (input, label)/(input, _) batched data. - Another good practice is to use neural_compressor.common.DataLoader - to initialize a neural_compressor dataloader object. - Notice neural_compressor.common.DataLoader is just a wrapper of the - information needed to build a dataloader, it can't yield - batched data and only in this setter method - a 'real' eval_dataloader will be created, - the reason is we have to know the framework info - and only after the Quantization object created then - framework information can be known. Future we will support - creating iterable dataloader from neural_compressor.common.DataLoader - """ - from .common import _generate_common_dataloader - - self._eval_dataloader = _generate_common_dataloader(dataloader, self.framework) - - @property - def model(self): - """Get model.""" - return self._model - - @model.setter - def model(self, user_model): - """Set the user model and dispatch to framework specific internal model object. - - Args: - user_model: user are supported to set model from original framework model format - (eg, tensorflow frozen_pb or path to a saved model), but not recommended. - Best practice is to set from a initialized neural_compressor.common.Model. - If tensorflow model is used, model's inputs/outputs will be auto inferred, - but sometimes auto inferred inputs/outputs will not meet your requests, - set them manually in config yaml file. Another corner case is slim model - of tensorflow, be careful of the name of model configured in yaml file, - make sure the name is in supported slim model list. - """ - if not isinstance(user_model, BaseModel): - logger.warning("Force convert framework model to neural_compressor model.") - if self.conf.usr_cfg.model.framework == "NA": - self.framework = get_model_fwk_name(user_model) - if self.framework == "pytorch": - if self.conf.usr_cfg.model.backend == "default": - self.framework = "pytorch_fx" - elif self.conf.usr_cfg.model.backend == "ipex": - self.framework = "pytorch_ipex" - self.conf.usr_cfg.model.framework = self.framework - self._model = NCModel(user_model, framework=self.framework) - self.set_config_by_model(self._model) - else: - self._model = NCModel(user_model, framework=self.framework) - else: - assert ( - self.conf.usr_cfg.model.framework != "NA" - ), "Please pass an original framework model but not neural compressor model!" - self._model = user_model - - @property - def metric(self): - """Get metric.""" - assert False, "Should not try to get the value of `metric` attribute." - return None - - @metric.setter - def metric(self, user_metric): - """Set metric class. - - neural_compressor will initialize this class when evaluation - neural_compressor have many built-in metrics, but user can set specific metric through - this api. The metric class should take the outputs of the model or - postprocess(if have) as inputs, neural_compressor built-in metric always take - (predictions, labels) as inputs for update, - and user_metric.metric_cls should be sub_class of neural_compressor.metric.BaseMetric - or user defined metric object - - Args: - user_metric(neural_compressor.common.Metric): user_metric should be object initialized from - neural_compressor.common.Metric, in this method the - user_metric.metric_cls will be registered to - specific frameworks and initialized. - """ - if deep_get(self.conf.usr_cfg, "evaluation.accuracy.metric"): - logger.warning( - "Override the value of `metric` field defined in yaml file" - " as user defines the value of `metric` attribute by code." - ) - - from ..metric import METRICS - from .common import Metric as NCMetric - - if isinstance(user_metric, NCMetric): - name = user_metric.name - metric_cls = user_metric.metric_cls - metric_cfg = {name: {**user_metric.kwargs}} - else: - for i in ["reset", "update", "result"]: - assert hasattr(user_metric, i), "Please realise {} function" "in user defined metric".format(i) - metric_cls = type(user_metric).__name__ - name = "user_" + metric_cls - metric_cfg = {name: id(user_metric)} - - deep_set(self.conf.usr_cfg, "evaluation.accuracy.metric", metric_cfg) - self.conf.usr_cfg = DotDict(self.conf.usr_cfg) - metrics = METRICS(self.framework) - metrics.register(name, metric_cls) - self._metric = user_metric - - @property - def postprocess(self, user_postprocess): - """Get postprocess.""" - assert False, "Should not try to get the value of `postprocess` attribute." - return None - - @postprocess.setter - def postprocess(self, user_postprocess): - """Set postprocess class. - - neural_compressor will initialize this class when evaluation. - The postprocess class should take the outputs of the model as inputs, and - output (predictions, labels) as inputs for metric update. - user_postprocess.postprocess_cls should be sub_class of neural_compressor.data.BaseTransform. - - Args: - user_postprocess(neural_compressor.common.Postprocess): user_postprocess should be object - initialized from neural_compressor.common.Postprocess, in this method the - user_postprocess.postprocess_cls will be registered to specific frameworks and initialized. - """ - from neural_compressor.data import Postprocess as NCPostprocess - - assert isinstance( - user_postprocess, NCPostprocess - ), "please initialize a neural_compressor.common.Postprocess and set...." - postprocess_cfg = {user_postprocess.name: {**user_postprocess.kwargs}} - if deep_get(self.conf.usr_cfg, "evaluation.accuracy.postprocess"): - logger.warning( - "Override the value of `postprocess` field defined in yaml file" - " as user defines the value of `postprocess` attribute by code." - ) - deep_set(self.conf.usr_cfg, "evaluation.accuracy.postprocess.transform", postprocess_cfg) - from neural_compressor.data import TRANSFORMS - - postprocesses = TRANSFORMS(self.framework, "postprocess") - postprocesses.register(user_postprocess.name, user_postprocess.postprocess_cls) - - @property - def eval_func(self): - """Get evaluation function.""" - assert False, "Should not try to get the value of `eval_func` attribute." - return None - - @eval_func.setter - def eval_func(self, user_eval_func): - """Set evaluation function provided by user. - - Args: - user_eval_func: This function takes model as parameter, - and evaluation dataset and metrics should be - encapsulated in this function implementation - and outputs a higher-is-better accuracy scalar - value. - The pseudo code should be something like: - def eval_func(model): - input, label = dataloader() - output = model(input) - accuracy = metric(output, label) - return accuracy - """ - self._eval_func = user_eval_func - - def __repr__(self): - """Return name.""" - return "GraphOptimization" - - -GraphOptimization = Graph_Optimization diff --git a/neural_compressor/experimental/metric/__init__.py b/neural_compressor/experimental/metric/__init__.py deleted file mode 100644 index 363fc66764f..00000000000 --- a/neural_compressor/experimental/metric/__init__.py +++ /dev/null @@ -1,30 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Intel Neural Compressor Metric.""" - -from .metric import METRICS, BaseMetric, metric_registry -from os.path import dirname, basename, isfile, join -import glob - -modules = glob.glob(join(dirname(__file__), "*.py")) - -for f in modules: - if isfile(f) and not f.startswith("__") and not f.endswith("__init__.py"): - __import__(basename(f)[:-3], globals(), locals(), level=1) - - -__all__ = ["METRICS", "BaseMetric", "metric_registry"] diff --git a/neural_compressor/experimental/metric/bleu.py b/neural_compressor/experimental/metric/bleu.py deleted file mode 100644 index 97897088b35..00000000000 --- a/neural_compressor/experimental/metric/bleu.py +++ /dev/null @@ -1,147 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Script for BLEU metric.""" - -import re -import sys -import unicodedata -from typing import List, Sequence - -import six -from deprecated import deprecated - -from .bleu_util import compute_bleu -from .metric import metric_registry - - -@deprecated(version="2.0") -class UnicodeRegex(object): - """Ad-hoc hack to recognize all punctuation and symbols. - - Attributes: - nondigit_punct_re: The compiled regular expressions to recognize - punctuation preceded with a digit. - punct_nondigit_re: The compiled regular expressions to recognize - punctuation followed by a digit. - symbol_re: The compiled regular expressions to recognize symbols. - """ - - def __init__(self) -> None: - """Initialize the regular expressions.""" - punctuation = self.property_chars("P") - self.nondigit_punct_re = re.compile(r"([^\d])([" + punctuation + r"])") - self.punct_nondigit_re = re.compile(r"([" + punctuation + r"])([^\d])") - self.symbol_re = re.compile("([" + self.property_chars("S") + "])") - - def property_chars(self, prefix: str) -> str: - """Collect all Unicode strings starting with a specific prefix. - - Args: - prefix: The specific prefix. - - Returns: - punctuation: The join result of all Unicode strings starting - with a specific prefix. - """ - punctuation = "".join( - six.unichr(x) for x in range(sys.maxunicode) if unicodedata.category(six.unichr(x)).startswith(prefix) - ) - return punctuation - - -uregex = UnicodeRegex() - - -@deprecated(version="2.0") -def bleu_tokenize(string: str) -> List[str]: - """Tokenize a string following the official BLEU implementation. - - See https://github.com/moses-smt/mosesdecoder/" - "blob/master/scripts/generic/mteval-v14.pl#L954-L983 - - Args: - string: The string to be tokenized. - - Returns: - tokens: A list of tokens. - """ - string = uregex.nondigit_punct_re.sub(r"\1 \2 ", string) - string = uregex.punct_nondigit_re.sub(r" \1 \2", string) - string = uregex.symbol_re.sub(r" \1 ", string) - tokens = string.split() - return tokens - - -@deprecated(version="2.0") -@metric_registry("BLEU", "tensorflow, tensorflow_itex") -class BLEU(object): - """Computes the BLEU (Bilingual Evaluation Understudy) score. - - BLEU is an algorithm for evaluating the quality of text which has - been machine-translated from one natural language to another. - This implementent approximate the BLEU score since we do not - glue word pieces or decode the ids and tokenize the output. - By default, we use ngram order of 4 and use brevity penalty. - Also, this does not have beam search. - - Attributes: - predictions: List of translations to score. - labels: List of the reference corresponding to the prediction result. - """ - - def __init__(self) -> None: - """Initialize predictions and labels.""" - self.predictions = [] - self.labels = [] - - def reset(self) -> None: - """Clear the predictions and labels in the cache.""" - self.predictions = [] - self.labels = [] - - def update(self, prediction: Sequence[str], label: Sequence[str]) -> None: - """Add the prediction and label. - - Args: - prediction: The prediction result. - label: The reference corresponding to the prediction result. - - Raises: - ValueError: An error occurred when the length of the prediction - and label are different. - """ - if len(label) != len(prediction): - raise ValueError( - "Reference and prediction files have different number " - "of lines. If training only a few steps (100-200), the " - "translation may be empty." - ) - label = [x.lower() for x in label] - prediction = [x.lower() for x in prediction] - label = [bleu_tokenize(x) for x in label] - prediction = [bleu_tokenize(x) for x in prediction] - self.labels.extend(label) - self.predictions.extend(prediction) - - def result(self) -> float: - """Compute the BLEU score. - - Returns: - bleu_score: The approximate BLEU score. - """ - bleu_score = compute_bleu(self.labels, self.predictions) * 100 - return bleu_score diff --git a/neural_compressor/experimental/metric/bleu_util.py b/neural_compressor/experimental/metric/bleu_util.py deleted file mode 100644 index 73e965dee0a..00000000000 --- a/neural_compressor/experimental/metric/bleu_util.py +++ /dev/null @@ -1,140 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# Copyright 2018 The TensorFlow Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the 'License'); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an 'AS IS' BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ============================================================================== -"""Script to compute BLEU score. - -Source: -https://github.com/tensorflow/tensor2tensor/blob/master/tensor2tensor/utils/bleu_hook.py -""" - -from __future__ import absolute_import, division, print_function - -import collections -import math -from typing import List, Sequence, Union - -import numpy as np -from deprecated import deprecated -from six.moves import xrange # pylint: disable=redefined-builtin - -from neural_compressor.utils.utility import LazyImport - -tf = LazyImport("tensorflow") - - -@deprecated(version="2.0") -def _get_ngrams_with_counter(segment: Sequence[str], max_order: List[int]) -> collections.Counter: - """Extract all n-grams up to a given maximum order from an input segment. - - Args: - segment: The text segment from which n-grams will be extracted. - max_order: The maximum length in tokens of the n-grams returned - by this methods. - - Returns: - ngram_counts: The Counter containing all n-grams up to max_order - in segment with a count of how many times each n-gram occurred. - """ - ngram_counts = collections.Counter() - for order in xrange(1, max_order + 1): - for i in xrange(0, len(segment) - order + 1): - ngram = tuple(segment[i : i + order]) - ngram_counts[ngram] += 1 - return ngram_counts - - -@deprecated(version="2.0") -def compute_bleu( - reference_corpus: Union[Sequence[str], Sequence[Sequence[str]]], - translation_corpus: Sequence[str], - max_order: int = 4, - use_bp: bool = True, -) -> float: - """Compute the BLEU score of translated segments against its references. - - Args: - reference_corpus: List of references for each translation. - Each reference should be tokenized into a list of tokens. - translation_corpus: List of translations to score. Each translation - should be tokenized into a list of tokens. - max_order: Maximum n-gram order to use when computing BLEU score. - use_bp: The flag to decide whether to apply brevity penalty. - - Returns: - bleu_score: The approximate BLEU score. - """ - reference_length = 0 - translation_length = 0 - bp = 1.0 - geo_mean = 0 - - matches_by_order = [0] * max_order - possible_matches_by_order = [0] * max_order - precisions = [] - - for references, translations in zip(reference_corpus, translation_corpus): - reference_length += len(references) - translation_length += len(translations) - ref_ngram_counts = _get_ngrams_with_counter(references, max_order) - translation_ngram_counts = _get_ngrams_with_counter(translations, max_order) - - overlap = dict( - (ngram, min(count, translation_ngram_counts[ngram])) for ngram, count in ref_ngram_counts.items() - ) - - for ngram in overlap: - matches_by_order[len(ngram) - 1] += overlap[ngram] - for ngram in translation_ngram_counts: - possible_matches_by_order[len(ngram) - 1] += translation_ngram_counts[ngram] - - precisions = [0] * max_order - smooth = 1.0 - - for i in xrange(0, max_order): - if possible_matches_by_order[i] > 0: - precisions[i] = float(matches_by_order[i]) / possible_matches_by_order[i] - if matches_by_order[i] > 0: - precisions[i] = float(matches_by_order[i]) / possible_matches_by_order[i] - else: - smooth *= 2 - precisions[i] = 1.0 / (smooth * possible_matches_by_order[i]) - else: - precisions[i] = 0.0 - - if max(precisions) > 0: - p_log_sum = sum(math.log(p) for p in precisions if p) - geo_mean = math.exp(p_log_sum / max_order) - - if use_bp: - ratio = translation_length / reference_length - bp = math.exp(1 - 1.0 / ratio) if ratio < 1.0 else 1.0 - bleu_score = np.float32(geo_mean * bp) - return bleu_score diff --git a/neural_compressor/experimental/metric/coco_label_map.py b/neural_compressor/experimental/metric/coco_label_map.py deleted file mode 100644 index 724842b7d40..00000000000 --- a/neural_compressor/experimental/metric/coco_label_map.py +++ /dev/null @@ -1,102 +0,0 @@ -# -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# -"""The dict mapping category IDs to its names of labels.""" - -category_map = { - 1: "person", - 2: "bicycle", - 3: "car", - 4: "motorcycle", - 5: "airplane", - 6: "bus", - 7: "train", - 8: "truck", - 9: "boat", - 10: "traffic light", - 11: "fire hydrant", - 13: "stop sign", - 14: "parking meter", - 15: "bench", - 16: "bird", - 17: "cat", - 18: "dog", - 19: "horse", - 20: "sheep", - 21: "cow", - 22: "elephant", - 23: "bear", - 24: "zebra", - 25: "giraffe", - 27: "backpack", - 28: "umbrella", - 31: "handbag", - 32: "tie", - 33: "suitcase", - 34: "frisbee", - 35: "skis", - 36: "snowboard", - 37: "sports ball", - 38: "kite", - 39: "baseball bat", - 40: "baseball glove", - 41: "skateboard", - 42: "surfboard", - 43: "tennis racket", - 44: "bottle", - 46: "wine glass", - 47: "cup", - 48: "fork", - 49: "knife", - 50: "spoon", - 51: "bowl", - 52: "banana", - 53: "apple", - 54: "sandwich", - 55: "orange", - 56: "broccoli", - 57: "carrot", - 58: "hot dog", - 59: "pizza", - 60: "donut", - 61: "cake", - 62: "chair", - 63: "couch", - 64: "potted plant", - 65: "bed", - 67: "dining table", - 70: "toilet", - 72: "tv", - 73: "laptop", - 74: "mouse", - 75: "remote", - 76: "keyboard", - 77: "cell phone", - 78: "microwave", - 79: "oven", - 80: "toaster", - 81: "sink", - 82: "refrigerator", - 84: "book", - 85: "clock", - 86: "vase", - 87: "scissors", - 88: "teddy bear", - 89: "hair drier", - 90: "toothbrush", -} diff --git a/neural_compressor/experimental/metric/coco_tools.py b/neural_compressor/experimental/metric/coco_tools.py deleted file mode 100644 index 84e033f724c..00000000000 --- a/neural_compressor/experimental/metric/coco_tools.py +++ /dev/null @@ -1,702 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Wrappers for third party pycocotools to be used within object_detection. - -Note that nothing in this file is tensorflow related and thus cannot -be called directly as a slim metric, for example. - -TODO(jonathanhuang): wrap as a slim metric in metrics.py - - -Usage example: given a set of images with ids in the list image_ids -and corresponding lists of numpy arrays encoding groundtruth (boxes and classes) -and detections (boxes, scores and classes), where elements of each list -correspond to detections/annotations of a single image, -then evaluation (in multi-class mode) can be invoked as follows: - - groundtruth_dict = coco_tools.ExportGroundtruthToCOCO( - image_ids, groundtruth_boxes_list, groundtruth_classes_list, - max_num_classes, output_path=None) - detections_list = coco_tools.ExportDetectionsToCOCO( - image_ids, detection_boxes_list, detection_scores_list, - detection_classes_list, output_path=None) - groundtruth = coco_tools.COCOWrapper(groundtruth_dict) - detections = groundtruth.LoadAnnotations(detections_list) - evaluator = coco_tools.COCOEvalWrapper(groundtruth, detections, - agnostic_mode=False) - metrics = evaluator.ComputeMetrics() -""" - -import copy -import time -from collections import OrderedDict -from typing import Any, Dict, List, Set, Union - -import numpy as np -from deprecated import deprecated -from pycocotools import coco, cocoeval, mask - -from neural_compressor.utils import logger - - -@deprecated(version="2.0") -class COCOWrapper(coco.COCO): - """Wrapper for the pycocotools COCO class. - - Attributes: - dataset: a dictionary holding bounding box annotations in the COCO format. - detection_type: type of detections being wrapped. Can be one of ['bbox', - 'segmentation'] - """ - - def __init__(self, dataset: Dict[str, Any], detection_type: str = "bbox"): - """Construct a COCOWrapper. - - See http://mscoco.org/dataset/#format for a description of the format. - By default, the coco.COCO class constructor reads from a JSON file. - This function duplicates the same behavior but loads from a dictionary, - allowing us to perform evaluation without writing to external storage. - - Args: - dataset: a dictionary holding bounding box annotations in the COCO format. - detection_type: type of detections being wrapped. Can be one of ['bbox', - 'segmentation'] - - Raises: - ValueError: if detection_type is unsupported. - """ - supported_detection_types = ["bbox", "segmentation"] - if detection_type not in supported_detection_types: - raise ValueError( - "Unsupported detection type: {}. " - "Supported values are: {}".format(detection_type, supported_detection_types) - ) - self._detection_type = detection_type - coco.COCO.__init__(self) - self.dataset = dataset - self.createIndex() - - def LoadAnnotations(self, annotations: list) -> coco.COCO: - """Load annotations dictionary into COCO datastructure. - - See http://mscoco.org/dataset/#format for a description of the annotations - format. As above, this function replicates the default behavior of the API - but does not require writing to external storage. - - Args: - annotations: python list holding object detection results where each - detection is encoded as a dict with required keys ['image_id', - 'category_id', 'score'] and one of ['bbox', 'segmentation'] based on - `detection_type`. - - Returns: - a coco.COCO datastructure holding object detection annotations results - - Raises: - ValueError: if (1) annotations is not a list or annotations do not - correspond to the images contained in self. - """ - results = coco.COCO() - results.dataset["images"] = [img for img in self.dataset["images"]] - - logger.info("Load and prepare annotation results.") - tic = time.time() - - if not isinstance(annotations, list): - raise ValueError("annotations is not a list of objects") - annotation_img_ids = [ann["image_id"] for ann in annotations] - if set(annotation_img_ids) != (set(annotation_img_ids) & set(self.getImgIds())): - raise ValueError("Results do not correspond to current coco set") - results.dataset["categories"] = copy.deepcopy(self.dataset["categories"]) - if self._detection_type == "bbox": - for idx, ann in enumerate(annotations): - bb = ann["bbox"] - ann["area"] = bb[2] * bb[3] - ann["id"] = idx + 1 - ann["iscrowd"] = 0 - elif self._detection_type == "segmentation": - for idx, ann in enumerate(annotations): - ann["area"] = mask.area(ann["segmentation"]) - ann["bbox"] = mask.toBbox(ann["segmentation"]) - ann["id"] = idx + 1 - ann["iscrowd"] = 0 - logger.info("DONE (t=%0.2fs)", (time.time() - tic)) - - results.dataset["annotations"] = annotations - results.createIndex() - return results - - -@deprecated(version="2.0") -class COCOEvalWrapper(cocoeval.COCOeval): - """Wrapper for the pycocotools COCOeval class. - - To evaluate, create two objects (groundtruth_dict and detections_list) - using the conventions listed at http://mscoco.org/dataset/#format. - Then call evaluation as follows: - - groundtruth = coco_tools.COCOWrapper(groundtruth_dict) - detections = groundtruth.LoadAnnotations(detections_list) - evaluator = coco_tools.COCOEvalWrapper(groundtruth, detections, - agnostic_mode=False) - metrics = evaluator.ComputeMetrics() - """ - - def __init__( - self, - groundtruth: coco.COCO = None, - detections: coco.COCO = None, - agnostic_mode=False, - iou_type: str = "bbox", - iou_thrs: Union[str, float] = None, - map_points=None, - ): - """Construct a COCOEvalWrapper. - - Note that for the area-based metrics to be meaningful, detection and - groundtruth boxes must be in image coordinates measured in pixels. - - Args: - groundtruth: a coco.COCO (or coco_tools.COCOWrapper) object holding - groundtruth annotations - detections: a coco.COCO (or coco_tools.COCOWrapper) object holding - detections - agnostic_mode: boolean (default: False). If True, evaluation ignores - class labels, treating all detections as proposals. - iou_thrs: Minimal value for intersection over union that allows to - make decision that prediction bounding box is true positive. - You can specify one float value between 0 to 1 or - string "05:0.05:0.95" for standard COCO thresholds. - iou_type: IOU type to use for evaluation. Supports `bbox` or `segm`. - map_points: The way to calculate mAP. 101 for 101-point interpolated AP, 11 for - 11-point interpolated AP, 0 for area under PR curve. - """ - cocoeval.COCOeval.__init__(self, groundtruth, detections, iouType=iou_type) - if agnostic_mode: - self.params.useCats = 0 - if iou_thrs == "0.5:0.05:0.95": - self.params.iouThrs = np.linspace(0.5, 0.95, int(np.round((0.95 - 0.5) / 0.05)) + 1, endpoint=True) - elif isinstance(iou_thrs, float): - self.params.iouThrs = [iou_thrs] - - if map_points == 101: - self.params.recThrs = np.linspace(0.0, 1.00, int(np.round((1.00 - 0.0) / 0.01)) + 1, endpoint=True) - if map_points == 11: - self.params.recThrs = np.linspace(0.0, 1.00, int(np.round((1.00 - 0.0) / 0.1)) + 1, endpoint=True) - if map_points == 0: - self.params.recThrs = [-1] - - def GetCategory(self, category_id: int) -> dict: - """Fetch dictionary holding category information given category id. - - Args: - category_id: integer id - - Returns: - dictionary holding 'id', 'name'. - """ - return self.cocoGt.cats[category_id] - - def GetAgnosticMode(self) -> bool: - """Return whether COCO Eval is configured to evaluate in agnostic mode.""" - return self.params.useCats == 0 - - def GetCategoryIdList(self) -> List[int]: - """Return the list of IDs of all valid categories.""" - return self.params.catIds - - def accumulate(self, p: cocoeval.Params = None): - """Accumulate evaluation results per image and store it to self.eval. - - Args: - p: input params for evaluation - """ - print("Accumulating evaluation results...") - tic = time.time() - if not self.evalImgs: - print("Please run evaluate() first") - # allows input customized parameters - if p is None: - p = self.params - p.catIds = p.catIds if p.useCats == 1 else [-1] - T = len(p.iouThrs) - R = len(p.recThrs) - K = len(p.catIds) if p.useCats else 1 - A = len(p.areaRng) - M = len(p.maxDets) - precision = -np.ones((T, R, K, A, M)) # -1 for the precision of absent categories - recall = -np.ones((T, K, A, M)) - scores = -np.ones((T, R, K, A, M)) - - # create dictionary for future indexing - _pe = self._paramsEval - print("-pe", _pe) - catIds = _pe.catIds if _pe.useCats else [-1] - setK = set(catIds) - setA = set(map(tuple, _pe.areaRng)) - setM = set(_pe.maxDets) - setI = set(_pe.imgIds) - # get inds to evaluate - k_list = [n for n, k in enumerate(p.catIds) if k in setK] - m_list = [m for n, m in enumerate(p.maxDets) if m in setM] - a_list = [n for n, a in enumerate(map(lambda x: tuple(x), p.areaRng)) if a in setA] - i_list = [n for n, i in enumerate(p.imgIds) if i in setI] - I0 = len(_pe.imgIds) - A0 = len(_pe.areaRng) - # retrieve E at each category, area range, and max number of detections - for k, k0 in enumerate(k_list): - Nk = k0 * A0 * I0 - for a, a0 in enumerate(a_list): - Na = a0 * I0 - for m, maxDet in enumerate(m_list): - E = [self.evalImgs[Nk + Na + i] for i in i_list] - E = [e for e in E if e is not None] - if len(E) == 0: - continue - dtScores = np.concatenate([e["dtScores"][0:maxDet] for e in E]) - - # different sorting method generates slightly different results. - # mergesort is used to be consistent as Matlab implementation. - inds = np.argsort(-dtScores, kind="mergesort") - dtScoresSorted = dtScores[inds] - - dtm = np.concatenate([e["dtMatches"][:, 0:maxDet] for e in E], axis=1)[:, inds] - dtIg = np.concatenate([e["dtIgnore"][:, 0:maxDet] for e in E], axis=1)[:, inds] - gtIg = np.concatenate([e["gtIgnore"] for e in E]) - npig = np.count_nonzero(gtIg == 0) - if npig == 0: - continue - tps = np.logical_and(dtm, np.logical_not(dtIg)) - fps = np.logical_and(np.logical_not(dtm), np.logical_not(dtIg)) - - tp_sum = np.cumsum(tps, axis=1).astype(dtype=np.float32) - fp_sum = np.cumsum(fps, axis=1).astype(dtype=np.float32) - for t, (tp, fp) in enumerate(zip(tp_sum, fp_sum)): - tp = np.array(tp) - fp = np.array(fp) - nd = len(tp) - rc = tp / npig - pr = tp / (fp + tp + np.spacing(1)) - - # calculate precision - if R == 1: - rc = np.concatenate(([0.0], rc, [1.0])) - pr = np.concatenate(([0.0], pr, [0.0])) - - # compute the precision envelope - for i in range(pr.size - 1, 0, -1): - pr[i - 1] = np.maximum(pr[i - 1], pr[i]) - - # to calculate area under PR curve, look for points - # where X axis (recall) changes value - change_point = np.where(rc[1:] != rc[:-1])[0] - # and sum (\Delta recall) * recall - res = np.sum((rc[change_point + 1] - rc[change_point]) * pr[change_point + 1]) - precision[t, :, k, a, m] = np.array([res]) - else: - q = np.zeros((R,)) - - # numpy is slow without cython optimization for accessing elements - # use python array gets significant speed improvement - pr = pr.tolist() - q = q.tolist() - - for i in range(nd - 1, 0, -1): - if pr[i] > pr[i - 1]: - pr[i - 1] = pr[i] - - inds = np.searchsorted(rc, p.recThrs, side="left") - try: - for ri, pi in enumerate(inds): - q[ri] = pr[pi] - except: - pass - precision[t, :, k, a, m] = np.array(q) - - # calculate recall - if nd: - recall[t, k, a, m] = rc[-1] - else: - recall[t, k, a, m] = 0 - - # calculate score - ss = np.zeros((R,)) - inds = np.searchsorted(rc, p.recThrs, side="left") - try: - for ri, pi in enumerate(inds): - ss[ri] = dtScoresSorted[pi] - except: - pass - scores[t, :, k, a, m] = np.array(ss) - # exit(0) - self.eval = { - "params": p, - "counts": [T, R, K, A, M], - "precision": precision, - "recall": recall, - "scores": scores, - } - toc = time.time() - print("DONE (t={:0.2f}s).".format(toc - tic)) - - def ComputeMetrics( - self, include_metrics_per_category: bool = False, all_metrics_per_category: bool = False - ): # pragma: no cover - """Compute detection metrics. - - Args: - include_metrics_per_category: Whether include metrics per category. - all_metrics_per_category: Whether include all the summery metrics for - each category in per_category_ap. Be careful with setting it to true if - you have more than handful of categories, because it will pollute - your mldash. - - Returns: - A tuple of (summary_metrics, per_category_ap), in which - (1) summary_metrics is a dictionary holding: - 'Precision/mAP': mean average precision over classes averaged over IOU - thresholds ranging from .5 to .95 with .05 increments; - 'Precision/mAP@.50IOU': mean average precision at 50% IOU; - 'Precision/mAP@.75IOU': mean average precision at 75% IOU; - 'Precision/mAP (small)': mean average precision for small objects - (area < 32^2 pixels); - 'Precision/mAP (medium)': mean average precision for medium sized - objects (32^2 pixels < area < 96^2 pixels); - 'Precision/mAP (large)': mean average precision for large objects - (96^2 pixels < area < 10000^2 pixels); - 'Recall/AR@1': average recall with 1 detection; - 'Recall/AR@10': average recall with 10 detections; - 'Recall/AR@100': average recall with 100 detections; - 'Recall/AR@100 (small)': average recall for small objects with 100 - detections; - 'Recall/AR@100 (medium)': average recall for medium objects with 100 - detections; - 'Recall/AR@100 (large)': average recall for large objects with 100 - detections; - and (2) per_category_ap is a dictionary holding category specific results with - keys of the form: 'Precision mAP ByCategory/category' - (without the supercategory part if no supercategories exist). - - For backward compatibility 'PerformanceByCategory' is included in the - output regardless of all_metrics_per_category. If evaluating class-agnostic - mode, per_category_ap is an empty dictionary. - - Raises: - ValueError: If category_stats does not exist. - """ - self.evaluate() - self.accumulate() - self.summarize() - - summary_metrics = OrderedDict( - [ - ("Precision/mAP", self.stats[0]), - ("Precision/mAP@.50IOU", self.stats[1]), - ("Precision/mAP@.75IOU", self.stats[2]), - ("Precision/mAP (small)", self.stats[3]), - ("Precision/mAP (medium)", self.stats[4]), - ("Precision/mAP (large)", self.stats[5]), - ("Recall/AR@1", self.stats[6]), - ("Recall/AR@10", self.stats[7]), - ("Recall/AR@100", self.stats[8]), - ("Recall/AR@100 (small)", self.stats[9]), - ("Recall/AR@100 (medium)", self.stats[10]), - ("Recall/AR@100 (large)", self.stats[11]), - ] - ) - if not include_metrics_per_category: - return summary_metrics, {} - if not hasattr(self, "category_stats"): - raise ValueError("Category stats do not exist") - per_category_ap = OrderedDict([]) - if self.GetAgnosticMode(): - return summary_metrics, per_category_ap - for category_index, category_id in enumerate(self.GetCategoryIdList()): - category = self.GetCategory(category_id)["name"] - # Kept for backward compatilbility - # pylint: disable=no-member - per_category_ap["PerformanceByCategory/mAP/{}".format(category)] = self.category_stats[0][category_index] - if all_metrics_per_category: - per_category_ap["Precision mAP ByCategory/{}".format(category)] = self.category_stats[0][category_index] - per_category_ap["Precision mAP@.50IOU ByCategory/{}".format(category)] = self.category_stats[1][ - category_index - ] - per_category_ap["Precision mAP@.75IOU ByCategory/{}".format(category)] = self.category_stats[2][ - category_index - ] - per_category_ap["Precision mAP (small) ByCategory/{}".format(category)] = self.category_stats[3][ - category_index - ] - per_category_ap["Precision mAP (medium) ByCategory/{}".format(category)] = self.category_stats[4][ - category_index - ] - per_category_ap["Precision mAP (large) ByCategory/{}".format(category)] = self.category_stats[5][ - category_index - ] - per_category_ap["Recall AR@1 ByCategory/{}".format(category)] = self.category_stats[6][category_index] - per_category_ap["Recall AR@10 ByCategory/{}".format(category)] = self.category_stats[7][category_index] - per_category_ap["Recall AR@100 ByCategory/{}".format(category)] = self.category_stats[8][category_index] - per_category_ap["Recall AR@100 (small) ByCategory/{}".format(category)] = self.category_stats[9][ - category_index - ] - per_category_ap["Recall AR@100 (medium) ByCategory/{}".format(category)] = self.category_stats[10][ - category_index - ] - per_category_ap["Recall AR@100 (large) ByCategory/{}".format(category)] = self.category_stats[11][ - category_index - ] - - return summary_metrics, per_category_ap - - -@deprecated(version="2.0") -def _ConvertBoxToCOCOFormat(box): - """Convert a box in [ymin, xmin, ymax, xmax] format to COCO format. - - This is a utility function for converting from our internal - [ymin, xmin, ymax, xmax] convention to the convention used by the COCO API - i.e., [xmin, ymin, width, height]. - - Args: - box: a numpy array in format of [ymin, xmin, ymax, xmax] - - Returns: - A list of floats, in COCO format, representing [xmin, ymin, width, height] - """ - return [float(box[1]), float(box[0]), float(box[3] - box[1]), float(box[2] - box[0])] - - -@deprecated(version="2.0") -def _RleCompress(masks): - """Compresses mask using Run-length encoding provided by pycocotools. - - Args: - masks: uint8 numpy array of shape [mask_height, mask_width] with values in - {0, 1}. - - Returns: - A pycocotools Run-length encoding of the mask. - """ - return mask.encode(np.asfortranarray(masks)) - - -@deprecated(version="2.0") -def ExportSingleImageGroundtruthToCoco( - image_id: Union[int, str], - next_annotation_id: int, - category_id_set: Set[str], - groundtruth_boxes: np.array, - groundtruth_classes: np.array, - groundtruth_masks: Union[np.array, None] = None, - groundtruth_is_crowd: Union[np.array, None] = None, -) -> list: - """Export groundtruth of a single image to COCO format. - - This function converts groundtruth detection annotations represented as numpy - arrays to dictionaries that can be ingested by the COCO evaluation API. Note - that the image_ids provided here must match the ones given to - ExportSingleImageDetectionsToCoco. We assume that boxes and classes are in - correspondence - that is: groundtruth_boxes[i, :], and - groundtruth_classes[i] are associated with the same groundtruth annotation. - - In the exported result, "area" fields are always set to the area of the - groundtruth bounding box. - - Args: - image_id: a unique image identifier either of type integer or string. - next_annotation_id: integer specifying the first id to use for the - groundtruth annotations. All annotations are assigned a continuous integer - id starting from this value. - category_id_set: A set of valid class ids. Groundtruth with classes not in - category_id_set are dropped. - groundtruth_boxes: numpy array (float32) with shape [num_gt_boxes, 4] - groundtruth_classes: numpy array (int) with shape [num_gt_boxes] - groundtruth_masks: optional uint8 numpy array of shape [num_detections, - image_height, image_width] containing detection_masks. - groundtruth_is_crowd: optional numpy array (int) with shape [num_gt_boxes] - indicating whether groundtruth boxes are crowd. - - Returns: - A list of groundtruth annotations for a single image in the COCO format. - - Raises: - ValueError: if (1) groundtruth_boxes and groundtruth_classes do not have the - right lengths or (2) if each of the elements inside these lists do not - have the correct shapes or (3) if image_ids are not integers - """ - if len(groundtruth_classes.shape) != 1: - raise ValueError("groundtruth_classes is " "expected to be of rank 1.") - if len(groundtruth_boxes.shape) != 2: - raise ValueError("groundtruth_boxes is expected to be of " "rank 2.") - if groundtruth_boxes.shape[1] != 4: - raise ValueError("groundtruth_boxes should have " "shape[1] == 4.") - num_boxes = groundtruth_classes.shape[0] - if num_boxes != groundtruth_boxes.shape[0]: - raise ValueError( - "Corresponding entries in groundtruth_classes, " - "and groundtruth_boxes should have " - "compatible shapes (i.e., agree on the 0th dimension)." - "Classes shape: %d. Boxes shape: %d. Image ID: %s" - % (groundtruth_classes.shape[0], groundtruth_boxes.shape[0], image_id) - ) - has_is_crowd = groundtruth_is_crowd is not None - if has_is_crowd and len(groundtruth_is_crowd.shape) != 1: - raise ValueError("groundtruth_is_crowd is expected to be of rank 1.") - groundtruth_list = [] - for i in range(num_boxes): - if groundtruth_classes[i] in category_id_set: - iscrowd = groundtruth_is_crowd[i] if has_is_crowd else 0 - export_dict = { - "id": next_annotation_id + i, - "image_id": image_id, - "category_id": int(groundtruth_classes[i]), - "bbox": list(_ConvertBoxToCOCOFormat(groundtruth_boxes[i, :])), - "area": float( - (groundtruth_boxes[i, 2] - groundtruth_boxes[i, 0]) - * (groundtruth_boxes[i, 3] - groundtruth_boxes[i, 1]) - ), - "iscrowd": iscrowd, - } - if groundtruth_masks is not None: - export_dict["segmentation"] = _RleCompress(groundtruth_masks[i]) - groundtruth_list.append(export_dict) - return groundtruth_list - - -@deprecated(version="2.0") -def ExportSingleImageDetectionBoxesToCoco( - image_id: Union[int, str], - category_id_set: Set[int], - detection_boxes: np.array, - detection_scores: np.array, - detection_classes: np.array, -) -> list: - """Export detections of a single image to COCO format. - - This function converts detections represented as numpy arrays to dictionaries - that can be ingested by the COCO evaluation API. Note that the image_ids - provided here must match the ones given to the - ExporSingleImageDetectionBoxesToCoco. We assume that boxes, and classes are in - correspondence - that is: boxes[i, :], and classes[i] - are associated with the same groundtruth annotation. - - Args: - image_id: unique image identifier either of type integer or string. - category_id_set: A set of valid class ids. Detections with classes not in - category_id_set are dropped. - detection_boxes: float numpy array of shape [num_detections, 4] containing - detection boxes. - detection_scores: float numpy array of shape [num_detections] containing - scored for the detection boxes. - detection_classes: integer numpy array of shape [num_detections] containing - the classes for detection boxes. - - Returns: - A list of detection annotations for a single image in the COCO format. - - Raises: - ValueError: if (1) detection_boxes, detection_scores and detection_classes - do not have the right lengths or (2) if each of the elements inside these - lists do not have the correct shapes or (3) if image_ids are not integers. - """ - if len(detection_classes.shape) != 1 or len(detection_scores.shape) != 1: - raise ValueError("All entries in detection_classes and detection_scores" "expected to be of rank 1.") - if len(detection_boxes.shape) != 2: - raise ValueError("All entries in detection_boxes expected to be of " "rank 2.") - if detection_boxes.shape[1] != 4: - raise ValueError("All entries in detection_boxes should have " "shape[1] == 4.") - num_boxes = detection_classes.shape[0] - if not num_boxes == detection_boxes.shape[0] == detection_scores.shape[0]: - raise ValueError( - "Corresponding entries in detection_classes, " - "detection_scores and detection_boxes should have " - "compatible shapes (i.e., agree on the 0th dimension). " - "Classes shape: %d. Boxes shape: %d. " - "Scores shape: %d" % (detection_classes.shape[0], detection_boxes.shape[0], detection_scores.shape[0]) - ) - detections_list = [] - for i in range(num_boxes): - if detection_classes[i] in category_id_set: - detections_list.append( - { - "image_id": image_id, - "category_id": int(detection_classes[i]), - "bbox": list(_ConvertBoxToCOCOFormat(detection_boxes[i, :])), - "score": float(detection_scores[i]), - } - ) - return detections_list - - -@deprecated(version="2.0") -def ExportSingleImageDetectionMasksToCoco( - image_id: Union[str, int], - category_id_set: Set[int], - detection_masks: np.array, - detection_scores: np.array, - detection_classes: np.array, -) -> list: - """Export detection masks of a single image to COCO format. - - This function converts detections represented as numpy arrays to dictionaries - that can be ingested by the COCO evaluation API. We assume that - detection_masks, detection_scores, and detection_classes are in correspondence - - that is: detection_masks[i, :], detection_classes[i] and detection_scores[i] - are associated with the same annotation. - - Args: - image_id: unique image identifier either of type integer or string. - category_id_set: A set of valid class ids. Detections with classes not in - category_id_set are dropped. - detection_masks: uint8 numpy array of shape [num_detections, image_height, - image_width] containing detection_masks. - detection_scores: float numpy array of shape [num_detections] containing - scores for detection masks. - detection_classes: integer numpy array of shape [num_detections] containing - the classes for detection masks. - - Returns: - A list of detection mask annotations for a single image in the COCO format. - - Raises: - ValueError: if (1) detection_masks, detection_scores and detection_classes - do not have the right lengths or (2) if each of the elements inside these - lists do not have the correct shapes or (3) if image_ids are not integers. - """ - if len(detection_classes.shape) != 1 or len(detection_scores.shape) != 1: - raise ValueError("All entries in detection_classes and detection_scores" "expected to be of rank 1.") - num_boxes = detection_classes.shape[0] - if not num_boxes == len(detection_masks) == detection_scores.shape[0]: - raise ValueError( - "Corresponding entries in detection_classes, " - "detection_scores and detection_masks should have " - "compatible lengths and shapes " - "Classes length: %d. Masks length: %d. " - "Scores length: %d" % (detection_classes.shape[0], len(detection_masks), detection_scores.shape[0]) - ) - detections_list = [] - for i in range(num_boxes): - if detection_classes[i] in category_id_set: - detections_list.append( - { - "image_id": image_id, - "category_id": int(detection_classes[i]), - "segmentation": _RleCompress(detection_masks[i]), - "score": float(detection_scores[i]), - } - ) - return detections_list diff --git a/neural_compressor/experimental/metric/evaluate_squad.py b/neural_compressor/experimental/metric/evaluate_squad.py deleted file mode 100644 index ddb94b644aa..00000000000 --- a/neural_compressor/experimental/metric/evaluate_squad.py +++ /dev/null @@ -1,123 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Official evaluation script for v1.1 of the SQuAD dataset. - -From https://github.com/allenai/bi-att-flow/blob/master/squad/evaluate-v1.1.py -""" - -from __future__ import print_function - -import sys -from collections import Counter - -from deprecated import deprecated - -from .f1 import normalize_answer - - -@deprecated(version="2.0") -def f1_score(prediction, ground_truth): - """Calculate the F1 score of the prediction and the ground_truth. - - Args: - prediction: The predicted result. - ground_truth: The ground truth. - - Returns: - The F1 score of prediction. Float point number. - """ - prediction_tokens = normalize_answer(prediction).split() - ground_truth_tokens = normalize_answer(ground_truth).split() - common = Counter(prediction_tokens) & Counter(ground_truth_tokens) - num_same = sum(common.values()) - if num_same == 0: - return 0 - precision = 1.0 * num_same / len(prediction_tokens) - recall = 1.0 * num_same / len(ground_truth_tokens) - f1 = (2 * precision * recall) / (precision + recall) - return f1 - - -@deprecated(version="2.0") -def metric_max_over_ground_truths(metric_fn, prediction, ground_truths): - """Calculate the max metric for each ground truth. - - For each answer in ground_truths, evaluate the metric of prediction with - this answer, and return the max metric. - - Args: - metric_fn: The function to calculate the metric. - prediction: The prediction result. - ground_truths: A list of correct answers. - - Returns: - The max metric. Float point number. - """ - scores_for_ground_truths = [] - for ground_truth in ground_truths: - score = metric_fn(prediction, ground_truth) - scores_for_ground_truths.append(score) - return max(scores_for_ground_truths) - - -@deprecated(version="2.0") -def exact_match_score(prediction, ground_truth): - """Compute the exact match score between prediction and ground truth. - - Args: - prediction: The result of predictions to be evaluated. - ground_truth: The ground truth. - - Returns: - The exact match score. - """ - return normalize_answer(prediction) == normalize_answer(ground_truth) - - -@deprecated(version="2.0") -def evaluate(dataset, predictions): - """Evaluate the average F1 score and the exact match score for Question-Answering results. - - Args: - dataset: The dataset to evaluate the prediction. A list instance of articles. - An article contains a list of paragraphs, a paragraph contains a list of - question-and-answers (qas), and a question-and-answer contains an id, a question, - and a list of correct answers. For example: - predictions: The result of predictions to be evaluated. A dict mapping the id of - a question to the predicted answer of the question. - - Returns: - The F1 score and the exact match score. - """ - f1 = exact_match = total = 0 - for article in dataset: - for paragraph in article["paragraphs"]: - for qa in paragraph["qas"]: - total += 1 - if qa["id"] not in predictions: - message = "Unanswered question " + qa["id"] + " will receive score 0." - print(message, file=sys.stderr) - continue - ground_truths = list(map(lambda x: x["text"], qa["answers"])) - prediction = predictions[qa["id"]] - exact_match += metric_max_over_ground_truths(exact_match_score, prediction, ground_truths) - f1 += metric_max_over_ground_truths(f1_score, prediction, ground_truths) - - exact_match = 100.0 * exact_match / total - f1 = 100.0 * f1 / total - - return {"exact_match": exact_match, "f1": f1} diff --git a/neural_compressor/experimental/metric/f1.py b/neural_compressor/experimental/metric/f1.py deleted file mode 100644 index 91ed4cc446d..00000000000 --- a/neural_compressor/experimental/metric/f1.py +++ /dev/null @@ -1,153 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Official evaluation script for v1.1 of the SQuAD dataset. - -From https://github.com/allenai/bi-att-flow/blob/master/squad/evaluate-v1.1.py -""" - -import re -import string -from collections import Counter, abc -from typing import Any, Callable, Dict, List, TypeVar - -from deprecated import deprecated - -from neural_compressor.utils import logger - - -@deprecated(version="2.0") -def normalize_answer(text: str) -> str: - """Normalize the answer text. - - Lower text, remove punctuation, articles and extra whitespace, - and replace other whitespace (newline, tab, etc.) to space. - - Args: - s: The text to be normalized. - - Returns: - The normalized text. - """ - - def _remove_articles(text): - return re.sub(r"\b(a|an|the)\b", " ", text) - - def _white_space_fix(text): - return " ".join(text.split()) - - def _remove_punc(text): - exclude = set(string.punctuation) - return "".join(ch for ch in text if ch not in exclude) - - def _lower(text): - return text.lower() - - return _white_space_fix(_remove_articles(_remove_punc(_lower(text)))) - - -@deprecated(version="2.0") -def f1_score(prediction: abc.Sequence, ground_truth: abc.Sequence): - """Calculate the F1 score of the prediction and the ground_truth. - - Args: - prediction: the predicted answer. - ground_truth: the correct answer. - - Returns: - The F1 score of prediction. Float point number. - """ - assert isinstance(prediction, abc.Sequence) and isinstance( - ground_truth, abc.Sequence - ), "prediction and ground_truth should be Sequence" - common = Counter(prediction) & Counter(ground_truth) - num_same = sum(common.values()) - if num_same == 0: - return 0 - precision = 1.0 * num_same / len(prediction) - recall = 1.0 * num_same / len(ground_truth) - f1 = (2 * precision * recall) / (precision + recall) - return f1 - - -T = TypeVar("T") - - -def metric_max_over_ground_truths( - metric_fn: Callable[[T, T], float], prediction: str, ground_truths: List[str] -) -> float: - """Calculate the max metric for each ground truth. - - For each answer in ground_truths, evaluate the metric of prediction with - this answer, and return the max metric. - - Args: - metric_fn: the function to calculate the metric. - prediction: the prediction result. - ground_truths: the list of correct answers. - - Returns: - The max metric. Float point number. - """ - scores_for_ground_truths = [] - for ground_truth in ground_truths: - prediction_tokens = normalize_answer(prediction).split() - ground_truth_tokens = normalize_answer(ground_truth).split() - score = metric_fn(prediction_tokens, ground_truth_tokens) - scores_for_ground_truths.append(score) - return max(scores_for_ground_truths) - - -def evaluate(predictions: Dict[str, str], dataset: List[Dict[str, Any]]) -> float: - """Evaluate the average F1 score of Question-Answering results. - - The F1 score is the harmonic mean of the precision and recall. It can be computed - with the equation: F1 = 2 * (precision * recall) / (precision + recall). - For all question-and-answers in dataset, it evaluates the f1-score - - Args: - predictions: The result of predictions to be evaluated. A dict mapping the id of - a question to the predicted answer of the question. - dataset: The dataset to evaluate the prediction. A list instance of articles. - An article contains a list of paragraphs, a paragraph contains a list of - question-and-answers (qas), and a question-and-answer contains an id, a question, - and a list of correct answers. For example: - - [{'paragraphs': - [{'qas':[{'answers': [{'answer_start': 177, 'text': 'Denver Broncos'}, ...], - 'question': 'Which NFL team represented the AFC at Super Bowl 50?', - 'id': '56be4db0acb8001400a502ec'}]}]}] - - Returns: - The F1 score of this prediction. Float point number in forms of a percentage. - """ - f1 = total = 0 - for article in dataset: - for paragraph in article["paragraphs"]: - for qa in paragraph["qas"]: - total += 1 - if qa["id"] not in predictions: - message = "Unanswered question " + qa["id"] + " will receive score 0." - logger.warning(message) - continue - - ground_truths = list(map(lambda x: x["text"], qa["answers"])) - prediction = predictions[qa["id"]] - - f1 += metric_max_over_ground_truths(f1_score, prediction, ground_truths) - - f1 = 100.0 * f1 / total - return f1 diff --git a/neural_compressor/experimental/metric/metric.py b/neural_compressor/experimental/metric/metric.py deleted file mode 100644 index 17eb84b6a97..00000000000 --- a/neural_compressor/experimental/metric/metric.py +++ /dev/null @@ -1,1633 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Neural Compressor metrics.""" - - -from abc import abstractmethod - -import numpy as np -from deprecated import deprecated -from sklearn.metrics import accuracy_score - -from neural_compressor.utils import logger -from neural_compressor.utils.utility import LazyImport, singleton - -torch = LazyImport("torch") -tf = LazyImport("tensorflow") -mx = LazyImport("mxnet") -transformers = LazyImport("transformers") - - -@deprecated(version="2.0") -@singleton -class TensorflowMetrics(object): - """Tensorflow metrics collection. - - Attributes: - metrics: A dict to maintain all metrics for Tensorflow model. - """ - - def __init__(self) -> None: - """Initialize the metrics collection.""" - self.metrics = {} - self.metrics.update(TENSORFLOW_METRICS) - - -@deprecated(version="2.0") -@singleton -class PyTorchMetrics(object): - """PyTorch metrics collection. - - Attributes: - metrics: A dict to maintain all metrics for PyTorch model. - """ - - def __init__(self) -> None: - """Initialize the metrics collection.""" - self.metrics = {} - self.metrics.update(PYTORCH_METRICS) - - -@deprecated(version="2.0") -@singleton -class MXNetMetrics(object): - """MXNet metrics collection. - - Attributes: - metrics: A dict to maintain all metrics for MXNet model. - """ - - def __init__(self) -> None: - """Initialize the metrics collection.""" - from ...adaptor.mxnet_utils.util import check_mx_version - - if check_mx_version("2.0.0"): # pragma: no cover - import mxnet.gluon.metric as mx_metrics - else: - import mxnet.metric as mx_metrics - self.metrics = { - "Accuracy": WrapMXNetMetric(mx_metrics.Accuracy), - "MAE": WrapMXNetMetric(mx_metrics.MAE), - "MSE": WrapMXNetMetric(mx_metrics.MSE), - "Loss": WrapMXNetMetric(mx_metrics.Loss), - } - self.metrics.update(MXNET_METRICS) - - -@deprecated(version="2.0") -@singleton -class ONNXRTQLMetrics(object): - """ONNXRT QLinear metrics collection. - - Attributes: - metrics: A dict to maintain all metrics for ONNXRT QLinear model. - """ - - def __init__(self) -> None: - """Initialize the metrics collection.""" - self.metrics = {} - self.metrics.update(ONNXRT_QL_METRICS) - - -@deprecated(version="2.0") -@singleton -class ONNXRTITMetrics(object): - """ONNXRT Integer metrics collection. - - Attributes: - metrics: A dict to maintain all metrics for ONNXRT Integer model. - """ - - def __init__(self) -> None: - """Initialize the metrics collection.""" - self.metrics = {} - self.metrics.update(ONNXRT_IT_METRICS) - - -framework_metrics = { - "tensorflow": TensorflowMetrics, - "tensorflow_itex": TensorflowMetrics, - "keras": TensorflowMetrics, - "mxnet": MXNetMetrics, - "pytorch": PyTorchMetrics, - "pytorch_ipex": PyTorchMetrics, - "pytorch_fx": PyTorchMetrics, - "onnxrt_qlinearops": ONNXRTQLMetrics, - "onnxrt_integerops": ONNXRTITMetrics, - "onnxrt_qdq": ONNXRTQLMetrics, - "onnxruntime": ONNXRTQLMetrics, -} - -# user/model specific metrics will be registered here -TENSORFLOW_METRICS = {} -TENSORFLOW_ITEX_METRICS = {} -MXNET_METRICS = {} -PYTORCH_METRICS = {} -ONNXRT_QL_METRICS = {} -ONNXRT_IT_METRICS = {} - -registry_metrics = { - "tensorflow": TENSORFLOW_METRICS, - "tensorflow_itex": TENSORFLOW_ITEX_METRICS, - "keras": TENSORFLOW_METRICS, - "mxnet": MXNET_METRICS, - "pytorch": PYTORCH_METRICS, - "pytorch_ipex": PYTORCH_METRICS, - "pytorch_fx": PYTORCH_METRICS, - "onnxrt_qlinearops": ONNXRT_QL_METRICS, - "onnxrt_qdq": ONNXRT_QL_METRICS, - "onnxrt_integerops": ONNXRT_IT_METRICS, - "onnxruntime": ONNXRT_QL_METRICS, -} - - -@deprecated(version="2.0") -class METRICS(object): - """Intel Neural Compressor Metrics. - - Attributes: - metrics: The collection of registered metrics for the specified framework. - """ - - def __init__(self, framework: str): - """Initialize the metrics collection based on the framework name. - - Args: - framework: The framework name. - """ - assert framework in ( - "tensorflow", - "tensorflow_itex", - "keras", - "pytorch", - "pytorch_ipex", - "pytorch_fx", - "onnxrt_qdq", - "onnxrt_qlinearops", - "onnxrt_integerops", - "mxnet", - "onnxruntime", - ), "framework support tensorflow pytorch mxnet onnxrt" - self.metrics = framework_metrics[framework]().metrics - - def __getitem__(self, metric_type: str): - """Get the metric based on the specified type. - - Args: - metric_type: The metric type. - - Returns: - The metric with the specified type. - """ - assert metric_type in self.metrics.keys(), "only support metrics in {}".format(self.metrics.keys()) - - return self.metrics[metric_type] - - def register(self, name, metric_cls) -> None: - """Register a metric. - - Args: - name: The name of metric. - metric_cls: The metric class. - """ - assert name not in self.metrics.keys(), "registered metric name already exists." - self.metrics.update({name: metric_cls}) - - -@deprecated(version="2.0") -def metric_registry(metric_type: str, framework: str): - """Decorate for registering all Metric subclasses. - - The cross-framework metric is supported by specifying the framework param - as one of tensorflow, pytorch, mxnet, onnxrt. - - Args: - metric_type: The metric type. - framework: The framework name. - - Returns: - decorator_metric: The function to register metric class. - """ - - def decorator_metric(cls): - for single_framework in [fwk.strip() for fwk in framework.split(",")]: - assert single_framework in [ - "tensorflow", - "tensorflow_itex", - "mxnet", - "onnxrt_qlinearops", - "onnxrt_integerops", - "onnxrt_qdq", - "onnxruntime", - "pytorch", - "pytorch_ipex", - "pytorch_fx", - ], "The framework support tensorflow mxnet pytorch onnxrt" - - if metric_type in registry_metrics[single_framework].keys(): # pragma: no cover - raise ValueError("Cannot have two metrics with the same name") - registry_metrics[single_framework][metric_type] = cls - return cls - - return decorator_metric - - -@deprecated(version="2.0") -class BaseMetric(object): - """The base class of Metric.""" - - def __init__(self, metric, single_output=False, hvd=None): - """Initialize the basic metric. - - Args: - metric: The metric class. - single_output: Whether the output is single or not, defaults to False. - hvd: The Horovod class for distributed training, defaults to None. - """ - self._metric_cls = metric - self._single_output = single_output - self._hvd = hvd - - def __call__(self, *args, **kwargs): - """Evaluate the model predictions, and the reference. - - Returns: - The class itself. - """ - self._metric = self._metric_cls(*args, **kwargs) - return self - - @abstractmethod - def update(self, preds, labels=None, sample_weight=None): - """Update the state that need to be evaluated. - - Args: - preds: The prediction result. - labels: The reference. Defaults to None. - sample_weight: The sampling weight. Defaults to None. - - Raises: - NotImplementedError: The method should be implemented by subclass. - """ - raise NotImplementedError - - @abstractmethod - def reset(self): - """Clear the predictions and labels. - - Raises: - NotImplementedError: The method should be implemented by subclass. - """ - raise NotImplementedError - - @abstractmethod - def result(self): - """Evaluate the difference between predictions and labels. - - Raises: - NotImplementedError: The method should be implemented by subclass. - """ - raise NotImplementedError - - @property - def metric(self): - """Return its metric class. - - Returns: - The metric class. - """ - return self._metric_cls - - @property - def hvd(self): - """Return its hvd class. - - Returns: - The hvd class. - """ - return self._hvd - - @hvd.setter - def hvd(self, hvd): - """Set its hvd. - - Args: - hvd: The Horovod class for distributed training. - """ - self._hvd = hvd - - -@deprecated(version="2.0") -class WrapPyTorchMetric(BaseMetric): - """The wrapper of Metric class for PyTorch.""" - - def update(self, preds, labels=None, sample_weight=None): - """Convert the prediction to torch. - - Args: - preds: The prediction result. - labels: The reference. Defaults to None. - sample_weight: The sampling weight. Defaults to None. - """ - if self._single_output: # pragma: no cover - output = torch.as_tensor(preds) - else: - output = (torch.as_tensor(preds), torch.as_tensor(labels)) - self._metric_cls.update(output) - - def reset(self): - """Clear the predictions and labels.""" - self._metric_cls.reset() - - def result(self): - """Evaluate the difference between predictions and labels.""" - return self._metric_cls.result() - - -@deprecated(version="2.0") -class WrapMXNetMetric(BaseMetric): - """The wrapper of Metric class for MXNet.""" - - def update(self, preds, labels=None, sample_weight=None): - """Convert the prediction to MXNet array. - - Args: - preds: The prediction result. - labels: The reference. Defaults to None. - sample_weight: The sampling weight. Defaults to None. - """ - preds = mx.nd.array(preds) - labels = mx.nd.array(labels) - self._metric.update(labels=labels, preds=preds) - - def reset(self): - """Clear the predictions and labels.""" - self._metric.reset() - - def result(self): - """Evaluate the difference between predictions and labels. - - Returns: - acc: The evaluated result. - """ - acc_name, acc = self._metric.get() - return acc - - -@deprecated(version="2.0") -class WrapONNXRTMetric(BaseMetric): - """The wrapper of Metric class for ONNXRT.""" - - def update(self, preds, labels=None, sample_weight=None): - """Convert the prediction to NumPy array. - - Args: - preds: The prediction result. - labels: The reference. Defaults to None. - sample_weight: The sampling weight. Defaults to None. - """ - preds = np.array(preds) - labels = np.array(labels) - self._metric_cls.update(labels=labels, preds=preds) - - def reset(self): - """Clear the predictions and labels.""" - self._metric_cls.reset() - - def result(self): - """Evaluate the difference between predictions and labels. - - Returns: - acc: The evaluated result. - """ - acc_name, acc = self._metric_cls.result() - return acc - - -@deprecated(version="2.0") -def _topk_shape_validate(preds, labels): - # preds shape can be Nxclass_num or class_num(N=1 by default) - # it's more suitable for 'Accuracy' with preds shape Nx1(or 1) output from argmax - if isinstance(preds, int): - preds = [preds] - preds = np.array(preds) - elif isinstance(preds, np.ndarray): - preds = np.array(preds) - elif isinstance(preds, list): - preds = np.array(preds) - preds = preds.reshape((-1, preds.shape[-1])) - - # consider labels just int value 1x1 - if isinstance(labels, int): - labels = [labels] - labels = np.array(labels) - elif isinstance(labels, tuple): - labels = np.array([labels]) - labels = labels.reshape((labels.shape[-1], -1)) - elif isinstance(labels, list): - if isinstance(labels[0], int): - labels = np.array(labels) - labels = labels.reshape((labels.shape[0], 1)) - elif isinstance(labels[0], tuple): - labels = np.array(labels) - labels = labels.reshape((labels.shape[-1], -1)) - else: - labels = np.array(labels) - # labels most have 2 axis, 2 cases: N(or Nx1 sparse) or Nxclass_num(one-hot) - # only support 2 dimension one-shot labels - # or 1 dimension one-hot class_num will confuse with N - - if len(preds.shape) == 1: - N = 1 - class_num = preds.shape[0] - preds = preds.reshape([-1, class_num]) - elif len(preds.shape) >= 2: - N = preds.shape[0] - preds = preds.reshape([N, -1]) - class_num = preds.shape[1] - - label_N = labels.shape[0] - assert label_N == N, "labels batch size should same with preds" - labels = labels.reshape([N, -1]) - # one-hot labels will have 2 dimension not equal 1 - if labels.shape[1] != 1: - labels = labels.argsort()[..., -1:] - return preds, labels - - -@deprecated(version="2.0") -def _shape_validate(preds, labels): - assert type(preds) in [int, list, np.ndarray], "preds must be in int or list, ndarray" - assert type(labels) in [int, list, np.ndarray], "labels must be in int or list, ndarray" - if isinstance(preds, int): - preds = [np.array([preds])] - elif isinstance(preds[0], int): - preds = [np.array(preds)] - else: - preds = [np.array(pred) for pred in preds] - if isinstance(labels, int): - labels = [np.array([labels])] - elif isinstance(labels[0], int): - labels = [np.array(labels)] - else: - labels = [np.array(label) for label in labels] - for pred, label in zip(preds, labels): - assert pred.shape == label.shape, "Shape mismatch, label shape {} vs pred shape {}".format( - label.shape, pred.shape - ) - return preds, labels - - -@deprecated(version="2.0") -@metric_registry("F1", "tensorflow, tensorflow_itex, pytorch, mxnet, onnxrt_qlinearops, onnxrt_integerops") -class F1(BaseMetric): - """F1 score of a binary classification problem. - - The F1 score is the harmonic mean of the precision and recall. - It can be computed with the equation: - F1 = 2 * (precision * recall) / (precision + recall) - """ - - def __init__(self): - """Initialize the F1 score list.""" - self._score_list = [] - - def update(self, preds, labels): - """Add the predictions and labels. - - Args: - preds: The predictions. - labels: The labels corresponding to the predictions. - """ - from .f1 import f1_score - - if getattr(self, "_hvd", None) is not None: - gathered_preds_list = self._hvd.allgather_object(preds) - gathered_labels_list = self._hvd.allgather_object(labels) - temp_preds_list, temp_labels_list = [], [] - for i in range(0, self._hvd.size()): - temp_preds_list += gathered_preds_list[i] - temp_labels_list += gathered_labels_list[i] - preds = temp_preds_list - labels = temp_labels_list - result = f1_score(preds, labels) - self._score_list.append(result) - - def reset(self): - """Clear the predictions and labels.""" - self._score_list = [] - - def result(self): - """Compute the F1 score.""" - return np.array(self._score_list).mean() - - -@deprecated(version="2.0") -def _accuracy_shape_check(preds, labels): - """Check and convert the shape of predictions and labels. - - Args: - preds: The predictions. - labels: The labels corresponding to the predictions. - - Returns: - preds: The predictions in the format of NumPy array. - labels: The labels in the format of NumPy array. - """ - if isinstance(preds, int): - preds = [preds] - preds = np.array(preds) - if isinstance(labels, int): - labels = [labels] - labels = np.array(labels) - if len(labels.shape) != len(preds.shape) and len(labels.shape) + 1 != len(preds.shape): - raise ValueError( - "labels must have shape of (batch_size, ..) and preds must have" - "shape of (batch_size, num_classes, ...) or (batch_size, ..)," - "but given {} and {}.".format(labels.shape, preds.shape) - ) - return preds, labels - - -@deprecated(version="2.0") -def _accuracy_type_check(preds, labels): - """Determine the type of prediction. - - Args: - preds: The predictions. - labels: The labels corresponding to the predictions. - - Returns: - update_type: The type of predictions. - """ - if len(preds.shape) == len(labels.shape) + 1: - num_classes = preds.shape[1] - if num_classes == 1: - update_type = "binary" - else: - update_type = "multiclass" - elif len(preds.shape) == len(labels.shape): - if len(preds.shape) == 1 or preds.shape[1] == 1: - update_type = "binary" - else: - update_type = "multilabel" - return update_type - - -@deprecated(version="2.0") -@metric_registry("Accuracy", "tensorflow, tensorflow_itex, pytorch, onnxrt_qlinearops, onnxrt_integerops") -class Accuracy(BaseMetric): - """The Accuracy for the classification tasks. - - The accuracy score is the proportion of the total number of predictions - that were correct classified. - - Attributes: - pred_list: List of prediction to score. - label_list: List of labels to score. - sample: The total number of samples. - """ - - def __init__(self): - """Initialize predictions, labels and sample.""" - self.pred_list = [] - self.label_list = [] - self.sample = 0 - - def update(self, preds, labels, sample_weight=None): - """Add the predictions and labels. - - Args: - preds: The predictions. - labels: The labels corresponding to the predictions. - sample_weight: The sample weight. - """ - preds, labels = _accuracy_shape_check(preds, labels) - update_type = _accuracy_type_check(preds, labels) - if update_type == "binary": - self.pred_list.extend(preds) - self.label_list.extend(labels) - self.sample += labels.shape[0] - elif update_type == "multiclass": - self.pred_list.extend(np.argmax(preds, axis=1).astype("int32")) - self.label_list.extend(labels) - self.sample += labels.shape[0] - elif update_type == "multilabel": - # (N, C, ...) -> (N*..., C) - num_label = preds.shape[1] - last_dim = len(preds.shape) - if last_dim - 1 != 1: - trans_list = [0] - trans_list.extend(list(range(2, len(preds.shape)))) - trans_list.extend([1]) - preds = preds.transpose(trans_list).reshape(-1, num_label) - labels = labels.transpose(trans_list).reshape(-1, num_label) - self.sample += preds.shape[0] * preds.shape[1] - self.pred_list.append(preds) - self.label_list.append(labels) - - def reset(self): - """Clear the predictions and labels.""" - self.pred_list = [] - self.label_list = [] - self.sample = 0 - - def result(self): - """Compute the accuracy.""" - correct_num = np.sum(np.array(self.pred_list) == np.array(self.label_list)) - if getattr(self, "_hvd", None) is not None: # pragma: no cover - allghter_correct_num = sum(self._hvd.allgather_object(correct_num)) - allgather_sample = sum(self._hvd.allgather_object(self.sample)) - return allghter_correct_num / allgather_sample - return correct_num / self.sample - - -@deprecated(version="2.0") -class PyTorchLoss: - """A dummy PyTorch Metric. - - A dummy metric that computes the average of predictions and prints it directly. - """ - - def __init__(self): - """Initialize the number of examples, sum of prediction. - - and device. - """ - self._num_examples = 0 - self._device = torch.device("cpu") - self._sum = torch.tensor(0.0, device=self._device) - - def reset(self): - """Reset the number of samples and total cases to zero.""" - self._num_examples = 0 - self._sum = torch.tensor(0.0, device=self._device) - - def update(self, output): - """Add the predictions. - - Args: - output: The predictions. - """ - y_pred, y = output[0].detach(), output[1].detach() - loss = torch.sum(y_pred) - self._sum += loss.to(self._device) - self._num_examples += y.shape[0] - - def compute(self): - """Compute the average of predictions. - - Raises: - ValueError: There must have at least one example. - - Returns: - The dummy loss. - """ - if self._num_examples == 0: - raise ValueError( - "Loss must have at least one example \ - before it can be computed." - ) - return self._sum.item() / self._num_examples - - -@deprecated(version="2.0") -@metric_registry("Loss", "tensorflow, tensorflow_itex, pytorch, onnxrt_qlinearops, onnxrt_integerops") -class Loss(BaseMetric): - """A dummy Metric. - - A dummy metric that computes the average of predictions and prints it directly. - - Attributes: - sample: The number of samples. - sum: The sum of prediction. - """ - - def __init__(self): - """Initialize the number of samples, sum of prediction.""" - self.sample = 0 - self.sum = 0 - - def update(self, preds, labels, sample_weight=None): - """Add the predictions and labels. - - Args: - preds: The predictions. - labels: The labels corresponding to the predictions. - sample_weight: The sample weight. - """ - preds, labels = _shape_validate(preds, labels) - self.sample += labels[0].shape[0] - self.sum += sum([np.sum(pred) for pred in preds]) - - def reset(self): - """Reset the number of samples and total cases to zero.""" - self.sample = 0 - self.sum = 0 - - def result(self): - """Compute the average of predictions. - - Returns: - The dummy loss. - """ - if getattr(self, "_hvd", None) is not None: # pragma: no cover - allgather_sum = sum(self._hvd.allgather_object(self.sum)) - allgather_sample = sum(self._hvd.allgather_object(self.sample)) - return allgather_sum / allgather_sample - return self.sum / self.sample - - -@deprecated(version="2.0") -@metric_registry("MAE", "tensorflow, tensorflow_itex, pytorch, onnxrt_qlinearops, onnxrt_integerops") -class MAE(BaseMetric): - """Computes Mean Absolute Error (MAE) loss. - - Mean Absolute Error (MAE) is the mean of the magnitude of - difference between the predicted and actual numeric values. - - Attributes: - pred_list: List of prediction to score. - label_list: List of references corresponding to the prediction result. - compare_label (bool): Whether to compare label. False if there are no - labels and will use FP32 preds as labels. - """ - - def __init__(self, compare_label=True): - """Initialize the list of prediction and labels. - - Args: - compare_label: Whether to compare label. False if there are no - labels and will use FP32 preds as labels. - """ - self.label_list = [] - self.pred_list = [] - self.compare_label = compare_label - - def update(self, preds, labels, sample_weight=None): - """Add the predictions and labels. - - Args: - preds: The predictions. - labels: The labels corresponding to the predictions. - sample_weight: The sample weight. - """ - preds, labels = _shape_validate(preds, labels) - self.label_list.extend(labels) - self.pred_list.extend(preds) - - def reset(self): - """Clear the predictions and labels.""" - self.label_list = [] - self.pred_list = [] - - def result(self): - """Compute the MAE score. - - Returns: - The MAE score. - """ - aes = [abs(a - b) for (a, b) in zip(self.label_list, self.pred_list)] - aes_sum = sum([np.sum(ae) for ae in aes]) - aes_size = sum([ae.size for ae in aes]) - assert aes_size, "predictions shouldn't be none" - if getattr(self, "_hvd", None) is not None: # pragma: no cover - aes_sum = sum(self._hvd.allgather_object(aes_sum)) - aes_size = sum(self._hvd.allgather_object(aes_size)) - return aes_sum / aes_size - - -@deprecated(version="2.0") -@metric_registry("RMSE", "tensorflow, tensorflow_itex, pytorch, mxnet, onnxrt_qlinearops, onnxrt_integerops") -class RMSE(BaseMetric): - """Computes Root Mean Squared Error (RMSE) loss. - - Attributes: - mse: The instance of MSE Metric. - """ - - def __init__(self, compare_label=True): - """Initialize the mse. - - Args: - compare_label (bool): Whether to compare label. False if there are no labels - and will use FP32 preds as labels. - """ - self.mse = MSE(compare_label) - - def update(self, preds, labels, sample_weight=None): - """Add the predictions and labels. - - Args: - preds: The predictions. - labels: The labels corresponding to the predictions. - sample_weight: The sample weight. - """ - self.mse.update(preds, labels, sample_weight) - - def reset(self): - """Clear the predictions and labels.""" - self.mse.reset() - - def result(self): - """Compute the RMSE score. - - Returns: - The RMSE score. - """ - if getattr(self, "_hvd", None) is not None: # pragma: no cover - self.mse._hvd = self._hvd - return np.sqrt(self.mse.result()) - - -@deprecated(version="2.0") -@metric_registry("MSE", "tensorflow, tensorflow_itex, pytorch, onnxrt_qlinearops, onnxrt_integerops") -class MSE(BaseMetric): - """Computes Mean Squared Error (MSE) loss. - - Mean Squared Error(MSE) represents the average of the squares of errors. - For example, the average squared difference between the estimated values - and the actual values. - - Attributes: - pred_list: List of prediction to score. - label_list: List of references corresponding to the prediction result. - compare_label (bool): Whether to compare label. False if there are no labels - and will use FP32 preds as labels. - """ - - def __init__(self, compare_label=True): - """Initialize the list of prediction and labels. - - Args: - compare_label: Whether to compare label. False if there are no - labels and will use FP32 preds as labels. - """ - self.label_list = [] - self.pred_list = [] - self.compare_label = compare_label - - def update(self, preds, labels, sample_weight=None): - """Add the predictions and labels. - - Args: - preds: The predictions. - labels: The labels corresponding to the predictions. - sample_weight: The sample weight. - """ - preds, labels = _shape_validate(preds, labels) - self.pred_list.extend(preds) - self.label_list.extend(labels) - - def reset(self): - """Clear the predictions and labels.""" - self.label_list = [] - self.pred_list = [] - - def result(self): - """Compute the MSE score. - - Returns: - The MSE score. - """ - squares = [(a - b) ** 2.0 for (a, b) in zip(self.label_list, self.pred_list)] - squares_sum = sum([np.sum(square) for square in squares]) - squares_size = sum([square.size for square in squares]) - assert squares_size, "predictions shouldn't be None" - if getattr(self, "_hvd", None) is not None: # pragma: no cover - squares_sum = sum(self._hvd.allgather_object(squares_sum)) - squares_size = sum(self._hvd.allgather_object(squares_size)) - return squares_sum / squares_size - - -@deprecated(version="2.0") -@metric_registry("topk", "tensorflow, tensorflow_itex") -class TensorflowTopK(BaseMetric): - """Compute Top-k Accuracy classification score for Tensorflow model. - - This metric computes the number of times where the correct label is among - the top k labels predicted. - - Attributes: - k (int): The number of most likely outcomes considered to find the correct label. - num_correct: The number of predictions that were correct classified. - num_sample: The total number of predictions. - """ - - def __init__(self, k=1): - """Initialize the k, number of samples and correct predictions. - - Args: - k: The number of most likely outcomes considered to find the correct label. - """ - self.k = k - self.num_correct = 0 - self.num_sample = 0 - - def update(self, preds, labels, sample_weight=None): - """Add the predictions and labels. - - Args: - preds: The predictions. - labels: The labels corresponding to the predictions. - sample_weight: The sample weight. - """ - preds, labels = _topk_shape_validate(preds, labels) - - labels = labels.reshape([len(labels)]) - with tf.Graph().as_default() as acc_graph: - topk = tf.nn.in_top_k( - predictions=tf.constant(preds, dtype=tf.float32), targets=tf.constant(labels, dtype=tf.int32), k=self.k - ) - fp32_topk = tf.cast(topk, tf.float32) - correct_tensor = tf.reduce_sum(input_tensor=fp32_topk) - - with tf.compat.v1.Session() as acc_sess: - correct = acc_sess.run(correct_tensor) - - self.num_sample += len(labels) - self.num_correct += correct - - def reset(self): - """Reset the number of samples and correct predictions.""" - self.num_correct = 0 - self.num_sample = 0 - - def result(self): - """Compute the top-k score. - - Returns: - The top-k score. - """ - if self.num_sample == 0: - logger.warning("Sample num during evaluation is 0.") - return 0 - elif getattr(self, "_hvd", None) is not None: # pragma: no cover - allgather_num_correct = sum(self._hvd.allgather_object(self.num_correct)) - allgather_num_sample = sum(self._hvd.allgather_object(self.num_sample)) - return allgather_num_correct / allgather_num_sample - return self.num_correct / self.num_sample - - -@deprecated(version="2.0") -@metric_registry("topk", "pytorch, mxnet, onnxrt_qlinearops, onnxrt_integerops") -class GeneralTopK(BaseMetric): - """Compute Top-k Accuracy classification score. - - This metric computes the number of times where the correct label is among - the top k labels predicted. - - Attributes: - k (int): The number of most likely outcomes considered to find the correct label. - num_correct: The number of predictions that were correct classified. - num_sample: The total number of predictions. - """ - - def __init__(self, k=1): - """Initialize the k, number of samples and correct predictions. - - Args: - k: The number of most likely outcomes considered to find the correct label. - """ - self.k = k - self.num_correct = 0 - self.num_sample = 0 - - def update(self, preds, labels, sample_weight=None): - """Add the predictions and labels. - - Args: - preds: The predictions. - labels: The labels corresponding to the predictions. - sample_weight: The sample weight. - """ - preds, labels = _topk_shape_validate(preds, labels) - preds = preds.argsort()[..., -self.k :] - if self.k == 1: - correct = accuracy_score(preds, labels, normalize=False) - self.num_correct += correct - - else: - for p, l in zip(preds, labels): - # get top-k labels with np.argpartition - # p = np.argpartition(p, -self.k)[-self.k:] - l = l.astype("int32") - if l in p: - self.num_correct += 1 - - self.num_sample += len(labels) - - def reset(self): - """Reset the number of samples and correct predictions.""" - self.num_correct = 0 - self.num_sample = 0 - - def result(self): - """Compute the top-k score. - - Returns: - The top-k score. - """ - if self.num_sample == 0: - logger.warning("Sample num during evaluation is 0.") - return 0 - elif getattr(self, "_hvd", None) is not None: # pragma: no cover - allgather_num_correct = sum(self._hvd.allgather_object(self.num_correct)) - allgather_num_sample = sum(self._hvd.allgather_object(self.num_sample)) - return allgather_num_correct / allgather_num_sample - return self.num_correct / self.num_sample - - -@deprecated(version="2.0") -@metric_registry("COCOmAPv2", "tensorflow, tensorflow_itex, onnxrt_qlinearops, onnxrt_integerops") -class COCOmAPv2(BaseMetric): - """Compute mean average precision of the detection task.""" - - def __init__( - self, - anno_path=None, - iou_thrs="0.5:0.05:0.95", - map_points=101, - map_key="DetectionBoxes_Precision/mAP", - output_index_mapping={"num_detections": -1, "boxes": 0, "scores": 1, "classes": 2}, - ): - """Initialize the metric. - - Args: - anno_path: The path of annotation file. - iou_thrs: Minimal value for intersection over union that allows to make decision - that prediction bounding box is true positive. You can specify one float value - between 0 to 1 or string "05:0.05:0.95" for standard COCO thresholds. - map_points: The way to calculate mAP. 101 for 101-point interpolated AP, 11 for - 11-point interpolated AP, 0 for area under PR curve. - map_key: The key that mapping to pycocotools COCOeval. - Defaults to 'DetectionBoxes_Precision/mAP'. - output_index_mapping: The output index mapping. - Defaults to {'num_detections':-1, 'boxes':0, 'scores':1, 'classes':2}. - """ - self.output_index_mapping = output_index_mapping - from .coco_label_map import category_map - - if anno_path: - import os - - import yaml - - assert os.path.exists(anno_path), "Annotation path does not exists!" - with open(anno_path, "r") as f: - label_map = yaml.safe_load(f.read()) - self.category_map_reverse = {k: v for k, v in label_map.items()} - else: - # label: index - self.category_map_reverse = {v: k for k, v in category_map.items()} - self.image_ids = [] - self.ground_truth_list = [] - self.detection_list = [] - self.annotation_id = 1 - self.category_map = category_map - self.category_id_set = set([cat for cat in self.category_map]) # index - self.iou_thrs = iou_thrs - self.map_points = map_points - self.map_key = map_key - - def update(self, predicts, labels, sample_weight=None): - """Add the predictions and labels. - - Args: - predicts: The predictions. - labels: The labels corresponding to the predictions. - sample_weight: The sample weight. Defaults to None. - """ - from .coco_tools import ExportSingleImageDetectionBoxesToCoco, ExportSingleImageGroundtruthToCoco - - detections = [] - if "num_detections" in self.output_index_mapping and self.output_index_mapping["num_detections"] > -1: - for item in zip(*predicts): - detection = {} - num = int(item[self.output_index_mapping["num_detections"]]) - detection["boxes"] = np.asarray(item[self.output_index_mapping["boxes"]])[0:num] - detection["scores"] = np.asarray(item[self.output_index_mapping["scores"]])[0:num] - detection["classes"] = np.asarray(item[self.output_index_mapping["classes"]])[0:num] - detections.append(detection) - else: - for item in zip(*predicts): - detection = {} - detection["boxes"] = np.asarray(item[self.output_index_mapping["boxes"]]) - detection["scores"] = np.asarray(item[self.output_index_mapping["scores"]]) - detection["classes"] = np.asarray(item[self.output_index_mapping["classes"]]) - detections.append(detection) - - bboxes, str_labels, int_labels, image_ids = labels - labels = [] - if len(int_labels[0]) == 0: - for str_label in str_labels: - str_label = [x if type(x) == "str" else x.decode("utf-8") for x in str_label] - labels.append([self.category_map_reverse[x] for x in str_label]) - elif len(str_labels[0]) == 0: - for int_label in int_labels: - labels.append([x for x in int_label]) - - for idx, image_id in enumerate(image_ids): - image_id = image_id if type(image_id) == "str" else image_id.decode("utf-8") - if image_id in self.image_ids: - continue - self.image_ids.append(image_id) - - ground_truth = {} - ground_truth["boxes"] = np.asarray(bboxes[idx]) - ground_truth["classes"] = np.asarray(labels[idx]) - - self.ground_truth_list.extend( - ExportSingleImageGroundtruthToCoco( - image_id=image_id, - next_annotation_id=self.annotation_id, - category_id_set=self.category_id_set, - groundtruth_boxes=ground_truth["boxes"], - groundtruth_classes=ground_truth["classes"], - ) - ) - self.annotation_id += ground_truth["boxes"].shape[0] - - self.detection_list.extend( - ExportSingleImageDetectionBoxesToCoco( - image_id=image_id, - category_id_set=self.category_id_set, - detection_boxes=detections[idx]["boxes"], - detection_scores=detections[idx]["scores"], - detection_classes=detections[idx]["classes"], - ) - ) - - def reset(self): - """Reset the prediction and labels.""" - self.image_ids = [] - self.ground_truth_list = [] - self.detection_list = [] - self.annotation_id = 1 - - def result(self): - """Compute mean average precision. - - Returns: - The mean average precision score. - """ - from .coco_tools import COCOEvalWrapper, COCOWrapper - - if len(self.ground_truth_list) == 0: - logger.warning("Sample num during evaluation is 0.") - return 0 - else: - groundtruth_dict = { - "annotations": self.ground_truth_list, - "images": [{"id": image_id} for image_id in self.image_ids], - "categories": [{"id": k, "name": v} for k, v in self.category_map.items()], - } - coco_wrapped_groundtruth = COCOWrapper(groundtruth_dict) - coco_wrapped_detections = coco_wrapped_groundtruth.LoadAnnotations(self.detection_list) - box_evaluator = COCOEvalWrapper( - coco_wrapped_groundtruth, - coco_wrapped_detections, - agnostic_mode=False, - iou_thrs=self.iou_thrs, - map_points=self.map_points, - ) - box_metrics, box_per_category_ap = box_evaluator.ComputeMetrics( - include_metrics_per_category=False, all_metrics_per_category=False - ) - box_metrics.update(box_per_category_ap) - box_metrics = {"DetectionBoxes_" + key: value for key, value in iter(box_metrics.items())} - - return box_metrics[self.map_key] - - -@deprecated(version="2.0") -@metric_registry("mAP", "tensorflow, tensorflow_itex, onnxrt_qlinearops, onnxrt_integerops") -class TensorflowMAP(BaseMetric): - """Computes mean average precision.""" - - def __init__(self, anno_path=None, iou_thrs=0.5, map_points=0, map_key="DetectionBoxes_Precision/mAP"): - """Initialize the metric. - - Args: - anno_path: The path of annotation file. - iou_thrs: Minimal value for intersection over union that allows to make decision - that prediction bounding box is true positive. You can specify one float value - between 0 to 1 or string "05:0.05:0.95" for standard COCO thresholds. - map_points: The way to calculate mAP. 101 for 101-point interpolated AP, 11 for - 11-point interpolated AP, 0 for area under PR curve. - map_key: The key that mapping to pycocotools COCOeval. - Defaults to 'DetectionBoxes_Precision/mAP'. - """ - from .coco_label_map import category_map - - if anno_path: - import os - - import yaml - - assert os.path.exists(anno_path), "Annotation path does not exists!" - with open(anno_path, "r") as f: - label_map = yaml.safe_load(f.read()) - self.category_map_reverse = {k: v for k, v in label_map.items()} - else: - # label: index - self.category_map_reverse = {v: k for k, v in category_map.items()} - self.image_ids = [] - self.ground_truth_list = [] - self.detection_list = [] - self.annotation_id = 1 - self.category_map = category_map - self.category_id_set = set([cat for cat in self.category_map]) # index - self.iou_thrs = iou_thrs - self.map_points = map_points - self.map_key = map_key - - def update(self, predicts, labels, sample_weight=None): - """Add the predictions and labels. - - Args: - predicts: The predictions. - labels: The labels corresponding to the predictions. - sample_weight: The sample weight. - """ - if getattr(self, "_hvd", None) is not None: # pragma: no cover - raise NotImplementedError("Metric TensorflowMAP currently do not support distributed inference.") - - from .coco_tools import ExportSingleImageDetectionBoxesToCoco, ExportSingleImageGroundtruthToCoco - - detections = [] - if len(predicts) == 3: - for bbox, score, cls in zip(*predicts): - detection = {} - detection["boxes"] = np.asarray(bbox) - detection["scores"] = np.asarray(score) - detection["classes"] = np.asarray(cls) - detections.append(detection) - elif len(predicts) == 4: - for num, bbox, score, cls in zip(*predicts): - detection = {} - num = int(num) - detection["boxes"] = np.asarray(bbox)[0:num] - detection["scores"] = np.asarray(score)[0:num] - detection["classes"] = np.asarray(cls)[0:num] - detections.append(detection) - else: - raise ValueError("Unsupported prediction format!") - - bboxes, str_labels, int_labels, image_ids = labels - labels = [] - if len(int_labels[0]) == 0: - for str_label in str_labels: - str_label = [x if type(x) == "str" else x.decode("utf-8") for x in str_label] - labels.append([self.category_map_reverse[x] for x in str_label]) - elif len(str_labels[0]) == 0: - for int_label in int_labels: - labels.append([x for x in int_label]) - - for idx, image_id in enumerate(image_ids): - image_id = image_id if type(image_id) == "str" else image_id.decode("utf-8") - if image_id in self.image_ids: - continue - self.image_ids.append(image_id) - - ground_truth = {} - ground_truth["boxes"] = np.asarray(bboxes[idx]) - ground_truth["classes"] = np.asarray(labels[idx]) - - self.ground_truth_list.extend( - ExportSingleImageGroundtruthToCoco( - image_id=image_id, - next_annotation_id=self.annotation_id, - category_id_set=self.category_id_set, - groundtruth_boxes=ground_truth["boxes"], - groundtruth_classes=ground_truth["classes"], - ) - ) - self.annotation_id += ground_truth["boxes"].shape[0] - - self.detection_list.extend( - ExportSingleImageDetectionBoxesToCoco( - image_id=image_id, - category_id_set=self.category_id_set, - detection_boxes=detections[idx]["boxes"], - detection_scores=detections[idx]["scores"], - detection_classes=detections[idx]["classes"], - ) - ) - - def reset(self): - """Reset the prediction and labels.""" - self.image_ids = [] - self.ground_truth_list = [] - self.detection_list = [] - self.annotation_id = 1 - - def result(self): - """Compute mean average precision. - - Returns: - The mean average precision score. - """ - from .coco_tools import COCOEvalWrapper, COCOWrapper - - if len(self.ground_truth_list) == 0: - logger.warning("Sample num during evaluation is 0.") - return 0 - else: - groundtruth_dict = { - "annotations": self.ground_truth_list, - "images": [{"id": image_id} for image_id in self.image_ids], - "categories": [{"id": k, "name": v} for k, v in self.category_map.items()], - } - coco_wrapped_groundtruth = COCOWrapper(groundtruth_dict) - coco_wrapped_detections = coco_wrapped_groundtruth.LoadAnnotations(self.detection_list) - box_evaluator = COCOEvalWrapper( - coco_wrapped_groundtruth, - coco_wrapped_detections, - agnostic_mode=False, - iou_thrs=self.iou_thrs, - map_points=self.map_points, - ) - box_metrics, box_per_category_ap = box_evaluator.ComputeMetrics( - include_metrics_per_category=False, all_metrics_per_category=False - ) - box_metrics.update(box_per_category_ap) - box_metrics = {"DetectionBoxes_" + key: value for key, value in iter(box_metrics.items())} - - return box_metrics[self.map_key] - - -@deprecated(version="2.0") -@metric_registry("COCOmAP", "tensorflow, tensorflow_itex, onnxrt_qlinearops, onnxrt_integerops") -class TensorflowCOCOMAP(TensorflowMAP): - """Computes mean average precision using algorithm in COCO.""" - - def __init__(self, anno_path=None, iou_thrs=None, map_points=None, map_key="DetectionBoxes_Precision/mAP"): - """Initialize the iou threshold and max points. - - Args: - anno_path: The path of annotation file. - iou_thrs: Minimal value for intersection over union that allows to make decision - that prediction bounding box is true positive. You can specify one float value - between 0 to 1 or string "05:0.05:0.95" for standard COCO thresholds. - map_points: The way to calculate mAP. 101 for 101-point interpolated AP, 11 for - 11-point interpolated AP, 0 for area under PR curve. - map_key: The key that mapping to pycocotools COCOeval. - Defaults to 'DetectionBoxes_Precision/mAP'. - """ - super(TensorflowCOCOMAP, self).__init__(anno_path, iou_thrs, map_points, map_key) - self.iou_thrs = "0.5:0.05:0.95" - self.map_points = 101 - - -@deprecated(version="2.0") -@metric_registry("VOCmAP", "tensorflow, tensorflow_itex, onnxrt_qlinearops, onnxrt_integerops") -class TensorflowVOCMAP(TensorflowMAP): - """Computes mean average precision using algorithm in VOC.""" - - def __init__(self, anno_path=None, iou_thrs=None, map_points=None, map_key="DetectionBoxes_Precision/mAP"): - """Initialize the iou threshold and max points. - - Args: - anno_path: The path of annotation file. - iou_thrs: Minimal value for intersection over union that allows to make decision - that prediction bounding box is true positive. You can specify one float value - between 0 to 1 or string "05:0.05:0.95" for standard COCO thresholds. - map_points: The way to calculate mAP. 101 for 101-point interpolated AP, 11 for - 11-point interpolated AP, 0 for area under PR curve. - map_key: The key that mapping to pycocotools COCOeval. - Defaults to 'DetectionBoxes_Precision/mAP'. - """ - super(TensorflowVOCMAP, self).__init__(anno_path, iou_thrs, map_points, map_key) - self.iou_thrs = 0.5 - self.map_points = 0 - - -@deprecated(version="2.0") -@metric_registry("SquadF1", "tensorflow, tensorflow_itex") -class SquadF1(BaseMetric): - """Evaluate for v1.1 of the SQuAD dataset.""" - - def __init__(self): - """Initialize the score list.""" - self._score_list = [] # squad metric only work when all data preds collected - - def update(self, preds, labels, sample_weight=None): - """Add the predictions and labels. - - Args: - preds: The predictions. - labels: The labels corresponding to the predictions. - sample_weight: The sample weight. - """ - if preds: - from .evaluate_squad import evaluate - - if getattr(self, "_hvd", None) is not None: # pragma: no cover - gathered_preds_list = self._hvd.allgather_object(preds) - gathered_labels_list = self._hvd.allgather_object(labels) - temp_preds_list, temp_labels_list = [], [] - for i in range(0, self._hvd.size()): - temp_preds_list += gathered_preds_list[i] - temp_labels_list += gathered_labels_list[i] - preds = temp_preds_list - labels = temp_labels_list - result = evaluate(labels, preds) - self._score_list.append(result["f1"]) - - def reset(self): - """Reset the score list.""" - self._score_list = [] - - def result(self): - """Compute F1 score.""" - if len(self._score_list) == 0: - return 0.0 - return np.array(self._score_list).mean() - - -@deprecated(version="2.0") -@metric_registry("mIOU", "tensorflow, tensorflow_itex") -class mIOU(BaseMetric): - """Compute the mean IOU(Intersection over Union) score.""" - - def __init__(self, num_classes=21): - """Initialize the number of classes. - - Args: - num_classes: The number of classes. - """ - self.num_classes = num_classes - self.hist = np.zeros((num_classes, num_classes)) - - def update(self, preds, labels): - """Add the predictions and labels. - - Args: - preds: The predictions. - labels: The labels corresponding to the predictions. - """ - preds = preds.flatten() - labels = labels.flatten() - p_dtype = preds.dtype - l_dtype = labels.dtype - if getattr(self, "_hvd", None) is not None: # pragma: no cover - preds = self._hvd.allgather_object(preds) - labels = self._hvd.allgather_object(labels) - preds_list, labels_list = np.array([], dtype=p_dtype), np.array([], dtype=l_dtype) - for i in range(self._hvd.size()): - preds_list = np.append(preds_list, preds[i]) - labels_list = np.append(labels_list, labels[i]) - preds, labels = preds_list, labels_list - mask = (labels >= 0) & (labels < self.num_classes) - self.hist += np.bincount( - self.num_classes * labels[mask].astype(int) + preds[mask], minlength=self.num_classes**2 - ).reshape(self.num_classes, self.num_classes) - - def reset(self): - """Reset the hist.""" - self.hist = np.zeros((self.num_classes, self.num_classes)) - - def result(self): - """Compute mean IOU. - - Returns: - The mean IOU score. - """ - iu = np.diag(self.hist) / (self.hist.sum(axis=1) + self.hist.sum(axis=0) - np.diag(self.hist)) - mean_iu = np.nanmean(iu) - return mean_iu - - -@deprecated(version="2.0") -@metric_registry("GLUE", "onnxrt_qlinearops, onnxrt_integerops") -class ONNXRTGLUE(BaseMetric): - """Compute the GLUE score.""" - - def __init__(self, task="mrpc"): - """Initialize the metric. - - Args: - task:The name of the task (Choices: mrpc, qqp, qnli, rte, - sts-b, cola, mnli, wnli.). - """ - assert task in ["mrpc", "qqp", "qnli", "rte", "sts-b", "cola", "mnli", "wnli", "sst-2"], "Unsupported task type" - self.pred_list = None - self.label_list = None - self.task = task - self.return_key = { - "cola": "mcc", - "mrpc": "acc", - "sts-b": "corr", - "qqp": "acc", - "mnli": "mnli/acc", - "qnli": "acc", - "rte": "acc", - "wnli": "acc", - "sst-2": "acc", - } - - def update(self, preds, labels): - """Add the predictions and labels. - - Args: - preds: The predictions. - labels: The labels corresponding to the predictions. - """ - if getattr(self, "_hvd", None) is not None: - raise NotImplementedError("Metric ONNXRTGLUE currently do not support distributed inference.") - if isinstance(preds, list) and len(preds) == 1: - preds = preds[0] - if isinstance(labels, list) and len(labels) == 1: - labels = labels[0] - if self.pred_list is None: - self.pred_list = preds - self.label_list = labels - else: - self.pred_list = np.append(self.pred_list, preds, axis=0) - self.label_list = np.append(self.label_list, labels, axis=0) - - def reset(self): - """Reset the prediction and labels.""" - self.pred_list = None - self.label_list = None - - def result(self): - """Compute the GLUE score.""" - output_mode = transformers.glue_output_modes[self.task] - - if output_mode == "classification": - processed_preds = np.argmax(self.pred_list, axis=1) - elif output_mode == "regression": # pragma: no cover - processed_preds = np.squeeze(self.pred_list) - result = transformers.glue_compute_metrics(self.task, processed_preds, self.label_list) - return result[self.return_key[self.task]] - - -@deprecated(version="2.0") -@metric_registry("ROC", "pytorch") -class ROC(BaseMetric): - """Computes ROC score.""" - - def __init__(self, task="dlrm"): - """Initialize the metric. - - Args: - task:The name of the task (Choices: dlrm, dien, wide_deep.). - """ - assert task in ["dlrm", "dien", "wide_deep"], "Unsupported task type" - self.pred_list = None - self.label_list = None - self.task = task - self.return_key = { - "dlrm": "acc", - "dien": "acc", - "wide_deep": "acc", - } - - def update(self, preds, labels): - """Add the predictions and labels. - - Args: - preds: The predictions. - labels: The labels corresponding to the predictions. - """ - if isinstance(preds, list) and len(preds) == 1: - preds = preds[0] - if isinstance(labels, list) and len(labels) == 1: - labels = labels[0] - if self.pred_list is None: - self.pred_list = preds - self.label_list = labels - else: - self.pred_list = np.append(self.pred_list, preds, axis=0) - self.label_list = np.append(self.label_list, labels, axis=0) - - def reset(self): - """Reset the prediction and labels.""" - self.pred_list = None - self.label_list = None - - def result(self): - """Compute the ROC score.""" - import sklearn.metrics - - scores = np.squeeze(self.pred_list) - targets = np.squeeze(self.label_list) - roc_auc = sklearn.metrics.roc_auc_score(targets, scores) - acc = sklearn.metrics.accuracy_score(targets, np.round(scores)) - return acc diff --git a/neural_compressor/experimental/mixed_precision.py b/neural_compressor/experimental/mixed_precision.py deleted file mode 100644 index 03c092347de..00000000000 --- a/neural_compressor/experimental/mixed_precision.py +++ /dev/null @@ -1,232 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ============================================================================== -"""Class for low precision model generation across multiple framework backends.""" -import os -import pickle -import random -import sys - -import numpy as np -from deprecated import deprecated - -from ..conf.config import MixedPrecision_Conf -from ..conf.dotdict import deep_get -from ..conf.pythonic_config import Config -from ..model import BaseModel -from ..utils import logger -from ..utils.create_obj_from_config import create_dataloader -from ..utils.utility import CpuInfo, time_limit -from .graph_optimization import GraphOptimization -from .strategy import EXP_STRATEGIES - - -@deprecated(version="2.0") -class MixedPrecision(GraphOptimization): - """Class used for generating low precision model. - - MixedPrecision class automatically generates low precision model across various DL - frameworks including tensorflow, pytorch and onnxruntime. - """ - - def __init__(self, conf_fname_or_obj=None): - """Initialize `MixedPrecision` class. - - Args: - conf_fname_or_obj (string or obj): The path to the YAML configuration file or - Graph_Optimization_Conf class containing accuracy goal, tuning objective and - preferred calibration & quantization tuning space etc. - """ - self.conf_name = conf_fname_or_obj - self._model = None - self._eval_dataloader = None - self._eval_func = None - self._precisions = "fp32" - self._input = None - self._output = None - self.conf = None - - if isinstance(conf_fname_or_obj, MixedPrecision_Conf): - self.conf = conf_fname_or_obj - elif isinstance(conf_fname_or_obj, Config): - self.conf = MixedPrecision_Conf() - self.conf.map_pyconfig_to_cfg(conf_fname_or_obj) - else: - self.conf = MixedPrecision_Conf(conf_fname_or_obj) - cfg = self.conf.usr_cfg - if cfg.model.framework != "NA": - self.framework = cfg.model.framework.lower() - - cfg.tuning.strategy.name = "automixedprecision" - seed = cfg.tuning.random_seed - random.seed(seed) - np.random.seed(seed) - - def __call__(self): - """Call `MixedPrecision` class. The main entry point of mixed precision process. - - This interface works on all the DL frameworks that neural_compressor supports - and provides three usages: - a) Fully yaml configuration: User specifies all the info through yaml, - including dataloaders used in calibration and evaluation phases - and quantization tuning settings. - - For this usage, only model parameter is mandatory. - - b) Partial yaml configuration: User specifies dataloaders used in evaluation phase by code. - The tool provides built-in dataloaders and evaluators, user just need provide - a dataset implemented __iter__ or __getitem__ methods and invoke dataloader() - with dataset as input parameter to create neural_compressor dataloader before calling this - function. - - After that, User specifies fp32 "model" and evaluation dataset "eval_dataloader". - The converted model is evaluated with "eval_dataloader" with evaluation metrics - specified in the configuration file. The evaluation tells the tuner whether the - converted model meets the accuracy criteria. If not, the tuner starts a tuning flow. - - For this usage, model and eval_dataloader parameters are mandatory. - - Returns: - converted model: best converted model found, otherwise return None - """ - assert isinstance(self._model, BaseModel), "need set your Model for mixed precision...." - if "onnx" in self.framework and "bf16" in self._precisions: - logger.warning("Mixed precision doesn't support bf16 for ONNX models.") - self._precisions.remove("bf16") - - if "bf16" in self._precisions and not CpuInfo().bf16: # pragma: no cover - if os.getenv("FORCE_BF16") == "1": - logger.warning( - "Mixed precision will generate bf16 graph although " - "the hardware doesn't support bf16 instruction." - ) - else: - logger.warning("Mixed precision exits due to the hardware " "doesn't support bf16 instruction.") - self._precisions.remove("bf16") - - if "fp16" in self._precisions and "gpu" not in self.conf.usr_cfg.device: - if os.getenv("FORCE_FP16") == "1": - logger.warning( - "Mixed precision will generate fp16 graph although " - "the hardware doesn't support fp16 instruction." - ) - else: - logger.warning("Mixed precision exits due to the hardware " "doesn't support fp16 instruction.") - self._precisions.remove("fp16") - - if self._precisions == ["fp32"] or len(self._precisions) == 0: - sys.exit(0) - - cfg = self.conf.usr_cfg - if self.framework == "tensorflow": - self._model.name = cfg.model.name - self._model.output_tensor_names = cfg.model.outputs if not self._output else self._output - self._model.input_tensor_names = cfg.model.inputs if not self._input else self._input - self._model.workspace_path = cfg.tuning.workspace.path - if ( - "bf16" in self._precisions - or (cfg.mixed_precision and "bf16" in cfg.mixed_precision.precisions) - or (cfg.graph_optimization and "bf16" in cfg.graph_optimization.precisions) - ): - cfg.use_bf16 = True - - # when eval_func is set, will be directly used and eval_dataloader can be None - if self._eval_func is None: - if self._eval_dataloader is None: - eval_dataloader_cfg = deep_get(cfg, "evaluation.accuracy.dataloader") - if eval_dataloader_cfg is None: - self._eval_func = None - else: - self._eval_dataloader = create_dataloader(self.framework, eval_dataloader_cfg) - - strategy = cfg.tuning.strategy.name.lower() - - assert strategy in EXP_STRATEGIES, "Tuning strategy {} is NOT supported".format(strategy) - - _resume = None - # check if interrupted tuning procedure exists. if yes, it will resume the - # whole auto tune process. - self.resume_file = ( - os.path.abspath(os.path.expanduser(cfg.tuning.workspace.resume)) - if cfg.tuning.workspace and cfg.tuning.workspace.resume - else None - ) - if self.resume_file: # pragma: no cover - assert os.path.exists(self.resume_file), "The specified resume file {} doesn't exist!".format( - self.resume_file - ) - with open(self.resume_file, "rb") as f: - _resume = pickle.load(f).__dict__ - - self.strategy = EXP_STRATEGIES[strategy]( - self._model, self.conf, None, None, self._eval_dataloader, self._eval_func, _resume - ) - - try: - with time_limit(self.conf.usr_cfg.tuning.exit_policy.timeout): - self.strategy.traverse() - except KeyboardInterrupt: # pragma: no cover - pass - except Exception as e: # pragma: no cover - logger.info("Unexpected exception {} happened during turing.".format(repr(e))) - finally: - if self.strategy.best_qmodel: - logger.info( - "Specified timeout or max trials is reached! " - "Found a converted model which meet accuracy goal. Exit." - ) - self.strategy.deploy_config() - else: # pragma: no cover - logger.info( - "Specified timeout or max trials is reached! " - "Not found any converted model which meet accuracy goal. Exit." - ) - - logger.info("Mixed Precision is done. Please invoke model.save() to save " "optimized model to disk.") - - return self.strategy.best_qmodel - - fit = __call__ - - @property - def precisions(self): - """Get private member variable `precisions` of `MixedPrecision` class.""" - return self._precisions - - @precisions.setter - def precisions(self, customized_precisions): - """Set private member variable `precisions` of `MixedPrecision` class.""" - if isinstance(customized_precisions, list): - self._precisions = sorted([i.strip() for i in customized_precisions]) - elif isinstance(customized_precisions, str): - self._precisions = sorted([i.strip() for i in customized_precisions.split(",")]) - self.conf.usr_cfg.mixed_precision.precisions = self._precisions - - def set_config_by_model(self, model_obj): - """Set member variable `conf` by a input model object.""" - self.conf.usr_cfg.model.framework = model_obj.framework() - if self._input: - self.conf.usr_cfg.model.inputs = self._input - if self._output: - if isinstance(self._output, str) and "," in self._output: - self.conf.usr_cfg.model.outputs = [s.strip() for s in self._output.split(",")] - else: - self.conf.usr_cfg.model.outputs = self._output - - def __repr__(self): - """Return 'MixedPrecision'.""" - return "MixedPrecision" diff --git a/neural_compressor/experimental/model_conversion.py b/neural_compressor/experimental/model_conversion.py deleted file mode 100644 index 700ec725063..00000000000 --- a/neural_compressor/experimental/model_conversion.py +++ /dev/null @@ -1,367 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Helps convert one model format to another.""" - -import datetime -import tempfile - -import yaml -from deprecated import deprecated - -from neural_compressor.adaptor import FRAMEWORKS - -from ..conf.config import Conf -from ..conf.dotdict import DotDict, deep_get, deep_set -from ..model import BaseModel -from ..utils import logger -from ..utils.create_obj_from_config import create_dataloader, create_eval_func -from .common import Model as NCModel - - -@deprecated(version="2.0") -class ModelConversion: # pragma: no cover - """ModelConversion class is used to convert one model format to another. - - Currently Neural Compressor only supports Quantization-aware training TensorFlow model to Default - quantized model. - - The typical usage is: - from neural_compressor.experimental import ModelConversion, common - conversion = ModelConversion() - conversion.source = 'QAT' - conversion.destination = 'default' - conversion.model = '/path/to/saved_model' - q_model = conversion() - - Args: - conf_fname_or_obj (string or obj): Optional. The path to the YAML configuration file or - Conf class containing model conversion and evaluation setting if not specified by code. - """ - - def __init__(self, conf_fname_or_obj=None): - """Initialize the ModelConversion class. - - Args: - conf_fname_or_obj (string or obj): Optional. The path to the YAML configuration file or - Conf class containing model conversion and evaluation setting if not specified by code. - """ - self.conf_name = conf_fname_or_obj - self._model = None - self.framework = "tensorflow" - - self._eval_dataloader = None - self._eval_func = None - self.adaptor = None - self._metric = None - - self._source = None - self._destination = None - - if conf_fname_or_obj is not None: - if isinstance(conf_fname_or_obj, str): - self.conf = Conf(conf_fname_or_obj) - elif isinstance(conf_fname_or_obj, Conf): - self.conf = conf_fname_or_obj - else: # pragma: no cover - assert ( - False - ), "Please pass a YAML configuration file path or \ - Conf class to model_conversion" - else: - self.conf = None - - def __call__(self): - """Execute model conversion process. - - NOTE: This interface works now only on Intel Optimized TensorFlow to - convert QAT model generated by tensorflow_model_optimization to default - quantized model which is able to run at Intel Xeon platforms. - https://github.com/tensorflow/model-optimization - - Returns: - converted quantized model - """ - assert self._model, '"model" property need to be set before __call_() gets invoked' - - framework_specific_info = {} - cfg = self.conf.usr_cfg - framework_specific_info.update( - { - "name": cfg.model.name, - "backend": "default", - "format": "default", - "device": cfg.device, - "fake_quant": True, - "inputs": cfg.model.inputs, - "outputs": cfg.model.outputs, - "workspace_path": cfg.tuning.workspace.path, - } - ) - - self.adaptor = FRAMEWORKS[self.framework](framework_specific_info) - q_model = self.adaptor.convert(self._model, self._source, self._destination) - - # when eval_func is None but metric or _eval_dataloader is set by yaml or code, - # it means Neural Compressor will create the eval_func from these info. - metric_cfg = [self._metric] if self._metric else deep_get(cfg, "evaluation.accuracy.metric") - postprocess_cfg = deep_get(cfg, "evaluation.accuracy.postprocess") - if self._eval_func is None and metric_cfg: - eval_dataloader_cfg = deep_get(cfg, "evaluation.accuracy.dataloader") - if self._eval_dataloader is None and eval_dataloader_cfg: - self._eval_dataloader = create_dataloader(self.framework, eval_dataloader_cfg) - assert self._eval_dataloader, ( - 'either "eval_dataloader" property or evaluation' - ".accuracy.dataloader field in yaml should be set when metric is set" - ) - - self._eval_func = create_eval_func( - self.framework, self.eval_dataloader, self.adaptor, metric_cfg, postprocess_cfg, fp32_baseline=True - ) - if self._eval_func: - baseline_score = self._eval_func(self._model) - qmodel_score = self._eval_func(q_model) - logger.info("The score of Quantization-Aware Training model is {}.".format(str(baseline_score))) - logger.info("Converted model score is {}.".format(str(qmodel_score))) - - return q_model - - fit = __call__ - - def _gen_yaml(self): - random_name = "{}".format(datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S")) - default_yaml_template = { - "model": {"framework": self.framework, "name": random_name}, - "device": "cpu", - "model_conversion": {"source": "QAT", "destination": "default"}, - } - - temp_yaml_path = tempfile.mkstemp(suffix=".yaml")[1] - with open(temp_yaml_path, "w", encoding="utf-8") as f: - yaml.dump(default_yaml_template, f) - self.conf = Conf(temp_yaml_path) - - def dataset(self, dataset_type, *args, **kwargs): - """Return dataset. - - Args: - dataset_type: dataset type - - Returns: - class: dataset class - """ - from .data import Datasets - - return Datasets(self.framework)[dataset_type](*args, **kwargs) - - @property - def source(self): - """Return source.""" - return self._source - - @source.setter - def source(self, _source): - """Set source.""" - assert _source.lower() == "qat", ( - "Model conversion now only supports TensorFlow " "QAT model to default quantized model" - ) - self._source = _source.lower() - - @property - def destination(self): - """Return destination.""" - return self._destination - - @destination.setter - def destination(self, _destination): - """Set destination.""" - assert _destination.lower() == "default", ( - "Model conversion now only supports " "TensorFlow QAT model to default quantized model" - ) - self._destination = _destination.lower() - - @property - def eval_dataloader(self): - """Return eval dataloader.""" - return self._eval_dataloader - - @eval_dataloader.setter - def eval_dataloader(self, dataloader): - """Set Data loader for evaluation. - - It is iterable and the batched data should consists of a tuple like (input, label), - when eval_dataloader is set, user should configure postprocess(optional) and metric - in yaml file or set postprocess and metric cls. Notice evaluation dataloader will be - used to generate data for model inference, make sure the input data can be feed to model. - - Args: - dataloader(generator): user are supported to set a user defined dataloader - which meet the requirements that can yield tuple of - (input, label)/(input, _) batched data. - Another good practice is to use neural_compressor.common.DataLoader - to initialize a neural_compressor dataloader object. - Notice neural_compressor.common.DataLoader is just a wrapper of the - information needed to build a dataloader, it can't yield - batched data and only in this setter method - a 'real' eval_dataloader will be created, - the reason is we have to know the framework info - and only after the Quantization object created then - framework information can be known. Future we will support - creating iterable dataloader from neural_compressor.common.DataLoader - """ - from .common import _generate_common_dataloader - - self._eval_dataloader = _generate_common_dataloader(dataloader, self.framework) - - @property - def model(self): - """Return model.""" - return self._model - - @model.setter - def model(self, user_model): - """Set the user model and dispatch to framework specific internal model object. - - Args: - user_model: user are supported to set model from original framework model format - (eg, tensorflow frozen_pb or path to a saved model), but not recommended. - Best practice is to set from a initialized neural_compressor.common.Model. - If tensorflow model is used, model's inputs/outputs will be auto inferred, - but sometimes auto inferred inputs/outputs will not meet your requests, - set them manually in config yaml file. Another corner case is slim model - of tensorflow, be careful of the name of model configured in yaml file, - make sure the name is in supported slim model list. - """ - if not isinstance(user_model, BaseModel): - logger.warning("Force convert framework model to neural_compressor model.") - self._model = NCModel(user_model) - else: - self._model = user_model - - assert self.framework == "tensorflow", "Model conversion only supports Tensorflow at current stage." - - if not self.conf: - self._gen_yaml() - - cfg = self.conf.usr_cfg - if self.framework == "tensorflow": - self._model.name = cfg.model.name - self._model.workspace_path = cfg.tuning.workspace.path - - @property - def metric(self): - """Return metric.""" - assert False, "Should not try to get the value of `metric` attribute." - return None - - @metric.setter - def metric(self, user_metric): - """Set the metric class. - - Set metric class and neural_compressor will initialize this class when evaluation - neural_compressor have many built-in metrics, but user can set specific metric through - this api. The metric class should take the outputs of the model or - postprocess(if have) as inputs, neural_compressor built-in metric always take - (predictions, labels) as inputs for update, - and user_metric.metric_cls should be sub_class of neural_compressor.metric.BaseMetric - or user defined metric object. - - Args: - user_metric(neural_compressor.common.Metric): user_metric should be object initialized from - neural_compressor.common.Metric, in this method the - user_metric.metric_cls will be registered to - specific frameworks and initialized. - """ - if deep_get(self.conf.usr_cfg, "evaluation.accuracy.metric"): - logger.warning( - "Override the value of `metric` field defined in yaml file" - " as user defines the value of `metric` attribute by code." - ) - - from .common import Metric as NCMetric - - if isinstance(user_metric, NCMetric): - metric_cfg = {user_metric.name: {**user_metric.kwargs}} - deep_set(self.conf.usr_cfg, "evaluation.accuracy.metric", metric_cfg) - self.conf.usr_cfg = DotDict(self.conf.usr_cfg) - from .metric import METRICS - - metrics = METRICS(self.framework) - metrics.register(user_metric.name, user_metric.metric_cls) - else: - for i in ["reset", "update", "result"]: - assert hasattr(user_metric, i), "Please realise {} function" "in user defined metric".format(i) - self._metric = user_metric - - @property - def postprocess(self): - """Check postprocess.""" - assert False, "Should not try to get the value of `postprocess` attribute." - return None - - @postprocess.setter - def postprocess(self, user_postprocess): - """Set postprocess class and neural_compressor will initialize this class when evaluation. - - The postprocess class should take the outputs of the model as inputs, and - output (predictions, labels) as inputs for metric update. - user_postprocess.postprocess_cls should be sub_class of neural_compressor.data.BaseTransform. - - Args: - user_postprocess(neural_compressor.common.Postprocess):user_postprocess should be - object initialized from neural_compressor.common.Postprocess, - in this method the user_postprocess.postprocess_cls will be - registered to specific frameworks and initialized. - """ - from .common import Postprocess as NCPostprocess - - assert isinstance( - user_postprocess, NCPostprocess - ), "please initialize a neural_compressor.common.Postprocess and set...." - postprocess_cfg = {user_postprocess.name: {**user_postprocess.kwargs}} - if deep_get(self.conf.usr_cfg, "evaluation.accuracy.postprocess"): - logger.warning( - "Override the value of `postprocess` field defined in yaml file" - " as user defines the value of `postprocess` attribute by code." - ) - deep_set(self.conf.usr_cfg, "evaluation.accuracy.postprocess.transform", postprocess_cfg) - from .data import TRANSFORMS - - postprocesses = TRANSFORMS(self.framework, "postprocess") - postprocesses.register(user_postprocess.name, user_postprocess.postprocess_cls) - - @property - def eval_func(self): - """Return eval_func.""" - assert False, "Should not try to get the value of `eval_func` attribute." - return None - - @eval_func.setter - def eval_func(self, user_eval_func): - """Set the evaluation function provided by user. - - Args: - user_eval_func: This function takes model as parameter, - and evaluation dataset and metrics should be - encapsulated in this function implementation - and outputs a higher-is-better accuracy scalar - value. - """ - self._eval_func = user_eval_func - - def __repr__(self): - """Return representation.""" - return "ModelConversion" diff --git a/neural_compressor/experimental/nas/__init__.py b/neural_compressor/experimental/nas/__init__.py deleted file mode 100644 index dbca01e1f36..00000000000 --- a/neural_compressor/experimental/nas/__init__.py +++ /dev/null @@ -1,22 +0,0 @@ -"""NAS module.""" - -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from .basic_nas import BasicNAS -from .dynas import DyNAS -from .nas import NAS diff --git a/neural_compressor/experimental/nas/basic_nas.py b/neural_compressor/experimental/nas/basic_nas.py deleted file mode 100644 index 44a3c5a909e..00000000000 --- a/neural_compressor/experimental/nas/basic_nas.py +++ /dev/null @@ -1,144 +0,0 @@ -"""Basic NAS approach class.""" - -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import os - -from deprecated import deprecated - -from neural_compressor.adaptor import FRAMEWORKS -from neural_compressor.conf.config import Conf, NASConfig -from neural_compressor.experimental.component import Component -from neural_compressor.utils.create_obj_from_config import create_dataloader, create_eval_func, create_train_func - -from .nas import NASBase -from .nas_utils import nas_registry - - -@deprecated(version="2.0") -@nas_registry("Basic") -class BasicNAS(NASBase, Component): - """Basic NAS approach. - - Defining the pipeline for basic NAS approach. - - Args: - conf_fname (string): The path to the YAML configuration file. - search_space (dict): A dictionary for defining the search space. - model_builder (function obj): A function to build model instance with the specified - model architecture parameters. - """ - - def __init__(self, conf_fname_or_obj, search_space=None, model_builder=None): - """Initialize the attributes.""" - NASBase.__init__(self, search_space=search_space, model_builder=model_builder) - Component.__init__(self) - self.init_by_cfg(conf_fname_or_obj) - - def execute(self): - """Execute the search process. - - Returns: - Best model architectures found in the search process. - """ - return self.search() - - def estimate(self, model): - """Estimate performance of the model. - - Depends on specific NAS algorithm. Here we use train and evaluate. - - Returns: - Evaluated metrics of the model. - """ - assert self._train_func is not None and self._eval_func is not None, "train_func and eval_func must be set." - self._train_func(model) - return self._eval_func(model) - - def init_by_cfg(self, conf_fname_or_obj): - """Initialize the configuration.""" - if isinstance(conf_fname_or_obj, str): - if os.path.isfile(conf_fname_or_obj): - self.conf = Conf(conf_fname_or_obj) - else: # pragma: no cover - raise FileNotFoundError( - "{} is not a file, please provide a NAS config file path.".format(conf_fname_or_obj) - ) - elif isinstance(conf_fname_or_obj, NASConfig): - conf_fname_or_obj.validate() - self.conf = conf_fname_or_obj - else: # pragma: no cover - raise NotImplementedError("Please provide a str path to the config file or an object of NASConfig.") - self._init_with_conf() - assert self.cfg.nas is not None, "nas section must be set" - # search related config - self.init_search_cfg(self.cfg.nas) - - def pre_process(self): - """Initialize the train and evaluation settings.""" - framework_specific_info = { - "device": self.cfg.device, - "backend": self.cfg.backend, - "random_seed": self.cfg.tuning.random_seed, - "workspace_path": self.cfg.tuning.workspace.path, - "q_dataloader": None, - } - - if self.framework == "tensorflow" or self.framework == "tensorflow_itex": - framework_specific_info.update({"inputs": self.cfg.model.inputs, "outputs": self.cfg.model.outputs}) - - self.adaptor = FRAMEWORKS[self.framework](framework_specific_info) - - # create dataloaders - if self._train_dataloader is None and self._train_func is None: - train_dataloader_cfg = self.cfg.train.dataloader - assert train_dataloader_cfg is not None, ( - "No training dataloader setting in current component. Please check " - "dataloader field of train field in yaml file. Or manually pass " - "dataloader to component." - ) - - self._train_dataloader = create_dataloader(self.framework, train_dataloader_cfg) - if self._eval_dataloader is None and self._eval_func is None: - eval_dataloader_cfg = self.cfg.evaluation.accuracy.dataloader - assert eval_dataloader_cfg is not None, ( - "No evaluation dataloader setting in current component. Please check " - "dataloader field of evaluation field in yaml file. Or manually pass " - "dataloader to component." - ) - self._eval_dataloader = create_dataloader(self.framework, eval_dataloader_cfg) - - # create functions - if self._train_func is None: - self._train_func = create_train_func( - self.framework, self._train_dataloader, self.adaptor, self.cfg.train, hooks=self.hooks - ) - if self._eval_func is None: - metric = [self._metric] if self._metric else self.cfg.evaluation.accuracy.metric - self._eval_func = create_eval_func( - self.framework, - self._eval_dataloader, - self.adaptor, - metric, - self.cfg.evaluation.accuracy.postprocess, - fp32_baseline=False, - ) - - def __repr__(self): - """Class representation.""" - return "BasicNAS" # pragma: no cover diff --git a/neural_compressor/experimental/nas/dynas.py b/neural_compressor/experimental/nas/dynas.py deleted file mode 100644 index 4e0e79564c6..00000000000 --- a/neural_compressor/experimental/nas/dynas.py +++ /dev/null @@ -1,113 +0,0 @@ -"""DyNAS approach class.""" - -# Copyright (c) 2022 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import os - -from deprecated import deprecated - -from neural_compressor.conf.config import Conf, NASConfig -from neural_compressor.utils import logger -from neural_compressor.utils.utility import LazyImport - -from .nas import NASBase -from .nas_utils import nas_registry - -DyNASManager = LazyImport("dynast.dynast_manager.DyNAS") - - -@deprecated(version="2.0") -@nas_registry("DyNAS") -class DyNAS(NASBase): - """DyNAS approach. - - Defining the pipeline for DyNAS approach. - - Args: - conf_fname_or_obj (string or obj): - The path to the YAML configuration file or the object of NASConfig. - """ - - def __init__(self, conf_fname_or_obj): - """Initialize the attributes.""" - super().__init__() - - self.init_cfg(conf_fname_or_obj) - - self.dynas_manager = DyNASManager( - supernet=self.supernet, - optimization_metrics=self.metrics, - measurements=self.metrics, - search_tactic="linas", - num_evals=self.num_evals, - results_path=self.results_csv_path, - dataset_path=self.dataset_path, - seed=self.seed, - population=self.population, - batch_size=self.batch_size, - eval_batch_size=self.eval_batch_size, - search_algo=self.search_algo, - supernet_ckpt_path=self.supernet_ckpt_path, - dataloader_workers=self.num_workers, - distributed=self.distributed, - test_fraction=self.test_fraction, - ) - - def search(self): - """Execute the search process. - - Returns: - Best model architectures found in the search process. - """ - return self.dynas_manager.search() - - def select_model_arch(self): # pragma: no cover - """Select the model architecture.""" - # model_arch_proposition intrinsically contained in - # pymoo.minimize API of search_manager.run_search method, - # don't have to implement it explicitly. - pass - - def init_cfg(self, conf_fname_or_obj): - """Initialize the configuration.""" - logger.info("init_cfg") - if isinstance(conf_fname_or_obj, str): - if os.path.isfile(conf_fname_or_obj): - self.conf = Conf(conf_fname_or_obj).usr_cfg - elif isinstance(conf_fname_or_obj, NASConfig): - conf_fname_or_obj.validate() - self.conf = conf_fname_or_obj.usr_cfg - else: # pragma: no cover - raise NotImplementedError("Please provide a str path to the config file or an object of NASConfig.") - # self.init_search_cfg(self.conf.nas) - assert "dynas" in self.conf.nas, "Must specify dynas section." - dynas_config = self.conf.nas.dynas - self.seed = self.conf.nas.search.seed - self.search_algo = self.conf.nas.search.search_algorithm - self.supernet = dynas_config.supernet - self.distributed = dynas_config.distributed - self.metrics = dynas_config.metrics - self.num_evals = dynas_config.num_evals - self.results_csv_path = dynas_config.results_csv_path - self.dataset_path = dynas_config.dataset_path - self.supernet_ckpt_path = dynas_config.supernet_ckpt_path - self.batch_size = dynas_config.batch_size - self.eval_batch_size = dynas_config.eval_batch_size - self.num_workers = dynas_config.num_workers - self.test_fraction = dynas_config.test_fraction - if dynas_config.population < 10: # pragma: no cover - raise NotImplementedError("Please specify a population size >= 10") - else: - self.population = dynas_config.population diff --git a/neural_compressor/experimental/nas/nas.py b/neural_compressor/experimental/nas/nas.py deleted file mode 100644 index 113699d7c1a..00000000000 --- a/neural_compressor/experimental/nas/nas.py +++ /dev/null @@ -1,353 +0,0 @@ -"""Common classes for different NAS approaches. - -NAS class for creating object of different NAS approaches. -NASBase class defines the common methods of different NAS approaches. -""" - -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import os -import shutil -from collections.abc import Iterable - -import numpy as np -from deprecated import deprecated - -from neural_compressor.conf.config import Conf, NASConfig -from neural_compressor.conf.pythonic_config import Config -from neural_compressor.utils.utility import LazyImport, logger - -from .nas_utils import NASMethods, find_pareto_front -from .search_algorithms import BayesianOptimizationSearcher, GridSearcher, RandomSearcher - -torch = LazyImport("torch") - - -@deprecated(version="2.0") -class NAS(object): - """Create object of different NAS approaches. - - Args: - conf_fname_or_obj (string or obj): - The path to the YAML configuration file or the object of NASConfig. - - Returns: - An object of specified NAS approach. - """ - - def __new__(self, conf_fname_or_obj, *args, **kwargs): - """Create an object of specified NAS approach.""" - if isinstance(conf_fname_or_obj, str): - if os.path.isfile(conf_fname_or_obj): - self.conf = Conf(conf_fname_or_obj) - elif isinstance(conf_fname_or_obj, NASConfig): - self.conf = conf_fname_or_obj - elif isinstance(conf_fname_or_obj, Config): - self.conf = NASConfig() - self.conf.map_pyconfig_to_cfg(conf_fname_or_obj) - else: # pragma: no cover - raise NotImplementedError("Please provide a str path to the config file.") - assert self.conf.usr_cfg.nas is not None, "nas section must be set" - if isinstance(self.conf.usr_cfg.nas.approach, str) and self.conf.usr_cfg.nas.approach.lower() in NASMethods: - method = self.conf.usr_cfg.nas.approach.lower() - else: - logger.warning("NAS approach not set in config, use default NAS approach, i.e. Basic.") - method = "basic" - if isinstance(self.conf, NASConfig): - return NASMethods[method](self.conf, *args, **kwargs) - return NASMethods[method](conf_fname_or_obj, *args, **kwargs) - - -@deprecated(version="2.0") -class NASBase(object): - """Base class for defining the common methods of different NAS approaches. - - Args: - search_space (dict): A dictionary for defining the search space. - model_builder (function obj): A function to build model instance with the specified - model architecture parameters. - """ - - def __init__(self, search_space=None, model_builder=None): - """Initialize the attributes.""" - super().__init__() - self._search_space = search_space - self._model_builder = model_builder - self._search_algorithm = None - self.search_results = {} - self.best_model_archs = None - self.seed = None - - def select_model_arch(self): - """Propose architecture of the model based on search algorithm for next search iteration. - - Returns: - Model architecture description. - """ - model_arch_paras = self._search_algorithm.suggest() - assert ( - self.search_space_keys - and isinstance(model_arch_paras, dict) - and self.search_space_keys == list(model_arch_paras.keys()) - ), "Keys of model_arch_paras should be the same with search_space_keys." - return model_arch_paras - - def search(self, res_save_path=None): - """NAS search process. - - Returns: - Best model architecture found in search process. - """ - assert ( - self.model_builder is not None - ), "Must specify model_builder for generating model instance by model architecture." - if res_save_path is None or not os.path.isdir(res_save_path): - res_save_path = os.getcwd() - save_path = os.path.join(res_save_path, "NASResults") - self.model_paras_num = {} - self.load_search_results(save_path) - os.makedirs(save_path, exist_ok=True) - - for i in range(self.max_trials): - logger.info( - "{fix} Trial {n} starts, {r} trials to go {fix}".format( - n=i + 1, r=self.max_trials - i - 1, fix="=" * 30 - ) - ) - model_arch_paras = self.select_model_arch() - logger.info("Model architecture {} proposed.".format(model_arch_paras)) - model = self._model_builder(model_arch_paras) - model_paras = self.count_model_parameters(model) - logger.info("***** Number of model parameters: {:.2f}M *****".format(model_paras / 10**6)) - self.model_paras_num[tuple(model_arch_paras.values())] = model_paras - if tuple(model_arch_paras.values()) in self.search_results: - logger.info("Skip evaluated model architecture {}.".format(model_arch_paras)) - continue - if tuple(model_arch_paras.values()) in self.resumed_search_results: - logger.info("Find previous results of model architecture: {}.".format(model_arch_paras)) - metrics = self.resumed_search_results[tuple(model_arch_paras.values())] - else: - logger.info("Assessing model architecture: {}.".format(model_arch_paras)) - metrics = self.estimate(model) - logger.info("Metrics of model architecture {} is {}.".format(model_arch_paras, metrics)) - self.search_results[tuple(model_arch_paras.values())] = metrics - self._search_algorithm.get_feedback(sum(self.metrics_conversion(metrics))) - self.dump_search_results(os.path.join(save_path, "Trial_{}_results.txt".format(i + 1))) - - for model_arch_vec in self.resumed_search_results: - if model_arch_vec not in self.search_results: - self.search_results[model_arch_vec] = self.resumed_search_results[model_arch_vec] - model = self._model_builder(self.params_vec2params_dict(model_arch_vec)) - self.model_paras_num[model_arch_vec] = self.count_model_parameters(model) - self.dump_search_results(os.path.join(save_path, "Final_results.txt")) - self.find_best_model_archs() - logger.info("{fix} Found {n} best model architectures {fix}".format(n=len(self.best_model_archs), fix="=" * 30)) - for i, model_arch in enumerate(self.best_model_archs): - logger.info("Best model architecture {}: {}".format(i + 1, model_arch)) - return self.best_model_archs - - def estimate(self, model): # pragma: no cover - """Estimate performance of the model. Depends on specific NAS algorithm. - - Returns: - Evaluated metrics of the model. - """ - raise NotImplementedError("Depends on specific NAS algorithm.") - - def count_model_parameters(self, model): - """Count number of model parameters. - - Returns: - Number of model parameters. - """ - if isinstance(model, torch.nn.Module): - return sum(p.numel() for p in model.parameters()) - else: - raise NotImplementedError("Only support torch model now.") # pragma: no cover - - def load_search_results(self, path): - """Load previous search results if exist.""" - self.resumed_search_results = {} - lastest_results_record = os.path.join(path, "lastest_results.npy") - if not os.path.exists(path) or not os.path.exists(lastest_results_record): - return - self.resumed_search_results = np.load(lastest_results_record, allow_pickle=True).item() - os.makedirs(os.path.join(path, "previous_results"), exist_ok=True) - for f in os.listdir(path): - if os.path.isfile(os.path.join(path, f)): - shutil.move(os.path.join(path, f), os.path.join(path, "previous_results", f)) - logger.info("Loaded previous results.") - - def dump_search_results(self, path): - """Save search results.""" - lastest_results_record = os.path.join(os.path.dirname(path), "lastest_results.npy") - np.save(lastest_results_record, self.search_results, allow_pickle=True) - write_contents = "=" * 30 + " All Search Results " + "=" * 30 + "\n\n" - for model_arch_vec in self.search_results: - tmp = ",".join(["{}_{}".format(k, v) for k, v in zip(self.search_space_keys, model_arch_vec)]) - write_contents += "{}: {} Paras: {}M\n".format( - tmp, self.search_results[model_arch_vec], self.model_paras_num[model_arch_vec] / 10**6 - ) - write_contents += "\n\n\n" + "=" * 30 + " Best Search Results " + "=" * 30 + "\n\n" - self.find_best_model_archs() - for i, model_arch in enumerate(self.best_model_archs): - model_arch_vec = tuple(model_arch.values()) - tmp = ",".join(["{}_{}".format(k, v) for k, v in zip(self.search_space_keys, model_arch_vec)]) - write_contents += "{}. {}: {} Paras: {}M\n".format( - i + 1, tmp, self.search_results[model_arch_vec], self.model_paras_num[model_arch_vec] / 10**6 - ) - with open(path, mode="w") as f: - f.write(write_contents) - - def params_vec2params_dict(self, paras_vec): - """Convert the parameters vector to parameters dictionary. - - Where parameters vector and parameters dictionary both define the model architecture. - - Returns: - Parameters dictionary defining the model architecture. - """ - assert len(paras_vec) == len( - self.search_space_keys - ), "Length of paras_vec and search_space_keys should be the same." - return {k: v for k, v in zip(self.search_space_keys, paras_vec)} - - def find_best_model_archs(self): - """Find the best model architectures. - - Find the best model architectures which lie on the pareto front. - """ - assert len(self.search_results) > 0, "Zero result in search_results." - model_arches = list(self.search_results.keys()) - metrics = [self.metrics_conversion(self.search_results[ma]) for ma in model_arches] - pareto_front_indices = find_pareto_front(metrics) - self.best_model_archs = [self.params_vec2params_dict(model_arches[i]) for i in pareto_front_indices] - - def metrics_conversion(self, metrics): - """Convert the metrics to specific format. - - Returns: - Converted metrics. - """ - if not isinstance(metrics, Iterable): - metrics = [metrics] - if isinstance(metrics, dict): - if self.metrics is None: - self.metrics = list(metrics.keys()) - assert list(metrics.keys()) == list( - self.metrics - ), "Keys of metrics not match with metrics in the configuration." - metrics = list(metrics.values()) - if self.higher_is_better is None: - self.higher_is_better = [ - True, - ] * len(metrics) - logger.warning( - "higher_is_better not set in the configuration, " - + "set it to all True for every metric entry by default." - ) - converted_metrics = [ - metric if higher_is_better else -metric for metric, higher_is_better in zip(metrics, self.higher_is_better) - ] - return converted_metrics - - def init_search_cfg(self, config): - """Initialize the search configuration.""" - self.search_cfg = config.search - - if not self._search_space: - self._search_space = self.search_cfg.search_space - else: - logger.warning( - "Use user provided search space {}, instead of search space " - "defined in the config, i.e. {}.".format(self._search_space, self.search_cfg.search_space) - ) - assert ( - isinstance(self._search_space, dict) and len(self._search_space) > 0 - ), "Must provide a dict as search_space for NAS." - self.search_space_keys = sorted(self.search_space.keys()) - for k in self.search_space_keys: - assert isinstance(self.search_space[k], (list, tuple)), "Value of key '{}' must be a list or tuple".format( - k - ) - - self.metrics = self.search_cfg.metrics if self.search_cfg.metrics else None - self.higher_is_better = self.search_cfg.higher_is_better if self.search_cfg.higher_is_better else None - self.seed = self.search_cfg.seed - self.max_trials = ( - self.search_cfg.max_trials if self.search_cfg.max_trials is not None else 3 - ) # set default 3 for max_trials - self.search_algorithm_type = self.search_cfg.search_algorithm if self.search_cfg.search_algorithm else None - if not self.search_algorithm_type: - self._search_algorithm = BayesianOptimizationSearcher(self.search_space, self.seed) - elif self.search_algorithm_type.lower() == "grid": - self._search_algorithm = GridSearcher(self.search_space) - elif self.search_algorithm_type.lower() == "random": - self._search_algorithm = RandomSearcher(self.search_space, self.seed) - elif self.search_algorithm_type.lower() == "bo": - self._search_algorithm = BayesianOptimizationSearcher(self.search_space, self.seed) - else: # pragma: no cover - logger.warning( - "Please be aware that '{}' is not a built-in search algorithm.".format(self.search_algorithm_type) - ) - - @property - def search_space(self): - """Getter of the search space. - - Returns: - The search space. - """ - return self._search_space - - @search_space.setter - def search_space(self, search_space): - """Setter of the search space.""" - self._search_space = search_space - - @property - def search_algorithm(self): - """Getter of the search algorithm. - - Returns: - The search algorithm. - """ - return self._search_algorithm - - @search_algorithm.setter - def search_algorithm(self, search_algorithm): - """Setter of the search algorithm.""" - self._search_algorithm = search_algorithm - - @property - def model_builder(self): - """Getter of the model builder. - - Returns: - The model builder. - """ - return self._model_builder - - @model_builder.setter - def model_builder(self, model_builder): - """Setter of the model builder.""" - self._model_builder = model_builder - - def __repr__(self): - """Class representation.""" - return "Base Class of NAS" # pragma: no cover diff --git a/neural_compressor/experimental/nas/nas_utils.py b/neural_compressor/experimental/nas/nas_utils.py deleted file mode 100644 index cc9e2fd144c..00000000000 --- a/neural_compressor/experimental/nas/nas_utils.py +++ /dev/null @@ -1,90 +0,0 @@ -"""Common methods for NAS.""" - -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import numpy as np -from deprecated import deprecated - -NASMethods = {} - - -@deprecated(version="2.0") -def nas_registry(nas_method): - """Decorate the NAS subclasses. - - The class decorator used to register all NAS subclasses. - - Args: - nas_method (str): The string of supported NAS Method. - - Returns: - cls: The class of register. - """ - assert isinstance(nas_method, str), "Expect nas_method to be a string." - - def decorator(cls): - NASMethods[nas_method.lower()] = cls - return cls - - return decorator - - -@deprecated(version="2.0") -def create_search_space_pool(search_space, idx=0): - """Create all the samples from the search space. - - Args: - search_space (dict): A dict defining the search space. - idx (int): An index for indicating which key of search_space to enumerate. - - Return: - A list of all the samples from the search space. - """ - search_space_keys = sorted(search_space.keys()) - if idx == len(search_space_keys): - return [[]] - key = search_space_keys[idx] - search_space_pool = [] - for v in search_space[key]: - sub_search_space_pool = create_search_space_pool(search_space, idx + 1) - search_space_pool += [[v] + item for item in sub_search_space_pool] - return search_space_pool - - -@deprecated(version="2.0") -def find_pareto_front(metrics): - """Find the pareto front points, assuming all metrics are "higher is better". - - Args: - metrics (numpy array or list): An (n_points, n_metrics) array. - - Return: - An array of indices of pareto front points. - It is a (n_pareto_points, ) integer array of indices. - """ - metrics = np.array(metrics) - pareto_front_point_indices = np.arange(metrics.shape[0]) - next_point_idx = 0 - while next_point_idx < len(metrics): - nondominated_points = np.any(metrics > metrics[next_point_idx], axis=1) - nondominated_points[next_point_idx] = True - # Remove points being dominated by current point - pareto_front_point_indices = pareto_front_point_indices[nondominated_points] - metrics = metrics[nondominated_points] - next_point_idx = np.sum(nondominated_points[: next_point_idx + 1]) - return pareto_front_point_indices diff --git a/neural_compressor/experimental/nas/search_algorithms.py b/neural_compressor/experimental/nas/search_algorithms.py deleted file mode 100644 index a823acf304b..00000000000 --- a/neural_compressor/experimental/nas/search_algorithms.py +++ /dev/null @@ -1,174 +0,0 @@ -"""Search algorithms for NAS.""" - -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import random - -from deprecated import deprecated - -from neural_compressor.strategy.bayesian import BayesianOptimization -from neural_compressor.utils import logger - -from .nas_utils import create_search_space_pool - - -@deprecated(version="2.0") -class Searcher(object): - """Base class for defining the common methods of different search algorithms. - - Args: - search_space (dict): A dictionary for defining the search space. - """ - - def __init__(self, search_space) -> None: - """Initialize the attributes.""" - assert isinstance(search_space, dict) and search_space, "Expect search_space to be a dict." - self.search_space = search_space - self.search_space_keys = sorted(search_space.keys()) - for k in self.search_space_keys: - assert isinstance( - self.search_space[k], (list, tuple) - ), "Value of key '{}' must be a list or tuple to specify choices".format(k) - - def suggest(self): - """Suggest the model architecture.""" - raise NotImplementedError("Depends on specific search algorithm.") # pragma: no cover - - def get_feedback(self, metric): - """Get metric feedback for the search algorithm.""" - pass - - def params_vec2params_dict(self, para_vec): - """Convert the parameters vector to parameters dictionary. - - Where parameters vector and parameters dictionary both define the model architecture. - - Returns: - Parameters dictionary defining the model architecture. - """ - assert len(para_vec) == len( - self.search_space_keys - ), "Length of para_vec and search_space_keys should be the same." - return {k: para_vec[i] for i, k in enumerate(self.search_space_keys)} - - -@deprecated(version="2.0") -class GridSearcher(Searcher): - """Grid search. - - Search the whole search space exhaustively. - - Args: - search_space (dict): A dictionary for defining the search space. - """ - - def __init__(self, search_space) -> None: - """Initialize the attributes.""" - super(GridSearcher, self).__init__(search_space) - self.search_space_pool = create_search_space_pool(search_space) - self.idx = 0 - - def suggest(self): - """Suggest the model architecture. - - Returns: - The model architecture. - """ - res = self.search_space_pool[self.idx] - self.idx = (self.idx + 1) % len(self.search_space_pool) - return self.params_vec2params_dict(res) - - -@deprecated(version="2.0") -class RandomSearcher(Searcher): - """Random search. - - Search the whole search space randomly. - - Args: - search_space (dict): A dictionary for defining the search space. - """ - - def __init__(self, search_space, seed=42) -> None: - """Initialize the attributes.""" - super(RandomSearcher, self).__init__(search_space) - self.search_space_pool = create_search_space_pool(search_space) - self.indices_pool = list(range(len(self.search_space_pool))) - random.seed(seed) - random.shuffle(self.indices_pool) - - def suggest(self): - """Suggest the model architecture. - - Returns: - The model architecture. - """ - if not self.indices_pool: - self.indices_pool = list(range(len(self.search_space_pool))) - random.shuffle(self.indices_pool) - idx = self.indices_pool.pop(-1) - return self.params_vec2params_dict(self.search_space_pool[idx]) - - -@deprecated(version="2.0") -class BayesianOptimizationSearcher(Searcher): - """Bayesian Optimization. - - Search the search space with Bayesian Optimization. - - Args: - search_space (dict): A dictionary for defining the search space. - """ - - def __init__(self, search_space, seed=42) -> None: - """Initialize the attributes.""" - super(BayesianOptimizationSearcher, self).__init__(search_space) - idx_search_space = {k: (0, len(search_space[k]) - 1) for k in self.search_space_keys} - self.bo_agent = BayesianOptimization(idx_search_space, random_seed=seed) - self.last_param_indices = None - - def suggest(self): - """Suggest the model architecture. - - Returns: - The model architecture. - """ - param_indices = self.bo_agent.gen_next_params() - self.last_param_indices = param_indices - return self.params_vec2params_dict(self.indices2params_vec(param_indices)) - - def get_feedback(self, metric): - """Get metric feedback and register this metric.""" - assert self.last_param_indices is not None, ( - "Need run suggest first " + "to get parameters and the input metric is corresponding to this parameters." - ) - try: - self.bo_agent._space.register(self.last_param_indices, metric) - except KeyError: # pragma: no cover - logger.debug("Find registered params, skip it.") - pass - self.last_param_indices = None - - def indices2params_vec(self, indices): - """Convert indices to parameters vector.""" - res = [] - for key, ind in indices.items(): - # keep ind within the index range of self.search_space[key] - ind = int(min(max(round(ind), 0), len(self.search_space[key]) - 1)) - res.append(self.search_space[key][ind]) - return res diff --git a/neural_compressor/experimental/pruner_legacy/__init__.py b/neural_compressor/experimental/pruner_legacy/__init__.py deleted file mode 100644 index 87f63033d12..00000000000 --- a/neural_compressor/experimental/pruner_legacy/__init__.py +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Legacy pruner module.""" - -from os.path import dirname, basename, isfile, join -import glob -from .pruner import PRUNERS - -modules = glob.glob(join(dirname(__file__), "*.py")) - -for f in modules: - if isfile(f) and not f.startswith("__") and not f.endswith("__init__.py"): - __import__(basename(f)[:-3], globals(), locals(), level=1) - -__all__ = ["PRUNERS"] diff --git a/neural_compressor/experimental/pruner_legacy/gradient_sensitivity.py b/neural_compressor/experimental/pruner_legacy/gradient_sensitivity.py deleted file mode 100644 index c8936186937..00000000000 --- a/neural_compressor/experimental/pruner_legacy/gradient_sensitivity.py +++ /dev/null @@ -1,233 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Gradient sensitivity pruner.""" - -import re -from heapq import heappop, heappush - -import numpy as np -from deprecated import deprecated - -from neural_compressor.utils import logger - -from .pruner import Pruner, pruner_registry - - -@deprecated(version="2.0") -@pruner_registry -class GradientSensitivityPruner(Pruner): - """Gradient sensitivity pruner class. - - Args: - model (object): The original model (currently PyTorchModel instance). - local_config (Conf): configs specific for this pruning instance. - global_config (Conf): global configs which may be overwritten by local_config. - """ - - def __init__(self, model, local_config, global_config): - """Initialize the attributes.""" - super(GradientSensitivityPruner, self).__init__(model, local_config, global_config) - self.parameters = local_config.parameters - self.importance = {} - self.elementwise_prune = False if local_config.parameters is not None else True - - def on_epoch_begin(self, epoch): - """Be called on the beginning of epochs.""" - if epoch == self.start_epoch: - # register hook for FWK model to get actual input tensor - self.model.register_forward_pre_hook() - if self.elementwise_prune: - self.sparsity = self.update_sparsity(epoch) - logger.debug("Start pruning in epoch {} with sparsity {}.".format(str(epoch), str(self.sparsity))) - self.is_last_epoch = epoch == self.end_epoch - if epoch >= self.start_epoch and epoch <= self.end_epoch: - self.compute_mask() - - def on_step_begin(self, batch_id): - """Multiple mask to weight elementwisely when elementwise_prune is True.""" - if self.elementwise_prune: - for weight_name in self.weights: - if weight_name in self.masks: - new_weight = self.masks[weight_name].reshape( - np.array(self.model.get_weight(weight_name).shape) - ) * np.array(self.model.get_weight(weight_name)) - self.model.update_weights(weight_name, new_weight) - - def on_epoch_end(self): - """Be called on the end of epochs.""" - if self.elementwise_prune: - if self.is_last_epoch: - for weight_name in self.weights: - if weight_name in self.masks: - logger.info( - "Set {} sparsity with mask {} {} {}.".format( - weight_name, - str(self.masks[weight_name].size), - str(self.masks[weight_name].sum()), - str(1 - self.masks[weight_name].sum() / self.masks[weight_name].size), - ) - ) - new_weight = self.masks[weight_name].reshape( - np.array(self.model.get_weight(weight_name).shape) - ) * np.array(self.model.get_weight(weight_name)) - self.model.update_weights(weight_name, new_weight) - else: - for weight_name_raw in self.weights: - for weight_name in self.parse_weight_name(weight_name_raw): - self.prune_weight(self.model, self.importance, weight_name, self.parameters) - if self.is_last_epoch: - # remove hooks for FWK model to ensure model saving - self.model.remove_hooks() - - def parse_weight_name(self, weight_name_pattern): - """Parse weight name.""" - # check if asterisk is used to match bert layer indexes - if "*" not in weight_name_pattern: - yield weight_name_pattern - else: - weight_all_names = self.model.get_all_weight_names() - importance_inputs = self.parameters["importance_inputs"] - for single_weight_name in weight_all_names: - index_group = re.match(weight_name_pattern.replace("*", "(\d+)"), single_weight_name) - if index_group is not None: - index = index_group.group(1) - if self.parameters.get(index) is None: - self.parameters["index"] = int(index) - # dynamic change importance_inputs with matched index - self.parameters["importance_inputs"] = [ - x.replace("*", index) for x in self.parameters["importance_inputs"] - ] - yield single_weight_name - # change importance_inputs back - self.parameters["importance_inputs"] = importance_inputs - - def on_step_end(self): - """Update importance tensor.""" - if self.elementwise_prune: - for weight_name in self.weights: - self.update_importance_elementwise(self.model, self.importance, weight_name) - if weight_name in self.masks: - new_weight = self.masks[weight_name].reshape( - np.array(self.model.get_weight(weight_name).shape) - ) * np.array(self.model.get_weight(weight_name)) - self.model.update_weights(weight_name, new_weight) - else: - for weight_name_raw in self.weights: - for weight_name in self.parse_weight_name(weight_name_raw): - if self.parameters["importance_metric"] == "abs_gradient": - self.update_importance_abs(self.model, self.importance, weight_name, self.parameters) - elif self.parameters["importance_metric"] == "weighted_gradient": - self.update_importance_weighted(self.model, self.importance, weight_name, self.parameters) - - def compute_mask(self): - """Compute masks according to absolute values.""" - for weight_name in self.weights: - if weight_name in self.importance.keys(): - tensor = self.importance[weight_name] - if len(tensor.shape) in self.tensor_dims: - reduced_tensor = self.pattern.reduce(tensor) - if self.method == "per_channel": - tensor_flat = reduced_tensor.reshape(list(tensor.shape)[:-2], -1) - tensor_flat.sort(axis=-1) - threshold = tensor_flat[..., int(self.sparsity * tensor_flat.shape[-1])] - threshold = np.expand_dims(np.expand_dims(threshold, -1), -1) - threshold = np.repeat(threshold, reduced_tensor.shape[-1], axis=-1) - threshold = np.repeat(threshold, reduced_tensor.shape[-2], axis=-2) - else: - tensor_flat = sorted(np.abs(reduced_tensor.flatten())) - threshold = float(tensor_flat[int(len(tensor_flat) * self.sparsity)]) - reduced_mask = threshold < np.abs(reduced_tensor) - self.masks[weight_name] = self.pattern.repeat_mask(reduced_mask) - - def prune_weight(self, model, importance, weight_name, parameters): - """Prune the specified weight by importance.""" - if parameters["normalize"]: - exponent = 2 - norm_by_layer = np.power(np.power(importance[weight_name], exponent).sum(-1), 1 / exponent) - importance[weight_name] /= np.expand_dims(norm_by_layer, -1) + 1e-20 - importance = importance[weight_name] - weight_tensor = np.array(model.get_weight(weight_name)) - if parameters["transpose"]: - weight_tensor = weight_tensor.transpose((1, 0)) - - weight_tensor = self.prune_by_importance(weight_tensor, importance, parameters["target"], parameters["stride"]) - if parameters["transpose"]: - weight_tensor = weight_tensor.transpose((1, 0)) - - model.update_weights(weight_name, weight_tensor) - - def update_importance_elementwise(self, model, importance, weight_name): - """Update importance tensor elementwisely.""" - if importance.get(weight_name) is not None: - importance[weight_name] += np.absolute( - np.array(np.array(model.get_gradient(weight_name)) * np.array(model.get_weight(weight_name))) - ) - else: - importance[weight_name] = np.absolute( - np.array(model.get_gradient(weight_name) * np.array(model.get_weight(weight_name))) - ) - - def update_importance_abs(self, model, importance, weight_name, parameters): - """Update importance tensor with absolute gradient.""" - head_mask = model.get_inputs(input_name=parameters["importance_inputs"][0]) - if importance.get(weight_name) is not None: - importance[weight_name] += np.absolute(np.array(model.get_gradient(head_mask)))[parameters["index"]] - else: - importance[weight_name] = np.absolute(np.array(model.get_gradient(head_mask)))[parameters["index"]] - - def update_importance_weighted(self, model, importance, weight_name, parameters): - """Update importance tensor with weighted gradient.""" - - def weighted_grad(input_weight): - """Compute weighted gradient.""" - weight_grad = np.array(model.get_gradient(input_weight)) - weight = np.array(model.get_weight(input_weight)) - weighted_grad = weight_grad * weight - # TODO: add more check here - if weighted_grad.ndim > 1: - weighted_grad = weighted_grad.sum(1) - return weighted_grad - - accumulated_grad = sum([weighted_grad(input_weight) for input_weight in parameters["importance_inputs"]]) - - if importance.get(weight_name) is not None: - importance[weight_name] += np.absolute(accumulated_grad) - else: - importance[weight_name] = np.absolute(accumulated_grad) - - def prune_by_importance(self, tensor, importance, num_instances, stride): - """Reserve specified number of weights only by importance.""" - # structured prune - importance_ordered = [] - i = 0 - for heads in importance: - heappush(importance_ordered, (-heads, i)) - i += 1 - sorted_tensor_to_concat = None - i = 0 - while importance_ordered and i < num_instances: - head_to_add = heappop(importance_ordered)[1] - if sorted_tensor_to_concat is None: - sorted_tensor_to_concat = ( - tensor[int(head_to_add * stride) : int(head_to_add * stride) + int(stride), ...], - ) - else: - sorted_tensor_to_concat += ( - tensor[int(head_to_add * stride) : int(head_to_add * stride) + int(stride), ...], - ) - i += 1 - return np.concatenate(sorted_tensor_to_concat) diff --git a/neural_compressor/experimental/pruner_legacy/group_lasso.py b/neural_compressor/experimental/pruner_legacy/group_lasso.py deleted file mode 100644 index f79c5be8aaa..00000000000 --- a/neural_compressor/experimental/pruner_legacy/group_lasso.py +++ /dev/null @@ -1,68 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Group Lasso pruner.""" - -import copy - -import numpy as np -from deprecated import deprecated - -from .magnitude import BasicMagnitudePruner -from .pruner import Pruner, pruner_registry - - -@deprecated(version="2.0") -@pruner_registry -class GroupLassoPruner(BasicMagnitudePruner): - """Group Lasso pruner class. - - Args: - model (object): The original model (currently PyTorchModel instance). - local_config (Conf): configs specific for this pruning instance. - global_config (Conf): global configs which may be overwritten by local_config. - """ - - def __init__(self, model, local_config, global_config): - """Initialize the attributes.""" - super(GroupLassoPruner, self).__init__(model, local_config, global_config) - self.cur_weights = copy.deepcopy(self.weights) - self.is_masks_set = False - self.alpha = local_config.parameters["alpha"] - - def on_before_optimizer_step(self): - """Update gradient to prune the weights by back propagation.""" - if self.cur_weights: - for weight_name in self.weights: - weight_grad = self.model.get_gradient(weight_name) - weight = np.array(self.model.get_weight(weight_name)) - reshaped_weight = self.pattern.reshape(weight) - coeff = self.alpha / np.linalg.norm(reshaped_weight, 2, axis=(1, 3)) - coeff[np.isinf(coeff)] = 0 - coeff = self.pattern.repeat_mask(coeff).reshape(weight.shape) - weight_grad += coeff * weight - self.model.update_gradient(weight_name, weight_grad) - - for weight_name in self.weights: - weight = self.model.get_weight(weight_name) - grad = self.model.get_gradient(weight_name) - grad[weight == 0] = 0 - self.model.update_gradient(weight_name, grad) - else: - for weight_name in self.weights: - grad = self.model.get_gradient(weight_name) - new_grad = grad * self.masks[weight_name] - self.model.update_gradient(weight_name, new_grad) diff --git a/neural_compressor/experimental/pruner_legacy/magnitude.py b/neural_compressor/experimental/pruner_legacy/magnitude.py deleted file mode 100644 index 6db167cc9ce..00000000000 --- a/neural_compressor/experimental/pruner_legacy/magnitude.py +++ /dev/null @@ -1,107 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Magnitude pruner.""" - -import numpy as np -from deprecated import deprecated - -from neural_compressor.utils import logger - -from .pruner import Pruner, pruner_registry - - -@deprecated(version="2.0") -@pruner_registry -class BasicMagnitudePruner(Pruner): - """Magnitude pruner class. - - Args: - model (object): The original model (currently PyTorchModel instance). - local_config (Conf): configs specific for this pruning instance. - global_config (Conf): global configs which may be overwritten by local_config. - """ - - def __init__(self, model, local_config, global_config): - """Initialize the attributes.""" - super(BasicMagnitudePruner, self).__init__(model, local_config, global_config) - - def on_epoch_begin(self, epoch): - """Update target sparsity according to the schedule and compute mask accordingly.""" - self.sparsity = self.update_sparsity(epoch) - logger.debug("Start pruning in epoch {} with sparsity {}.".format(str(epoch), str(self.sparsity))) - self.is_last_epoch = epoch == self.end_epoch - if epoch >= self.start_epoch and epoch <= self.end_epoch: - self.compute_mask() - - def on_step_begin(self, batch_id): - """Apply mask to the weight.""" - res = dict() - - for weight in self.weights: - if weight in self.masks: - new_weight = self.masks[weight] * np.array(self.model.get_weight(weight)) - self.model.update_weights(weight, new_weight) - res[weight] = new_weight - return res - - def compute_mask(self): - """Compute masks according to absolute values.""" - for weight in self.weights: - tensor = np.array(self.model.get_weight(weight)) - if len(tensor.shape) in self.tensor_dims: - reduced_tensor = self.pattern.reduce(tensor) - if self.method == "per_channel": - tensor_flat = reduced_tensor.reshape(list(tensor.shape)[:-2], -1) - tensor_flat.sort(axis=-1) - threshold = tensor_flat[..., int(self.sparsity * tensor_flat.shape[-1])] - threshold = np.expand_dims(np.expand_dims(threshold, -1), -1) - threshold = np.repeat(threshold, reduced_tensor.shape[-1], axis=-1) - threshold = np.repeat(threshold, reduced_tensor.shape[-2], axis=-2) - else: - tensor_flat = sorted(np.abs(reduced_tensor.flatten())) - threshold = float(tensor_flat[int(len(tensor_flat) * self.sparsity)]) - reduced_mask = threshold < np.abs(reduced_tensor) - self.masks[weight] = self.pattern.repeat_mask(reduced_mask, tensor.shape) - - def on_epoch_end(self): - """Sparsity ratio summary and apply mask to the weight.""" - res = dict() - if self.is_last_epoch: - for weight in self.weights: - if weight in self.masks: - logger.info( - "Set {} sparsity with mask {} {} {}.".format( - weight, - str(self.masks[weight].size), - str(self.masks[weight].sum()), - str(1 - self.masks[weight].sum() / self.masks[weight].size), - ) - ) - new_weight = self.masks[weight] * np.array(self.model.get_weight(weight)) - self.model.update_weights(weight, new_weight) - res[weight] = new_weight - return res - - def on_step_end(self): - """Apply mask to the weight.""" - res = dict() - for weight in self.weights: - if weight in self.masks: - new_weight = self.masks[weight] * np.array(self.model.get_weight(weight)) - self.model.update_weights(weight, new_weight) - res[weight] = new_weight - return res diff --git a/neural_compressor/experimental/pruner_legacy/pattern_lock.py b/neural_compressor/experimental/pruner_legacy/pattern_lock.py deleted file mode 100644 index be3125acc84..00000000000 --- a/neural_compressor/experimental/pruner_legacy/pattern_lock.py +++ /dev/null @@ -1,66 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Pattern lock pruner.""" -from deprecated import deprecated - -from .pruner import Pruner, pruner_registry - - -@deprecated(version="2.0") -@pruner_registry -class PatternLockPruner(Pruner): - """Pattern lock pruner class. - - Args: - model (object): The original model (currently PyTorchModel instance). - local_config (Conf): configs specific for this pruning instance. - global_config (Conf): global configs which may be overwritten by local_config. - """ - - def __init__(self, model, local_config, global_config): - """Initialize the attributes.""" - super(PatternLockPruner, self).__init__(model, local_config, global_config) - self.compute_mask() - - def on_epoch_begin(self, epoch): - """Be called on the beginning of epochs.""" - pass - - def on_step_begin(self, batch_id): - """Be called on the beginning of steps.""" - pass - - def on_epoch_end(self): - """Be called on the end of epochs.""" - pass - - def on_step_end(self): - """Update weights.""" - self.update_weights() - - def compute_mask(self): - """Compute masks according to current sparsity pattern.""" - for weight in self.weights: - tensor = self.model.get_weight(weight) - if len(tensor.shape) in self.tensor_dims: - self.masks[weight] = tensor == 0.0 - - def update_weights(self): - """Update weights according to the masks.""" - for weight in self.weights: - if weight in self.masks: - self.model.prune_weights_(weight, self.masks[weight]) diff --git a/neural_compressor/experimental/pruner_legacy/pruner.py b/neural_compressor/experimental/pruner_legacy/pruner.py deleted file mode 100644 index 28793f7dcba..00000000000 --- a/neural_compressor/experimental/pruner_legacy/pruner.py +++ /dev/null @@ -1,140 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Pattern lock pruner.""" -from deprecated import deprecated - -from neural_compressor.experimental.pruning_recipes.patterns import patterns - -PRUNERS = {} - - -@deprecated(version="2.0") -def pruner_registry(cls): - """The class decorator used to register all Pruners subclasses. - - Args: - cls (class): The class of register. - - Returns: - cls: The class of register. - """ - assert cls.__name__.endswith("Pruner"), "The name of subclass of Pruner should end with 'Pruner' substring." - if cls.__name__[: -len("Pruner")].lower() in PRUNERS: - raise ValueError("Cannot have two pruner with the same name") - PRUNERS[cls.__name__[: -len("Pruner")]] = cls - return cls - - -@deprecated(version="2.0") -class Pruner: - """The base clase of Pruner. - - Args: - model (object): The original model (currently PyTorchModel instance). - local_config (Conf): configs specific for this pruning instance. - global_config (Conf): global configs which may be overwritten by local_config. - """ - - def __init__(self, model, local_config, global_config): - """Initialize the attributes.""" - self.model = model - # 2 for linear weight, 4 for conv weight - self.tensor_dims = [2, 4] - - if local_config.method is not None: - self.method = local_config.method - else: - self.method = "per_tensor" - - if local_config.initial_sparsity is not None: - self.initial_sparsity = local_config.initial_sparsity - else: - self.initial_sparsity = global_config.initial_sparsity - if local_config.target_sparsity is not None: - self.target_sparsity = local_config.target_sparsity - else: - self.target_sparsity = global_config.target_sparsity - if local_config.start_epoch is not None: - self.start_epoch = local_config.start_epoch - else: - self.start_epoch = global_config.start_epoch - if local_config.end_epoch is not None: - self.end_epoch = local_config.end_epoch - else: - self.end_epoch = global_config.end_epoch - if local_config.update_frequency is not None: - self.freq = local_config.update_frequency - else: - self.freq = global_config.update_frequency - if local_config.names is not None: - self.weights = local_config.names - else: - self.weights = self.model.get_all_weight_names() - - self.is_last_epoch = False - - # TBD, add pattern in config - if hasattr(local_config, "pattern"): - self.pattern = patterns[local_config.pattern]() - else: - self.pattern = patterns["tile_pattern_1x1"]() - self.masks = {} - - def on_epoch_begin(self, epoch): - """Be called on the beginning of epochs.""" - raise NotImplementedError - - def on_step_begin(self, batch_id): - """Be called on the beginning of steps.""" - raise NotImplementedError - - def on_epoch_end(self): - """Be called on the end of epochs.""" - raise NotImplementedError - - def on_step_end(self): - """Be called on the end of steps.""" - raise NotImplementedError - - def on_before_optimizer_step(self): - """Be called before optimizer steps.""" - pass - - def on_train_begin(self, dataloader=None): - """Be called on the beginning of the training process.""" - pass - - def on_train_end(self): - """Be called on the end of the training process.""" - pass - - def update_sparsity(self, epoch): - """Update sparsity goals according to epoch numbers. - - Args: - epoch (int): the epoch number - - Returns: - sparsity (float): sparsity target in this epoch - """ - if epoch < self.start_epoch: - return 0 - if self.start_epoch == self.end_epoch or epoch > self.end_epoch: - return self.target_sparsity - return self.initial_sparsity + (self.target_sparsity - self.initial_sparsity) * ( - (epoch - self.start_epoch + 1) // self.freq - ) * self.freq / (self.end_epoch - self.start_epoch + 1) diff --git a/neural_compressor/experimental/pruner_legacy/util/block_mask.py b/neural_compressor/experimental/pruner_legacy/util/block_mask.py deleted file mode 100644 index 9a11d608175..00000000000 --- a/neural_compressor/experimental/pruner_legacy/util/block_mask.py +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Block mask.""" diff --git a/neural_compressor/experimental/pruning.py b/neural_compressor/experimental/pruning.py deleted file mode 100644 index 0b51c89589c..00000000000 --- a/neural_compressor/experimental/pruning.py +++ /dev/null @@ -1,504 +0,0 @@ -"""Pruning module.""" - -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from deprecated import deprecated - -from ..adaptor import FRAMEWORKS -from ..conf.config import PruningConf -from ..conf.pythonic_config import Config -from ..model import BaseModel -from ..utils import logger -from ..utils.create_obj_from_config import create_dataloader, create_eval_func, create_train_func -from ..utils.utility import GLOBAL_STATE, MODE -from .component import Component -from .pruner_legacy import PRUNERS - - -@deprecated(version="2.0") -class Pruning(Component): - """This is base class of pruning object. - - Since DL use cases vary in the accuracy metrics (Top-1, MAP, ROC etc.), loss criteria - (<1% or <0.1% etc.) and pruning objectives (performance, memory footprint etc.). - Pruning class provides a flexible configuration interface via YAML for users to specify - these parameters. - - Args: - conf_fname_or_obj (string or obj): The path to the YAML configuration file or - PruningConf class containing accuracy goal, pruning objective and related - dataloaders etc. - - Attributes: - conf: A config dict object. Contains pruning setting parameters. - pruners: A list of Pruner object. - """ - - def __init__(self, conf_fname_or_obj=None): - """Initialize.""" - super(Pruning, self).__init__() - if isinstance(conf_fname_or_obj, PruningConf): - self.conf = conf_fname_or_obj - elif isinstance(conf_fname_or_obj, Config): - self.conf = PruningConf() - self.conf.map_pyconfig_to_cfg(conf_fname_or_obj) - else: - self.conf = PruningConf(conf_fname_or_obj) - self._init_with_conf() - - self.pruners = [] - self.callbacks = dict(tf_pruning=TfPruningCallback) - - def update_items_for_all_pruners(self, **kwargs): - """Functions which add User-defined arguments to the original configurations. - - The original config of pruning is read from a file. - However, users can still modify configurations by passing key-value arguments in this function. - Please note that the key-value arguments' keys are analysable in current configuration. - """ - for item in self.cfg.pruning.approach.weight_compression_pytorch.pruners: - for key in kwargs: - if hasattr(item, key): - setattr(item, key, kwargs[key]) - - def _on_train_begin(self, dataloader=None): - """Functions called before training.""" - for pruner in self.pruners: - pruner.on_train_begin(dataloader) - - def _on_epoch_begin(self, epoch): - """Functions called on the beginning of epochs.""" - for pruner in self.pruners: - pruner.on_epoch_begin(epoch) - - def _on_step_begin(self, batch_id): - """Functions called on the beginning of batches.""" - res = [] - for pruner in self.pruners: - res.append(pruner.on_step_begin(batch_id)) - return res - - def _on_before_optimizer_step(self): - """Functions called after gradient computed, usually for getting gradients.""" - for pruner in self.pruners: - pruner.on_before_optimizer_step() - - def _on_after_optimizer_step(self): - """Functions called after optimizer step.""" - for pruner in self.pruners: - pruner.on_after_optimizer_step() - - def _on_step_end(self): - """Functions called on the end of batches.""" - res = [] - for pruner in self.pruners: - res.append(pruner.on_step_end()) - return res - - def _on_epoch_end(self): - """Functions called on the end of epochs.""" - res = [] - for pruner in self.pruners: - res.append(pruner.on_epoch_end()) - if hasattr(self, "_model"): - stats, sparsity = self._model.report_sparsity() - logger.info(stats) - logger.info(sparsity) - return res - - def _on_train_end(self): - """Functions called after training.""" - for pruner in self.pruners: - pruner.on_train_end() - - def _on_before_eval(self): - """Implement at the beginning of evaluation phase.""" - for pruner in self.pruners: - pruner.on_before_eval() - - def _on_after_eval(self): - """Implement at the end of evaluation phase.""" - for pruner in self.pruners: - pruner.on_after_eval() - - def prepare(self): - """Functions prepare for generate_hooks, generate_pruners.""" - self.generate_hooks() - self.generate_pruners() - - def pre_process(self): - """Functions called before pruning begins, usually set up pruners.""" - assert isinstance(self._model, BaseModel), "need set neural_compressor Model for pruning...." - - GLOBAL_STATE.STATE = MODE.PRUNING - framework_specific_info = { - "device": self.cfg.device, - "random_seed": self.cfg.tuning.random_seed, - "workspace_path": self.cfg.tuning.workspace.path, - "q_dataloader": None, - "format": "default", - "backend": "default", - } - - if self.framework == "tensorflow": - framework_specific_info.update({"inputs": self.cfg.model.inputs, "outputs": self.cfg.model.outputs}) - - self.adaptor = FRAMEWORKS[self.framework](framework_specific_info) - - self.prepare() - - if self._train_dataloader is None and self._train_func is None: - train_dataloader_cfg = self.cfg.pruning.train.dataloader - assert train_dataloader_cfg is not None, ( - "dataloader field of train field of pruning section " - "in yaml file should be configured as train_dataloader property is NOT set!" - ) - train_dataloader_cfg.distributed = self.train_distributed - self._train_dataloader = create_dataloader(self.framework, train_dataloader_cfg) - - if self._eval_dataloader is None and self._eval_func is None: - eval_dataloader_cfg = self.cfg.evaluation.accuracy.dataloader - assert eval_dataloader_cfg is not None, ( - "dataloader field of evaluation " - "in yaml file should be configured as eval_dataloader property is NOT set!" - ) - eval_dataloader_cfg.distributed = self.evaluation_distributed - self._eval_dataloader = create_dataloader(self.framework, eval_dataloader_cfg) - - if self._train_func is None: - # train section of pruning section in yaml file should be configured. - train_cfg = self.cfg.pruning.train - assert train_cfg, ( - "train field of pruning section in yaml file must " - "be configured for pruning if pruning_func is NOT set." - ) - self._train_func = create_train_func( - self.framework, - self.train_dataloader, - self.adaptor, - train_cfg, - hooks=self.hooks, - callbacks=self.callbacks, - ) - if self._eval_func is None: - # eval section in yaml file should be configured. - eval_cfg = self.cfg.evaluation - assert eval_cfg, ( - "eval field of pruning section in yaml file must " "be configured for pruning if eval_func is NOT set." - ) - self._eval_func = create_eval_func( - self.framework, - self.eval_dataloader, - self.adaptor, - eval_cfg.accuracy.metric, - eval_cfg.accuracy.postprocess, - fp32_baseline=False, - ) - if getattr(self.train_dataloader, "distributed", False): - self.register_hook("on_train_begin", self.adaptor._pre_hook_for_hvd) - - def execute(self): - """Functions that execute the pruning process. - - Follow the working flow: evaluate the dense model -> train/prune the model, evaluate the sparse model. - """ - logger.info("Start to get the baseline model's score before pruning.") - self.baseline_score = self._eval_func( - self._model if getattr(self._eval_func, "builtin", None) else self._model.model - ) - logger.info("Baseline model's score is {}.".format(str(self.baseline_score))) - logger.info("Model pruning begins.") - self._train_func(self._model if getattr(self._train_func, "builtin", None) else self._model.model) - logger.info("Model pruning is done. Start to evaluate the pruned model.") - self.last_score = self._eval_func( - self._model if getattr(self._eval_func, "builtin", None) else self._model.model - ) - logger.info("Pruned model score is {}.".format(str(self.last_score))) - return self._model - - def generate_hooks(self): - """Register hooks for pruning.""" - self.register_hook("on_train_begin", self._on_train_begin) - self.register_hook("on_train_end", self._on_train_end) - self.register_hook("on_epoch_begin", self._on_epoch_begin) - self.register_hook("on_epoch_end", self._on_epoch_end) - self.register_hook("on_step_begin", self._on_step_begin) - self.register_hook("on_step_end", self._on_step_end) - self.register_hook("on_before_optimizer_step", self._on_before_optimizer_step) - self.register_hook("on_after_optimizer_step", self._on_after_optimizer_step) - self.register_hook("on_before_eval", self._on_before_eval) - self.register_hook("on_after_eval", self._on_after_eval) - - def generate_pruners(self): - """Functions that generate pruners and set up self.pruners.""" - for name in self.cfg.pruning.approach: - assert ( - name == "weight_compression" or name == "weight_compression_pytorch" - ), "now we only support weight_compression and weight_compression_pytorch" - - if self.cfg.pruning.approach.weight_compression_pytorch is not None: - from .pytorch_pruner.pruning import Pruning as PytorchPruning - - self.pytorch_pruner = PytorchPruning(self.cfg) - self.pytorch_pruner.model = self.model._model # extract their pytorch model - self.pytorch_pruner.prepare() - self.pytorch_pruner.on_train_begin() - self.pruners += self.pytorch_pruner.pruners - - if self.cfg.pruning.approach.weight_compression is not None: - for pruner in self.cfg.pruning.approach.weight_compression.pruners: - if pruner.prune_type == "basic_magnitude": - self.pruners.append( - PRUNERS["BasicMagnitude"](self._model, pruner, self.cfg.pruning.approach.weight_compression) - ) - elif pruner.prune_type == "pattern_lock": - self.pruners.append( - PRUNERS["PatternLock"](self._model, pruner, self.cfg.pruning.approach.weight_compression) - ) - elif pruner.prune_type == "gradient_sensitivity": - self.pruners.append( - PRUNERS["GradientSensitivity"]( - self._model, pruner, self.cfg.pruning.approach.weight_compression - ) - ) - elif pruner.prune_type == "group_lasso": - self.pruners.append( - PRUNERS["GroupLasso"](self._model, pruner, self.cfg.pruning.approach.weight_compression) - ) - else: - ##print(pruner.prune_type) - assert False, "now only support {}".format(PRUNERS.keys()) - - def __call__(self): - """Entry point of pruning. - - This interface currently only works on pytorch - and provides three usages: - a) Fully yaml configuration: User specifies all the info through yaml, - including dataloaders used in training and evaluation phases - and pruning tuning settings. - - For this usage, only model parameter is mandatory. - - b) Partial yaml configuration: User specifies dataloaders used in training - and evaluation phase by code. - The tool provides built-in dataloaders and evaluators, user just need provide - a dataset implemented __iter__ or __getitem__ methods and invoke dataloader() - with dataset as input parameter to create neural_compressor dataloader before calling this - function. - - After that, User specifies fp32 "model", training dataset "p_dataloader" - and evaluation dataset "eval_dataloader". - - For this usage, model, p_dataloader and eval_dataloader parameters are mandatory. - - c) Partial yaml configuration: User specifies dataloaders used in training phase - by code. - This usage is quite similar with b), just user specifies a custom "eval_func" - which encapsulates the evaluation dataset by itself. - The trained and pruned model is evaluated with "eval_func". - The "eval_func" tells the tuner whether the pruned model meets - the accuracy criteria. If not, the Tuner starts a new training and tuning flow. - - For this usage, model, q_dataloader and eval_func parameters are mandatory. - - Returns: - pruned model: best pruned model found, otherwise return None - """ - return super(Pruning, self).__call__() - - """This makes pruning.fit() equals to pruning().""" - fit = __call__ - - @property - def pruning_func(self): - """Not support get pruning_func.""" - assert False, "Should not try to get the value of `pruning_func` attribute." - return None - - @pruning_func.setter - @deprecated(version="2.0", reason="please use `train_func` instead") - def pruning_func(self, user_pruning_func): - """Training function for pruning. - - Args: - user_pruning_func: This function takes "model" as input parameter - and executes entire training process with self - contained training hyper-parameters. If pruning_func set, - an evaluation process must be triggered and user should - set eval_dataloader with metric configured or directly eval_func - to make evaluation of the model executed. - """ - self._train_func = user_pruning_func - - @property - def evaluation_distributed(self): - """Getter to know whether need distributed evaluation dataloader.""" - eval_dataloader_cfg = self.cfg.evaluation.accuracy.dataloader - yaml_distributed = eval_dataloader_cfg.get("distributed", False) - return self._evaluation_distributed or yaml_distributed - - @evaluation_distributed.setter - def evaluation_distributed(self, distributed): - """Work with the former function.""" - self._evaluation_distributed = distributed - - @property - def train_distributed(self): - """Getter to know whether need distributed training dataloader.""" - train_dataloader_cfg = self.cfg.pruning.train.dataloader - yaml_distributed = train_dataloader_cfg.get("distributed", False) - return self._train_distributed or yaml_distributed - - @train_distributed.setter - def train_distributed(self, distributed): - """Work with the former function.""" - self._train_distributed = distributed - - def __repr__(self): - """Return the class's string representation.""" - return "Pruning" - - def get_sparsity_ratio(self): - """Functions that calculate a modules/layers sparsity. - - Returns: - Three floats. - elementwise_over_matmul_gemm_conv refers to zero elements' ratio in pruning layers. - elementwise_over_all refers to zero elements' ratio in all layers in the model. - blockwise_over_matmul_gemm_conv refers to all-zero blocks' ratio in pruning layers. - """ - import torch - - pattern_sparsity_cnt = 0 - element_sparsity_cnt = 0 - for pruner in self.pruners: - modules = pruner.modules - sparsity_ratio = pruner.pattern.get_sparsity_ratio(pruner.masks) - cnt = 0 - for key in modules.keys(): - cnt += modules[key].weight.numel() - pattern_sparsity_cnt += int(cnt * sparsity_ratio) - for key in pruner.masks.keys(): - element_sparsity_cnt += torch.sum(pruner.masks[key] == 0).data.item() - - linear_conv_cnt = 0 - param_cnt = 0 - for name, module in self.model.named_modules(): - if type(module).__name__ in ["Linear"] or "Conv" in type(module).__name__: - linear_conv_cnt += module.weight.numel() - - for n, param in self.model.named_parameters(): - param_cnt += param.numel() - blockwise_over_matmul_gemm_conv = float(pattern_sparsity_cnt) / linear_conv_cnt - elementwise_over_matmul_gemm_conv = float(element_sparsity_cnt) / linear_conv_cnt - elementwise_over_all = float(element_sparsity_cnt) / param_cnt - - return elementwise_over_matmul_gemm_conv, elementwise_over_all, blockwise_over_matmul_gemm_conv - - -@deprecated(version="2.0") -class TfPruningCallback(object): - """Class that contains callback functions. - - Args: - nc_model: A neural compression model object. - hooks: A dict. Contains pure-defined hooks. - """ - - def __init__(self, nc_model, input_model, hooks): - """Initialize.""" - self.hooks = hooks - self.nc_model = nc_model - self.model = input_model - - def __getitem__(self, func): - """Get the class's function.""" - return getattr(self, func) - - def _set_weights(self): - """Copy the input model's weight to the nc_model.""" - res = {} - for index, layer in enumerate(self.model.layers): - if len(layer.weights): - res[index] = layer.get_weights()[0] - self.nc_model.weights = res - - def on_train_begin(self, logs=None, dataloader=None): - """Call the same-name function from hooks.""" - self.hooks["on_train_begin"](dataloader) - - def on_train_end(self, logs=None): - """Call the same-name function from hooks.""" - self.hooks["on_train_end"]() - - @deprecated(version="2.0", reason="please use `on_train_begin` instead") - def pre_epoch_begin(self, logs=None, dataloader=None): # pragma: no cover - """Call the same-name function from hooks.""" - self.on_train_begin(logs, dataloader) - - @deprecated(version="2.0", reason="please use `on_train_end` instead") - def post_epoch_end(self, logs=None): # pragma: no cover - """Call the same-name function from hooks.""" - self.on_train_end(logs) - - def on_epoch_begin(self, epoch, logs=None): - """Call the same-name function from hooks.""" - self._set_weights() - self.hooks["on_epoch_begin"](epoch) - - def on_epoch_end(self, logs=None): - """Call the same-name function from hooks.""" - self._set_weights() - res = self.hooks["on_epoch_end"]() - for layer_index, weights in res[0][0].items(): - get_weights = self.model.layers[layer_index].get_weights() - get_weights[0] = weights - self.model.layers[layer_index].set_weights(get_weights) - - def on_step_begin(self, batch, logs=None): - """Call the same-name function from hooks.""" - self._set_weights() - res = self.hooks["on_step_begin"](batch) - for layer_index, weights in res[0][0].items(): - get_weights = self.model.layers[layer_index].get_weights() - get_weights[0] = weights - self.model.layers[layer_index].set_weights(get_weights) - - @deprecated(version="2.0", reason="please use `on_step_begin` instead") - def on_batch_begin(self, batch, logs=None): # pragma: no cover - """Call the same-name function from hooks.""" - self.on_step_begin(batch, logs) - - def on_after_compute_loss(self, input, s_outputs, s_loss, t_outputs=None): - """Call the same-name function from hooks.""" - return self.hooks["on_after_compute_loss"](input, s_outputs, s_loss, t_outputs) - - def on_step_end(self, logs=None): - """Call the same-name function from hooks.""" - self._set_weights() - res = self.hooks["on_step_end"]() - for layer_index, weights in res[0][0].items(): - get_weights = self.model.layers[layer_index].get_weights() - get_weights[0] = weights - self.model.layers[layer_index].set_weights(get_weights) - - @deprecated(version="2.0", reason="please use `on_step_end` instead") - def on_batch_end(self, logs=None): # pragma: no cover - """Call the same-name function from hooks.""" - self.on_step_end(logs) diff --git a/neural_compressor/experimental/pruning_recipes/__init__.py b/neural_compressor/experimental/pruning_recipes/__init__.py deleted file mode 100644 index f6fd99fa8fb..00000000000 --- a/neural_compressor/experimental/pruning_recipes/__init__.py +++ /dev/null @@ -1,26 +0,0 @@ -"""patterns.""" - -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -from .patterns import PATTERNS, patterns - -__all__ = [ - "PATTERNS", - "patterns", -] diff --git a/neural_compressor/experimental/pruning_recipes/patterns/__init__.py b/neural_compressor/experimental/pruning_recipes/patterns/__init__.py deleted file mode 100644 index 6f4589ebad2..00000000000 --- a/neural_compressor/experimental/pruning_recipes/patterns/__init__.py +++ /dev/null @@ -1,31 +0,0 @@ -"""Different patterns.""" - -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from os.path import dirname, basename, isfile, join -import glob -from .pattern import PATTERNS - -modules = glob.glob(join(dirname(__file__), "*.py")) - -for f in modules: - if isfile(f) and not f.startswith("__") and not f.endswith("__init__.py"): - __import__(basename(f)[:-3], globals(), locals(), level=1) - -patterns = PATTERNS() -__all__ = ["PATTERNS"] diff --git a/neural_compressor/experimental/pruning_recipes/patterns/pattern.py b/neural_compressor/experimental/pruning_recipes/patterns/pattern.py deleted file mode 100644 index 286ac65c249..00000000000 --- a/neural_compressor/experimental/pruning_recipes/patterns/pattern.py +++ /dev/null @@ -1,113 +0,0 @@ -"""Pattern classes.""" - -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import numpy as np -from deprecated import deprecated - -registry_patterns = {} - - -@deprecated(version="2.0") -def pattern_registry(pattern_type): - """Class decorator used to register all Pruning Pattern subclasses. - - Args: - cls (class): The class of register. - pattern_type (str): The pattern registration name - - Returns: - cls: The class of register. - """ - - def decorator_pattern(cls): - if pattern_type in registry_patterns: - raise ValueError("Cannot have two pattern with the same name") - registry_patterns[pattern_type] = cls - return cls - - return decorator_pattern - - -@deprecated(version="2.0") -class PATTERNS(object): - """Class that contain all registered pattern types. - - Attributes: - patterns: A dict which stores registered Pruning Pattern subclasses. - """ - - patterns = registry_patterns - - def __getitem__(self, pattern_type): - """Obtain a Pruning Pattern subclass.""" - assert pattern_type in self.patterns, "pattern type only support {}".format(self.patterns.keys()) - return self.patterns[pattern_type] - - @classmethod - def support_pattern(self): - """Support patterns.""" - return set(self.patterns.keys()) - - -@deprecated(version="2.0") -class PatternBase: - """Base class of pruning pattern.""" - - def __init__(self, mask_shape, is_contiguous=True): - """Initialize.""" - self.mask_shape = mask_shape - self.is_contiguous = is_contiguous - - def compute_sparsity(self, tensor): - """To be implemented in subclasses.""" - raise NotImplementedError - - def reduce(self, tensor, method="abs_sum"): - """Reshaped tensor, support 'abs_max', 'abs_sum'.""" - if len(tensor.shape) in [2, 4]: - reshaped_tensor = self.reshape(tensor) - dims = list(range(4)) - new_tensor = np.transpose(reshaped_tensor, dims[:-3] + [dims[-2], dims[-3], dims[-1]]) - new_shape = list(reshaped_tensor.shape) - reduced_tensor = new_tensor.reshape(new_shape[:-3] + [new_shape[-2], -1]) - else: - assert False, "tile-pattern pruning now only support 2d & 4d tensor" - if method == "abs_max": - return np.abs(reduced_tensor).max(-1).values - elif method == "abs_sum": - return np.abs(reduced_tensor).sum(-1) - else: - raise NotImplementedError - - def reshape(self, tensor): - """Reshape tensor into dims+2.""" - if len(tensor.shape) == 4: - tensor = tensor.reshape(tensor.shape[0], -1) - assert ( - tensor.shape[-1] % self.mask_shape[-1] == 0 and tensor.shape[-2] % self.mask_shape[-2] == 0 - ), "tensor shape {} cannot be divided by mask {}".format(tensor.shape, self.mask_shape) - - new_shape = list(tensor.shape)[:-2] - new_shape.append(tensor.shape[-2] // self.mask_shape[-2]) - new_shape.append(self.mask_shape[-2]) - new_shape.append(tensor.shape[-1] // self.mask_shape[-1]) - new_shape.append(self.mask_shape[-1]) - - reshaped_tensor = tensor.reshape(new_shape) - return reshaped_tensor diff --git a/neural_compressor/experimental/pruning_recipes/patterns/tile_pattern.py b/neural_compressor/experimental/pruning_recipes/patterns/tile_pattern.py deleted file mode 100644 index 0d6f08dc41e..00000000000 --- a/neural_compressor/experimental/pruning_recipes/patterns/tile_pattern.py +++ /dev/null @@ -1,95 +0,0 @@ -"""Tile pattern classes.""" - -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import numpy as np -from deprecated import deprecated - -from .pattern import PatternBase, pattern_registry - - -@deprecated(version="2.0") -class TilePatternBase(PatternBase): - """Parent class for all NxM tile patterns.""" - - def __init__(self, mask_shape): - """Magnitude mask should be contiguous.""" - super(TilePatternBase, self).__init__(mask_shape, True) - - def compute_sparsity(self, tensor): - """Calculate the sparsity of a tensor (weight matrix).""" - reduced_tensor = self.reduce(tensor) - return 1 - np.count_nonzero(reduced_tensor) / reduced_tensor.size - - def repeat_mask(self, mask, ori_shape=None): - """Repeat mask in 2 dimensions.""" - flatten_mask = np.repeat(np.repeat(mask, self.mask_shape[0], axis=-2), self.mask_shape[1], axis=-1) - if ori_shape: - return flatten_mask.reshape(ori_shape) - else: - return flatten_mask - - -@deprecated(version="2.0") -@pattern_registry(pattern_type="tile_pattern_1x1") -class TilePattern_1x1(TilePatternBase): - """1x1 tile pattern (unstructured).""" - - def __init__(self): - """Element wise sparsity.""" - super(TilePattern_1x1, self).__init__([1, 1]) - - -@deprecated(version="2.0") -@pattern_registry(pattern_type="tile_pattern_2x2") -class TilePattern_2x2(TilePatternBase): - """2x2 tile pattern (unstructured).""" - - def __init__(self): - """2x2 tile wise sparsity.""" - super(TilePattern_2x2, self).__init__([2, 2]) - - -@deprecated(version="2.0") -@pattern_registry(pattern_type="tile_pattern_1x16") -class TilePattern_1x16(TilePatternBase): - """1x16 tile pattern (unstructured).""" - - def __init__(self): - """1x16 tile wise sparsity.""" - super(TilePattern_1x16, self).__init__([1, 16]) - - -@deprecated(version="2.0") -@pattern_registry(pattern_type="tile_pattern_4x1") -class TilePattern_4x1(TilePatternBase): - """4x1 tile pattern (unstructured).""" - - def __init__(self): - """4x1 tile wise vnni-aware sparsity.""" - super(TilePattern_4x1, self).__init__([4, 1]) - - -@deprecated(version="2.0") -@pattern_registry(pattern_type="tile_pattern_1x2") -class TilePattern_1x2(TilePatternBase): - """1x2 tile pattern (unstructured).""" - - def __init__(self): - """1x2 tile wise vnni-aware sparsity.""" - super(TilePattern_1x2, self).__init__([1, 2]) diff --git a/neural_compressor/experimental/pruning_v2.py b/neural_compressor/experimental/pruning_v2.py deleted file mode 100644 index dd8284f922f..00000000000 --- a/neural_compressor/experimental/pruning_v2.py +++ /dev/null @@ -1,527 +0,0 @@ -"""Pruning module.""" - -# !/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from ..adaptor import FRAMEWORKS -from ..compression.pruner.pruners import get_pruner -from ..compression.pruner.utils import ( - check_config, - generate_pruner_config, - parse_to_prune, - process_config, - update_params, -) -from ..conf.config import PruningConf -from ..conf.pythonic_config import Config -from ..config import WeightPruningConfig -from ..model import BaseModel -from ..utils import logger -from ..utils.create_obj_from_config import create_dataloader, create_eval_func, create_train_func -from ..utils.utility import GLOBAL_STATE, MODE, LazyImport -from .component import Component -from .pruner_legacy import PRUNERS - -LazyImport("torch.nn") -torch = LazyImport("torch") - -import re - -from deprecated import deprecated - - -@deprecated(version="2.0") -class Pruning(Component): - """This is base class of pruning object. - - Since DL use cases vary in the accuracy metrics (Top-1, MAP, ROC etc.), loss criteria - (<1% or <0.1% etc.) and pruning objectives (performance, memory footprint etc.). - Pruning class provides a flexible configuration interface via YAML for users to specify - these parameters. - - Args: - conf_fname_or_obj (string or obj): The path to the YAML configuration file or - PruningConf class containing accuracy goal, pruning objective and related - dataloaders etc. - - Attributes: - conf: A config dict object. Contains pruning setting parameters. - pruners: A list of Pruner object. - """ - - def __init__(self, conf_fname_or_obj=None): - """Initialize.""" - super(Pruning, self).__init__() - # we support WeightPruningConfig object and yaml file as - if isinstance(conf_fname_or_obj, Config): - self.cfg = PruningConf() - self.cfg.map_pyconfig_to_cfg(conf_fname_or_obj) - self.cfg = self.cfg.usr_cfg - self.conf = conf_fname_or_obj.pruning - # elif isinstance(conf_fname_or_obj, WeightPruningConfig): - # self.conf = conf_fname_or_obj - else: - # yaml file - raise NotImplementedError("Only WeightPruningConfig config is supported currently.") - self.pruners_info = process_config(self.conf) - # self.model = None # here skip - # align with old Component based API - # self._init_with_conf() - self.callbacks = dict(tf_pruning=TfPruningCallback) - self.pruners = [] - self.generate_hooks() # place generate hooks here, to get rid of prepare() function. - - def update_config(self, *args, **kwargs): - """Add user-defined arguments to the original configurations. - - The original config of pruning is read from a file. - However, users can still modify configurations by passing key-value arguments in this function. - Please note that the key-value arguments' keys are analysable in current configuration. - """ - for item in self.pruners_info: - for key in kwargs: - if key in item.keys(): - item[key] = kwargs[key] - - update_params(item) - check_config(item) - - def get_sparsity_ratio(self): - """Calculate sparsity ratio of a module/layer. - - Returns: - Three floats. - elementwise_over_matmul_gemm_conv refers to zero elements' ratio in pruning layers. - elementwise_over_all refers to zero elements' ratio in all layers in the model. - blockwise_over_matmul_gemm_conv refers to all-zero blocks' ratio in pruning layers. - """ - pattern_sparsity_cnt = 0 - element_sparsity_cnt = 0 - for pruner in self.pruners: - modules = pruner.modules - sparsity_ratio = pruner.pattern.get_sparsity_ratio(pruner.masks) - cnt = 0 - for key in modules.keys(): - cnt += modules[key].weight.numel() - pattern_sparsity_cnt += int(cnt * sparsity_ratio) - for key in pruner.masks.keys(): - element_sparsity_cnt += torch.sum(pruner.masks[key] == 0).data.item() - - linear_conv_cnt = 0 - param_cnt = 0 - for name, module in self._model.model.named_modules(): - if type(module).__name__ in ["Linear"] or re.search(r"Conv.d", type(module).__name__) is not None: - linear_conv_cnt += module.weight.numel() - - for n, param in self._model.model.named_parameters(): - param_cnt += param.numel() - - blockwise_over_matmul_gemm_conv = float(pattern_sparsity_cnt) / linear_conv_cnt if linear_conv_cnt != 0 else 0 - elementwise_over_matmul_gemm_conv = float(element_sparsity_cnt) / linear_conv_cnt if linear_conv_cnt != 0 else 0 - - elementwise_over_all = float(element_sparsity_cnt) / param_cnt if param_cnt != 0 else 0 - - logger.info( - f"elementwise_over_matmul_gemm_conv:{elementwise_over_matmul_gemm_conv}," - f" elementwise_over_all:{elementwise_over_all}," - f"blockwise_over_matmul_gemm_conv:{blockwise_over_matmul_gemm_conv}" - ) - - return elementwise_over_matmul_gemm_conv, elementwise_over_all, blockwise_over_matmul_gemm_conv - - def _on_train_begin(self, dataloader=None): - """Implement at the beginning of training process. - - Before training, ensure that pruners are generated. - """ - # self.model = self.model.model - self._generate_pruners() ##TODO is there better place to place - - def _on_epoch_begin(self, epoch): - """Functions called on the beginning of epochs.""" - for pruner in self.pruners: - pruner.on_epoch_begin(epoch) - - def _on_step_begin(self, batch_id): - """Functions called on the beginning of batches.""" - res = [] - for pruner in self.pruners: - res.append(pruner.on_step_begin(batch_id)) - return res - - def _on_before_optimizer_step(self): - """Functions called after gradient computed, usually for getting gradients.""" - for pruner in self.pruners: - pruner.on_before_optimizer_step() - - def _on_after_optimizer_step(self): - """Functions called after optimizer step.""" - for pruner in self.pruners: - pruner.on_after_optimizer_step() - - def _on_step_end(self): - """Functions called on the end of batches.""" - res = [] - for pruner in self.pruners: - res.append(pruner.on_step_end()) - return res - - def _on_epoch_end(self): - """Functions called on the end of epochs.""" - res = [] - for pruner in self.pruners: - res.append(pruner.on_epoch_end()) - # if hasattr(self, "_model"): - # stats, sparsity = self._model.report_sparsity() - # logger.info(stats) - # logger.info(sparsity) - return res - - def _on_train_end(self): - """Functions called after training.""" - for pruner in self.pruners: - pruner.on_train_end() - if isinstance(self._model.model, torch.nn.Module): - self.get_sparsity_ratio() - - def _on_before_eval(self): - """Implement at the beginning of evaluation phase.""" - for pruner in self.pruners: - pruner.on_before_eval() - - def _on_after_eval(self): - """Implement at the end of evaluation phase.""" - for pruner in self.pruners: - pruner.on_after_eval() - - def prepare(self): - """Functions prepare for generate_hooks, generate_pruners.""" - # self.generate_hooks() - pass - - def pre_process(self): - """Functions called before pruning begins, usually set up pruners.""" - assert isinstance(self._model, BaseModel), "need set neural_compressor Model for pruning...." - - GLOBAL_STATE.STATE = MODE.PRUNING - framework_specific_info = { - "device": self.cfg.device, - "random_seed": self.cfg.tuning.random_seed, - "workspace_path": self.cfg.tuning.workspace.path, - "q_dataloader": None, - "format": "default", - "backend": "default", - } - - if self.framework == "tensorflow": - framework_specific_info.update({"inputs": self.cfg.model.inputs, "outputs": self.cfg.model.outputs}) - - self.adaptor = FRAMEWORKS[self.framework](framework_specific_info) - - self.prepare() - - if self._train_dataloader is None and self._train_func is None: - train_dataloader_cfg = self.cfg.pruning.train.dataloader - assert train_dataloader_cfg is not None, ( - "dataloader field of train field of pruning section " - "in yaml file should be configured as train_dataloader property is NOT set!" - ) - train_dataloader_cfg.distributed = self.train_distributed - self._train_dataloader = create_dataloader(self.framework, train_dataloader_cfg) - - if self._eval_dataloader is None and self._eval_func is None: - eval_dataloader_cfg = self.cfg.evaluation.accuracy.dataloader - assert eval_dataloader_cfg is not None, ( - "dataloader field of evaluation " - "in yaml file should be configured as eval_dataloader property is NOT set!" - ) - eval_dataloader_cfg.distributed = self.evaluation_distributed - self._eval_dataloader = create_dataloader(self.framework, eval_dataloader_cfg) - - if self._train_func is None: - # train section of pruning section in yaml file should be configured. - train_cfg = self.cfg.pruning.train - assert train_cfg, ( - "train field of pruning section in yaml file must " - "be configured for pruning if pruning_func is NOT set." - ) - self._train_func = create_train_func( - self.framework, - self.train_dataloader, - self.adaptor, - train_cfg, - hooks=self.hooks, - callbacks=self.callbacks, - ) - if self._eval_func is None: - # eval section in yaml file should be configured. - eval_cfg = self.cfg.evaluation - assert eval_cfg, ( - "eval field of pruning section in yaml file must " "be configured for pruning if eval_func is NOT set." - ) - self._eval_func = create_eval_func( - self.framework, - self.eval_dataloader, - self.adaptor, - eval_cfg.accuracy.metric, - eval_cfg.accuracy.postprocess, - fp32_baseline=False, - ) - if getattr(self.train_dataloader, "distributed", False): - self.register_hook("on_train_begin", self.adaptor._pre_hook_for_hvd) - - def execute(self): - """Functions that execute the pruning process. - - Follow the working flow: evaluate the dense model -> train/prune the model, evaluate the sparse model. - """ - logger.info("Start to get the baseline model's score before pruning.") - self.baseline_score = self._eval_func( - self._model if getattr(self._eval_func, "builtin", None) else self._model.model - ) - logger.info("Baseline model's score is {}.".format(str(self.baseline_score))) - logger.info("Model pruning begins.") - self._train_func(self._model if getattr(self._train_func, "builtin", None) else self._model.model) - logger.info("Model pruning is done. Start to evaluate the pruned model.") - self.last_score = self._eval_func( - self._model if getattr(self._eval_func, "builtin", None) else self._model.model - ) - logger.info("Pruned model score is {}.".format(str(self.last_score))) - return self._model - - def generate_hooks(self): - """Register hooks for pruning.""" - self.register_hook("on_train_begin", self._on_train_begin) - self.register_hook("on_train_end", self._on_train_end) - self.register_hook("on_epoch_begin", self._on_epoch_begin) - self.register_hook("on_epoch_end", self._on_epoch_end) - self.register_hook("on_step_begin", self._on_step_begin) - self.register_hook("on_step_end", self._on_step_end) - self.register_hook("on_before_optimizer_step", self._on_before_optimizer_step) - self.register_hook("on_after_optimizer_step", self._on_after_optimizer_step) - self.register_hook("on_before_eval", self._on_before_eval) - self.register_hook("on_after_eval", self._on_after_eval) - - def _generate_pruners(self): - """Obtain Pruner objects.""" - if isinstance(self._model.model, torch.nn.Module): - for info in self.pruners_info: - modules = parse_to_prune(info, self._model.model) - if modules == {}: - logger.warning("one pruner hooks no layers, please have a check") - - self.pruners.append(get_pruner(info, modules)) - info["modules"] = [key for key in modules.keys()] - info["len_of_modules"] = len(info["modules"]) - logger.info(info) - else: - for info in self.pruners_info: - pruner = generate_pruner_config(info) - if info.prune_type == "magnitude": - self.pruners.append(PRUNERS["BasicMagnitude"](self._model, pruner, None)) - elif info.prune_type == "pattern_lock": - self.pruners.append(PRUNERS["PatternLock"](self._model, pruner, None)) - elif info.prune_type == "gradient_sensitivity": - self.pruners.append(PRUNERS["GradientSensitivity"](self._model, pruner, None)) - elif info.prune_type == "group_lasso": - self.pruners.append(PRUNERS["GroupLasso"](self._model, pruner, None)) - else: - ##print(pruner.prune_type) - assert False, "now only support {}".format(PRUNERS.keys()) - logger.info(info) - - def __call__(self): - """Entry point of pruning. - - This interface currently only works on pytorch - and provides three usages: - a) Fully yaml configuration: User specifies all the info through yaml, - including dataloaders used in training and evaluation phases - and pruning tuning settings. - - For this usage, only model parameter is mandatory. - - b) Partial yaml configuration: User specifies dataloaders used in training - and evaluation phase by code. - The tool provides built-in dataloaders and evaluators, user just need provide - a dataset implemented __iter__ or __getitem__ methods and invoke dataloader() - with dataset as input parameter to create neural_compressor dataloader before calling this - function. - - After that, User specifies fp32 "model", training dataset "p_dataloader" - and evaluation dataset "eval_dataloader". - - For this usage, model, p_dataloader and eval_dataloader parameters are mandatory. - - c) Partial yaml configuration: User specifies dataloaders used in training phase - by code. - This usage is quite similar with b), just user specifies a custom "eval_func" - which encapsulates the evaluation dataset by itself. - The trained and pruned model is evaluated with "eval_func". - The "eval_func" tells the tuner whether the pruned model meets - the accuracy criteria. If not, the Tuner starts a new training and tuning flow. - - For this usage, model, q_dataloader and eval_func parameters are mandatory. - - Returns: - pruned model: best pruned model found, otherwise return None - """ - return super(Pruning, self).__call__() - - """This makes pruning.fit() equals to pruning().""" - fit = __call__ - - @property - def pruning_func(self): - """Not support get pruning_func.""" - assert False, "Should not try to get the value of `pruning_func` attribute." - return None - - @pruning_func.setter - @deprecated(version="2.0", reason="please use `train_func` instead") - def pruning_func(self, user_pruning_func): - """Training function for pruning. - - Args: - user_pruning_func: This function takes "model" as input parameter - and executes entire training process with self - contained training hyper-parameters. If pruning_func set, - an evaluation process must be triggered and user should - set eval_dataloader with metric configured or directly eval_func - to make evaluation of the model executed. - """ - self._train_func = user_pruning_func - - @property - def evaluation_distributed(self): - """Getter to know whether need distributed evaluation dataloader.""" - eval_dataloader_cfg = self.cfg.evaluation.accuracy.dataloader - yaml_distributed = eval_dataloader_cfg.get("distributed", False) - return self._evaluation_distributed or yaml_distributed - - @evaluation_distributed.setter - def evaluation_distributed(self, distributed): - """Work with the former function.""" - self._evaluation_distributed = distributed - - @property - def train_distributed(self): - """Getter to know whether need distributed training dataloader.""" - train_dataloader_cfg = self.cfg.pruning.train.dataloader - yaml_distributed = train_dataloader_cfg.get("distributed", False) - return self._train_distributed or yaml_distributed - - @train_distributed.setter - def train_distributed(self, distributed): - """Work with the former function.""" - self._train_distributed = distributed - - def __repr__(self): - """Return the class's string representation.""" - return "Pruning" - - -@deprecated(version="2.0") -class TfPruningCallback(object): - """Class that contains callback functions. - - Args: - nc_model: A neural compression model object. - hooks: A dict. Contains pure-defined hooks. - """ - - def __init__(self, nc_model, input_model, hooks): - """Initialize.""" - self.hooks = hooks - self.nc_model = nc_model - self.model = input_model - - def __getitem__(self, func): - """Get the class's function.""" - return getattr(self, func) - - def _set_weights(self): - """Copy the input model's weight to the nc_model.""" - res = {} - for index, layer in enumerate(self.model.layers): - if len(layer.weights): - res[index] = layer.get_weights()[0] - self.nc_model.weights = res - - def on_train_begin(self, logs=None, dataloader=None): - """Call the same-name function from hooks.""" - self.hooks["on_train_begin"](dataloader) - - def on_train_end(self, logs=None): - """Call the same-name function from hooks.""" - self.hooks["on_train_end"]() - - @deprecated(version="2.0", reason="please use `on_train_begin` instead") - def pre_epoch_begin(self, logs=None, dataloader=None): # pragma: no cover - """Call the same-name function from hooks.""" - self.on_train_begin(logs, dataloader) - - @deprecated(version="2.0", reason="please use `on_train_end` instead") - def post_epoch_end(self, logs=None): # pragma: no cover - """Call the same-name function from hooks.""" - self.on_train_end(logs) - - def on_epoch_begin(self, epoch, logs=None): - """Call the same-name function from hooks.""" - self._set_weights() - self.hooks["on_epoch_begin"](epoch) - - def on_epoch_end(self, logs=None): - """Call the same-name function from hooks.""" - self._set_weights() - res = self.hooks["on_epoch_end"]() - for layer_index, weights in res[0][0].items(): - get_weights = self.model.layers[layer_index].get_weights() - get_weights[0] = weights - self.model.layers[layer_index].set_weights(get_weights) - - def on_step_begin(self, batch, logs=None): - """Call the same-name function from hooks.""" - self._set_weights() - res = self.hooks["on_step_begin"](batch) - for layer_index, weights in res[0][0].items(): - get_weights = self.model.layers[layer_index].get_weights() - get_weights[0] = weights - self.model.layers[layer_index].set_weights(get_weights) - - @deprecated(version="2.0", reason="please use `on_step_begin` instead") - def on_batch_begin(self, batch, logs=None): # pragma: no cover - """Call the same-name function from hooks.""" - self.on_step_begin(batch, logs) - - def on_after_compute_loss(self, input, s_outputs, s_loss, t_outputs=None): - """Call the same-name function from hooks.""" - return self.hooks["on_after_compute_loss"](input, s_outputs, s_loss, t_outputs) - - def on_step_end(self, logs=None): - """Call the same-name function from hooks.""" - self._set_weights() - res = self.hooks["on_step_end"]() - for layer_index, weights in res[0][0].items(): - get_weights = self.model.layers[layer_index].get_weights() - get_weights[0] = weights - self.model.layers[layer_index].set_weights(get_weights) - - @deprecated(version="2.0", reason="please use `on_step_end` instead") - def on_batch_end(self, logs=None): # pragma: no cover - """Call the same-name function from hooks.""" - self.on_step_end(logs) diff --git a/neural_compressor/experimental/pytorch_pruner/__init__.py b/neural_compressor/experimental/pytorch_pruner/__init__.py deleted file mode 100644 index ebe1a78ed66..00000000000 --- a/neural_compressor/experimental/pytorch_pruner/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -"""PyTorch Pruner module.""" - -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2022 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/neural_compressor/experimental/pytorch_pruner/logger.py b/neural_compressor/experimental/pytorch_pruner/logger.py deleted file mode 100644 index 2bcc34e2e59..00000000000 --- a/neural_compressor/experimental/pytorch_pruner/logger.py +++ /dev/null @@ -1,20 +0,0 @@ -"""Logger module.""" - -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2022 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from ...utils import logger diff --git a/neural_compressor/experimental/pytorch_pruner/patterns.py b/neural_compressor/experimental/pytorch_pruner/patterns.py deleted file mode 100644 index 879830aa721..00000000000 --- a/neural_compressor/experimental/pytorch_pruner/patterns.py +++ /dev/null @@ -1,586 +0,0 @@ -"""Pattern module.""" - -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2022 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import torch -from deprecated import deprecated - -from .logger import logger - -PATTERNS = {} - - -@deprecated(version="2.0") -def register_pattern(name): - """Class decorator used to register a Pattern subclass to the registry. - - Decorator function used before a Pattern subclasses. - Make sure that this Pattern class can be registered in PATTERNS. - - Args: - cls (class): The class of register. - name: A string. Define the pattern type which will be included in a pruning process. - - Returns: - cls: The class of register. - """ - - def register(pattern): - PATTERNS[name] = pattern - return pattern - - return register - - -@deprecated(version="2.0") -def get_pattern(config): - """Get registered pattern class. - - Get a Pattern object from PATTERNS. - - Args: - config: A config dict object. Contains the pattern information. - - Returns: - A Pattern object. - - Raises: - AssertionError: Currently only support patterns which have been registered in PATTERNS. - """ - name = config.pattern - name = name.split("_")[-1] - if "x" in name: - return PATTERNS["NxM"](config) - if ":" in name: - return PATTERNS["N:M"](config) - assert False, f"currently only support {PATTERNS.keys()}" - - -@deprecated(version="2.0") -class Pattern: - """Pruning Pattern. - - Every Pruner object will contain a Pattern object. - It defines the basic pruning unit and how this unit will be pruned during pruning. - - Args: - config: A config dict object. Contains the pattern information. - - Attributes: - pattern: A config dict object. The pattern related part in args config. - is_global: A bool. Whether the pruning take global pruning option. - Global pruning means that all pruning layers are gathered to calculate pruning criteria. - Local pruning, on the contrast, means that pruning layers are to calculate criteria individually. - """ - - def __init__(self, config): - """Initialize.""" - self.pattern = config.pattern - self.is_global = config.prune_domain == "global" - - def get_masks(self, scores, target_sparsity_ratio, pre_masks, max_sparsity_ratio_per_layer): - """Call when new masks for pruning are to be calculated. - - Args: - scores: A dict{“layer_name”: Tensor}. Store the pruning scores of weights. - target_sparsity_ratio: A float. After pruning, the model's sparsity will reach this value. - pre_masks: A dict{"layer_name": Tensor}. The masks generated after the last pruning step. - max_sparsity_ratio_per_layer: A float. The maximum sparsity that one layer can reach. - - Returns: - A dict with the identical size as pre_masks. Update the 0/1 values in it. - """ - if self.is_global: - return self.get_masks_global(scores, target_sparsity_ratio, pre_masks, max_sparsity_ratio_per_layer) - else: - return self.get_masks_local(scores, target_sparsity_ratio, pre_masks, max_sparsity_ratio_per_layer) - - def get_masks_global(self, scores, target_sparsity_ratio, pre_masks, max_sparsity_ratio_per_layer): - """To be implemented in subclasses.""" - raise NotImplementedError - - def get_mask_single(self, score, exact_sparsity_ratio): - """Obtain a mask for one layer. - - Args: - score: A Tensor. Store the pruning scores of one layer. - exact_sparsity_ratio: A float. After pruning, the layer's sparsity will reach this value. - - Returns: - A Tensor with the identical size as score. a new mask. - """ - flattern_score = torch.flatten(score) - k = int(exact_sparsity_ratio * flattern_score.numel()) - threshold, _ = torch.kthvalue(flattern_score, k) - if not k < 1: - zero = torch.tensor([0.0]).to(score.device) - one = torch.tensor([1.0]).to(score.device) - mask = torch.where(score <= threshold, zero, one) - else: - mask = torch.ones(score.shape, device=score.device) - return mask - - def get_block_size_dict(self, data): - """To be implemented in subclasses.""" - raise NotImplementedError - - def get_masks_local(self, scores, target_sparsity_ratio, pre_masks, max_sparsity_ratio_per_layer): - """Obtain layers' local masks. - - Args: - scores: A dict{“layer_name”: Tensor}. Store the pruning scores of weights. - target_sparsity_ratio: A float. After pruning, the model's sparsity will reach this value. - pre_masks: A dict{"layer_name": Tensor}. The masks generated after the last pruning step. - max_sparsity_ratio_per_layer: A float. The maximum sparsity that one layer can reach. - - Returns: - A dict with the identical size as pre_masks. Update the 0/1 values in it. - """ - masks = {} - if isinstance(self, PatternNxM) and not isinstance(self.block_size, dict): - self.block_size = self.get_block_size_dict(pre_masks) - for key in scores.keys(): - score = {key: scores[key]} - pre_mask = {key: pre_masks[key]} - mask = self.get_masks_global(score, target_sparsity_ratio, pre_mask, max_sparsity_ratio_per_layer) - masks[key] = mask[key] - return masks - - def get_sparsity_ratio(self, pre_masks): - """Calculate the zero elements' ration in pre_masks. - - Args: - pre_masks: Dict{"layer_name": Tensor}. The masks generated after the last pruning step. - - Returns: - A float. The zero elements' ratio in pre_masks. - """ - zero_cnt = 0 - total_cnt = 0 - for key in pre_masks.keys(): - pre_mask = pre_masks[key] - zero_cnt += torch.sum(pre_mask == 0.0).data.item() - total_cnt += pre_masks.numel() - return float(zero_cnt) / total_cnt - - def get_pattern_lock_masks(self, modules): - """Obtain masks from original weight map, by masking where weights' are zero. - - Args: - modules: A dict{“layer_name”: Tensor}. Store weights. - - Returns: - A dict with the identical size as modules, containing pattern lock masks. - """ - pattern_lock_masks = {} - for key in modules.keys(): - weight = modules[key].weight - shape = weight.shape - mask = torch.ones(shape) - mask[weight == 0] = 0.0 - pattern_lock_masks[key] = mask.to(weight.device) - return pattern_lock_masks - - -@deprecated(version="2.0") -@register_pattern("NxM") -class PatternNxM(Pattern): - """Pruning Pattern. - - A Pattern class derived from Pattern. In this pattern, the weights in a NxM block will be pruned or kept - during one pruning step. - - Args: - config: A config dict object. Contains the pattern information. - - Attributes: - block_size: A list of two Integers. The height and width of the block. - Please be aware that the vertical direction of a Linear layer's weight in PyTorch refer to output channel. - Because PyTorch's tensor matmul has a hidden transpose operation. - """ - - def __init__(self, config): - """Initialize.""" - super(PatternNxM, self).__init__(config) - pattern = self.pattern.split("_")[-1] - self.N = pattern.split("x")[0] - self.M = pattern.split("x")[1] - if self.N == "channel": ##channel-wise pruning mode - self.block_size = ["channel", int(self.M)] - elif self.M == "channel": ##channel-wise pruning mode - self.block_size = [int(self.N), "channel"] - else: - self.block_size = [int(pattern.split("x")[0]), int(pattern.split("x")[1])] - - def get_block_size_dict(self, data): - """Calculate the zero elements' ration in pre_masks. - - Args: - data: Dict{"layer_name": Tensor}. Store weights or scores. - - Returns: - A dict. Dict{"layer_name": [block_size_1, block_size_2]}. - Containing layers' corresponding pruning pattern's block shape. - Please be aware that because in channel-wise pruning, - different layers can have different pruning patterns. - """ - block_sizes_dict = {} - if self.N == "channel" or self.M == "channel": - for key in data.keys(): - if isinstance(data[key], torch.nn.Module): - shape = data[key].weight.shape - else: - shape = data[key].shape - if self.N == "channel": - block_sizes_dict[key] = [shape[0], 1] - else: - block_sizes_dict[key] = [1, shape[1]] - return block_sizes_dict - for key in data.keys(): - block_sizes_dict[key] = self.block_size - return block_sizes_dict - - def get_sparsity_ratio(self, pre_masks): - """Calculate the zero elements' ration in pre_masks. - - Args: - pre_masks: Dict{"layer_name": Tensor}. The masks generated after the last pruning step. - - Returns: - A float. Calculate the zero elements' ratio in pre_masks. - """ - zero_cnt = 0 - total_cnt = 0 - if isinstance(self.block_size, list): - self.block_size = self.get_block_size_dict(pre_masks) - for key in pre_masks.keys(): - block_size = self.block_size[key] - pre_mask = pre_masks[key] - shape = pre_mask.shape - if len(shape) == 4: - shape = pre_mask.reshape(pre_mask.shape[0], -1).shape - if shape[0] % block_size[0] != 0 or shape[1] % block_size[1] != 0: - logger.warning(f"layer {key} is not support under current pattern, ignoring") - continue - - new_shape = [shape[0] // block_size[0], block_size[0], shape[1] // block_size[1], block_size[1]] - pre_mask = pre_mask.reshape(new_shape) - pre_mask_sum = pre_mask.sum(-1).sum(1) - zero_cnt += torch.sum(pre_mask_sum == 0.0).data.item() - total_cnt += pre_mask_sum.numel() - return float(zero_cnt) / total_cnt - - def get_masks_global( - self, scores, target_sparsity_ratio, pre_masks, max_sparsity_ratio_per_layer, keep_pre_mask=False - ): - """Generate masks for layers. - - Gather all layer's scores together and calculate a common threshold. - This threshold will be applied for all layers. - - Args: - scores: A dict{“layer_name”: Tensor}. Store the pruning scores of weights. - target_sparsity_ratio: A float. After pruning, the model's sparsity will reach this value. - pre_masks: A dict{"layer_name": Tensor}. The masks generated after the last pruning step. - max_sparsity_ratio_per_layer: A float. The maximum sparsity that one layer can reach. - keep_pre_masks: A bool. If True, keep the masks unchanged. - - Returns: - A dict with the identical size as pre_masks. Update the 0/1 values in it. - """ - if isinstance(self.block_size, list): - self.block_size = self.get_block_size_dict(scores) - new_scores = {} - not_divided_keys = [] - for key in scores.keys(): - block_size = self.block_size[key] - current_score = scores[key] - if len(current_score.shape) == 4: ##TODO need to verify whether it's ok for transposed conv - current_score = current_score.permute(0, 2, 3, 1) ##cout,k,k,cin - current_score = current_score.reshape(current_score.shape[0], -1) - shape = current_score.shape - if shape[0] % block_size[0] != 0 or shape[1] % block_size[1] != 0: ## only consider input channel - not_divided_keys.append(key) - continue - - new_shape = [shape[0] // block_size[0], block_size[0], shape[1] // block_size[1], block_size[1]] - current_score = current_score.reshape(new_shape) - current_score_sum = current_score.mean(-1).mean( - 1 - ) ##TODO sum or mean is quite different for per channel pruning - new_scores[key] = current_score_sum - global_scores = torch.cat([torch.flatten(v) for v in new_scores.values()]) - k = int(target_sparsity_ratio * global_scores.numel()) - masks = {} - if not k < 1: - threshold, _ = torch.kthvalue(global_scores, k) - for key in new_scores.keys(): - block_size = self.block_size[key] - score = new_scores[key] - zero = torch.tensor([0.0]).to(score.device) - one = torch.tensor([1.0]).to(score.device) - mask = torch.where(score <= threshold, zero, one) - mask = mask.repeat_interleave(block_size[0], dim=0).repeat_interleave(block_size[1], dim=-1) - if torch.sum(mask) / mask.numel() < 1.0 - max_sparsity_ratio_per_layer: - ##to prevent some layer not be purned too much - ##this is different with our original implementation - masks[key] = self.get_mask_single(new_scores[key], max_sparsity_ratio_per_layer) - masks[key] = masks[key].repeat_interleave(block_size[0], 0).repeat_interleave(block_size[1], -1) - # if pre_masks != {}:##when use one shot, this is not right - # masks[key] = pre_masks[key] - # else: - # masks[key] = mask - else: - masks[key] = mask - # if len(scores[key].shape) == 4: - # ##we need to revert back - # masks[key] = masks[key].reshape(scores[key].shape) - - for key in not_divided_keys: - p = scores[key] - masks[key] = torch.ones(p.shape).to(p.device) - logger.warning(f"{key} shape {scores[key].shape} cannot be divided by {self.pattern}") - - else: - for key in scores.keys(): - p = scores[key] - masks[key] = torch.ones(p.shape).to(p.device) - - for key in masks.keys(): - if len(scores[key].shape) == 4 and len(masks[key].shape) == 2: ## need to permute - mask = masks[key] - mask = mask.reshape( - scores[key].shape[0], scores[key].shape[2], scores[key].shape[3], scores[key].shape[1] - ) - mask = mask.permute(0, 3, 1, 2) - masks[key] = mask - return masks - - def get_pattern_lock_masks(self, modules): - """Obtain masks from original weight map, by masking where weights' are zero. - - Args: - modules: A dict{“layer_name”: Tensor}. Store weights. - - Returns: - A dict with the identical size as modules, containing pattern lock masks. - """ - pattern_lock_masks = {} - if isinstance(self.block_size, list): - self.block_size = self.get_block_size_dict(modules) - for key in modules.keys(): - block_size = self.block_size[key] - weight = modules[key].weight - if len(weight.shape) == 4: # conv - weight = weight.permute(0, 2, 3, 1) - weight = weight.reshape(weight.shape[0], -1) - shape = weight.shape - new_shape = [shape[0] // block_size[0], block_size[0], shape[1] // block_size[1], block_size[1]] - p = weight.reshape(new_shape) - p_mag = p.abs() # avoid the scene which sum is zero but weights are not - weight_block_sum = p_mag.sum(-1).sum(1) - mask = torch.ones(weight_block_sum.shape) - mask[weight_block_sum == 0] = 0.0 - mask = mask.repeat_interleave(block_size[0], dim=0).repeat_interleave(block_size[1], dim=-1) - orig_shape = modules[key].weight.shape - if len(orig_shape) == 4: - mask = mask.reshape(orig_shape[0], orig_shape[2], orig_shape[3], orig_shape[1]) - mask = mask.permute(0, 3, 1, 2) - if len(orig_shape) == 3: - mask = mask.reshape(orig_shape[0], orig_shape[2], orig_shape[1]) - mask = mask.permute(0, 2, 1) - pattern_lock_masks[key] = mask.to(weight.device) - return pattern_lock_masks - - -@deprecated(version="2.0") -@register_pattern("N:M") -class PatternNInM(Pattern): - """Pruning Pattern. - - A Pattern class derived from Pattern. In this pattern, N out of every M continuous weights will be pruned. - For more info of this pattern, please refer to - https://github.com/intel/neural-compressor/blob/master/docs/pruning.md - - Args: - config: A config dict object. Contains the pattern information. - - Attributes: - N: The number of elements to be prune in a weight sequence. - M: The size of the weight sequence. - """ - - def __init__(self, config): - """Initialize.""" - super(PatternNInM, self).__init__(config) - pattern = self.pattern.split("_")[-1] - self.N = int(pattern.split(":")[0]) - self.M = int(pattern.split(":")[1]) ##m is bigger - - def get_sparsity_ratio(self, pre_masks): - """Calculate the zero elements' ration in pre_masks. - - Args: - pre_masks: Dict{"layer_name": Tensor}. The masks generated after the last pruning step. - - Returns: - A float. Calculate the zero elements' ratio in pre_masks. - """ - ##simply use elemwise sparsity - non_zero_cnt = 0 - total_cnt = 0 - for key in pre_masks.keys(): - non_zero_cnt += (torch.sum(pre_masks[key])).data.item() - total_cnt += pre_masks[key].numel() - return 1.0 - float(non_zero_cnt) / total_cnt - - def get_masks_global(self, scores, target_sparsity_ratio, pre_masks, max_sparsity_ratio_per_layer): - """Generate masks for layers. - - Gather all layer's scores together and calculate a common threshold. - This threshold will be applied for all layers. - - Args: - scores: A dict{“layer_name”: Tensor}. Store the pruning scores of weights. - target_sparsity_ratio: A float. After pruning, the model's sparsity will reach this value. - pre_masks: A dict{"layer_name": Tensor}. The masks generated after the last pruning step. - max_sparsity_ratio_per_layer: A float. The maximum sparsity that one layer can reach. - - Returns: - A dict with the identical size as pre_masks. Update the 0/1 values in it. - """ - N = self.N - M = self.M - target_sparsity_ratio = target_sparsity_ratio / (float(N / M)) ##recover sparsity for block wise - all_nm_masks = {} - new_scores = {} - not_divided_keys = [] - for key in scores.keys(): - current_score = scores[key] - shape = current_score.shape - if shape[1] % M != 0: - not_divided_keys.append(key) - continue - if len(current_score.shape) == 4: ##TODO need to verify whether it's ok for transposed conv - current_score = current_score.permute(0, 2, 3, 1) ##cout,k,k,cin - current_score = current_score.reshape(current_score.shape[0], -1) - shape = current_score.shape - new_shape = [shape[0], shape[1] // M, M] - current_score_new = current_score.reshape(new_shape) - - threshold, _ = torch.kthvalue(current_score_new, N, dim=2) - threshold = threshold.unsqueeze(-1) - - threshold = threshold.expand(shape[0], shape[1] // M, M) - threshold = threshold.reshape((shape[0], shape[1])) - - one = torch.tensor([1.0]).to(current_score.device) - zero = torch.tensor([0.0]).to(current_score.device) - mask = torch.where(current_score <= threshold, zero, one) - current_score_new = current_score_new.reshape((shape[0], shape[1])) - ##to get the sum of N scores in each block with M - current_score_new = current_score_new * (1.0 - mask) - current_score_new = current_score_new.reshape(shape[0], shape[1] // M, M) - score_sum = torch.mean(current_score_new, dim=-1) - all_nm_masks[key] = mask - new_scores[key] = score_sum - - global_scores = torch.cat([torch.flatten(v) for v in new_scores.values()]) - k = int(target_sparsity_ratio * global_scores.numel()) - masks = {} - if not k < 1: - threshold, _ = torch.kthvalue(global_scores, k) - for key in new_scores.keys(): - score = new_scores[key] - zero = torch.tensor([0.0]).to(score.device) - one = torch.tensor([1.0]).to(score.device) - mask = torch.where(score <= threshold, zero, one) - mask = mask.repeat_interleave(M, dim=-1) - ## both zero will be zero - mask = mask + all_nm_masks[key] - mask = torch.where(mask <= 0, zero, one) - if torch.sum(mask) / mask.numel() < 1.0 - max_sparsity_ratio_per_layer: - ##trick, to prevent some layer not be purned too much - masks[key] = self.get_mask_single(new_scores[key], max_sparsity_ratio_per_layer) - masks[key] = masks[key].repeat_interleave(M, dim=-1) - ## both zero will be zero - masks[key] = masks[key] + all_nm_masks[key] - masks[key] = torch.where(masks[key] <= 0, zero, one) - else: - masks[key] = mask - for key in not_divided_keys: - p = scores[key] - masks[key] = torch.ones(p.shape).to(p.device) - logger.warning(f"{key} shape {scores[key].shape} cannot be divided by {self.pattern}") - - else: - for key in scores.keys(): - p = scores[key] - masks[key] = torch.ones(p.shape).to(p.device) - for key in masks.keys(): - if len(scores[key].shape) == 4 and len(masks[key].shape) == 2: ## need to permute - mask = masks[key] - mask = mask.reshape( - scores[key].shape[0], scores[key].shape[2], scores[key].shape[3], scores[key].shape[1] - ) - mask = mask.permute(0, 3, 1, 2) - masks[key] = mask - - return masks - - def get_pattern_lock_masks(self, modules): - """Obtain masks from original weight map, by masking where weights' are zero. - - Args: - modules: A dict{“layer_name”: Tensor}. Store weights. - - Returns: - A dict with the identical size as modules, containing pattern lock masks. - """ - pattern_lock_masks = {} - N, M = self.N, self.M - for key in modules.keys(): - weight = modules[key].weight - if len(weight.shape) == 4: # conv - weight = weight.permute(0, 2, 3, 1) - weight = weight.reshape(weight.shape[0], -1) - shape = weight.shape - ##TODO need to check whether it can be divisible later - new_shape = [shape[0], shape[1] // M, M] - weight_new = weight.reshape(new_shape) - mask1 = torch.ones(weight_new.shape) - mask2 = torch.ones(weight_new.shape) - nonzeros = torch.count_nonzero(weight_new, dim=-1) - zeros = M - nonzeros - mask1[weight_new == 0] = 0.0 - mask2[zeros >= N] = 0.0 - mask3 = mask1 + mask2 # zero in mask3 means its block has been completely pruned. - zero = torch.tensor([0.0]).to(weight.device) - one = torch.tensor([1.0]).to(weight.device) - mask = torch.where(mask3 == 0, zero, one) - mask = mask.reshape(shape) - orig_shape = modules[key].weight.shape - if len(orig_shape) == 4: - mask = mask.reshape(orig_shape[0], orig_shape[2], orig_shape[3], orig_shape[1]) - mask = mask.permute(0, 3, 1, 2) - if len(orig_shape) == 3: - mask = mask.reshape(orig_shape[0], orig_shape[2], orig_shape[1]) - mask = mask.permute(0, 2, 1) - - pattern_lock_masks[key] = mask.to(weight.device) - return pattern_lock_masks diff --git a/neural_compressor/experimental/pytorch_pruner/prune_utils.py b/neural_compressor/experimental/pytorch_pruner/prune_utils.py deleted file mode 100644 index dad0a009cbc..00000000000 --- a/neural_compressor/experimental/pytorch_pruner/prune_utils.py +++ /dev/null @@ -1,231 +0,0 @@ -"""Prune utils.""" - -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2022 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import re - -import yaml -from deprecated import deprecated - -try: - from ...conf.dotdict import DotDict -except: - from .dot_dict import DotDict ##TODO - -from .logger import logger - - -@deprecated(version="2.0") -def check_config(prune_config): - """Functions that check key-value is valid to run Pruning object. - - Args: - prune_config: A config dict object. Contains Pruning parameters and configurations. - - Returns: - None if everything is correct. - - Raises: - AssertionError. - """ - assert prune_config["start_step"] >= 0, "start_step should be greater than 0" - assert prune_config["end_step"] >= -1, "end_step should be greater than 0" - assert prune_config["end_step"] >= prune_config["start_step"], "end_step should be greater than start_step" - assert ( - prune_config["target_sparsity"] >= 0 and prune_config["target_sparsity"] < 1.0 - ), "begin_pruning_step should be in range [0,1)" - assert prune_config["update_frequency_on_step"] > 0, "update_frequency_on_step should be greater than 0" - assert ( - prune_config["max_sparsity_ratio_per_layer"] >= 0 and prune_config["max_sparsity_ratio_per_layer"] < 1 - ), "update_frequency_on_step should be greater than 0" - assert ( - prune_config["prune_domain"] == "global" or prune_config["prune_domain"] == "local" - ), "only support 'global' and 'local' prune domain" - if "x" in prune_config["pattern"]: - pattern = prune_config["pattern"].split("_")[-1].split("x") - if pattern[0] == "channel" or pattern[1] == "channel": - pass - else: - try: - N = int(pattern[0]) - M = int(pattern[1]) - except: - assert False, "N or M can't convert to int" - assert N > 0, "N should be greater than 0" - assert M > 0, "M should be greater than 0" - if ":" in prune_config["pattern"]: - pattern = prune_config["pattern"].split("_")[-1].split(":") - try: - N = int(pattern[0]) - M = int(pattern[1]) - except: - assert False, "N or M can't convert to int" - assert N > 0, "N should be greater than 0" - assert M > N, "M should be greater than N" - max_ratio = float(N) / M - assert prune_config["target_sparsity"] <= max_ratio, "in N:M pattern, the max sparsity is N/M={}".format( - max_ratio - ) - prune_config["max_sparsity_ratio_per_layer"] = min(max_ratio, prune_config["max_sparsity_ratio_per_layer"]) - - -@deprecated(version="2.0") -def reset_non_value_to_default(obj, key, default): - """Functions that add up undefined configurations. - - If some configurations are not defined in the configuration, set it to a default value. - - Args: - obj: A dict{key: value} - key: A string. Key in obj. - default: When the key is not in obj, Add key: default item in original obj. - """ - if isinstance(obj, dict): - if (key not in obj.keys()) or obj[key] is None: - return default - else: - return obj[key] - else: - if not hasattr(obj, key) or getattr(obj, key) is None: - return default - else: - return getattr(obj, key) - - -@deprecated(version="2.0") -def process_and_check_config(val): - """Functions which converts a initial configuration object to a Pruning configuration. - - Copy parameters and add some non-define parameters to a new Pruning configuration object. - - Args: - val: A dict directly read from a config file. - - Returns: - A dict whose contents which are regularized for a Pruning object. - """ - val = val["pruning"]["approach"]["weight_compression_pytorch"] - start_step = reset_non_value_to_default(val, "start_step", 0) - end_step = reset_non_value_to_default(val, "end_step", 0) - excluded_names = reset_non_value_to_default(val, "excluded_names", []) - prune_layer_type = reset_non_value_to_default(val, "prune_layer_type", ["Conv2d", "Linear"]) - target_sparsity = reset_non_value_to_default(val, "target_sparsity", 0.0) ## be care of this val - update_frequency_on_step = int(reset_non_value_to_default(val, "update_frequency_on_step", 1)) - prune_domain = reset_non_value_to_default(val, "prune_domain", "global") - prune_type = reset_non_value_to_default(val, "prune_type", "snip_momentum") - sparsity_decay_type = reset_non_value_to_default(val, "sparsity_decay_type", "exp") - max_sparsity_ratio_per_layer = reset_non_value_to_default(val, "max_sparsity_ratio_per_layer", 0.98) - names = reset_non_value_to_default(val, "names", []) - extra_excluded_names = reset_non_value_to_default(val, "extra_excluded_names", []) - pattern = reset_non_value_to_default(val, "pattern", "tile_pattern_4x1") - - pruners_info = [] - for info in val["pruners"]: - pruner = {} - pruner["start_step"] = reset_non_value_to_default(info, "start_step", start_step) - pruner["end_step"] = reset_non_value_to_default(info, "end_step", end_step) - pruner["excluded_names"] = reset_non_value_to_default(info, "excluded_names", excluded_names) - pruner["prune_layer_type"] = reset_non_value_to_default(info, "prune_layer_type", prune_layer_type) - pruner["target_sparsity"] = reset_non_value_to_default(info, "target_sparsity", target_sparsity) - pruner["update_frequency_on_step"] = reset_non_value_to_default( - info, "update_frequency_on_step", update_frequency_on_step - ) - pruner["prune_domain"] = reset_non_value_to_default(info, "prune_domain", prune_domain) - pruner["prune_type"] = reset_non_value_to_default(info, "prune_type", prune_type) - pruner["sparsity_decay_type"] = reset_non_value_to_default(info, "sparsity_decay_type", sparsity_decay_type) - pruner["max_sparsity_ratio_per_layer"] = reset_non_value_to_default( - info, "max_sparsity_ratio_per_layer", max_sparsity_ratio_per_layer - ) - pruner["names"] = reset_non_value_to_default(info, "names", names) - pruner["extra_excluded_names"] = reset_non_value_to_default(info, "extra_excluded_names", extra_excluded_names) - pruner["pattern"] = reset_non_value_to_default(info, "pattern", pattern) - check_config(pruner) - pruner_info = DotDict(pruner) - pruners_info.append(pruner_info) - return pruners_info - - -@deprecated(version="2.0") -def process_config(config): - """Obtain a config dict object from a config file. - - Args: - config: A string. The path to configuration file. - - Returns: - A config dict object. - """ - if isinstance(config, str): - try: - with open(config, "r") as f: - content = f.read() - try: - from .schema_check import schema - - except ImportError: - from ...conf.config import schema - - val = yaml.safe_load(content) - schema.validate(val) - except FileNotFoundError as f: - logger.error("{}.".format(f)) - raise RuntimeError("The yaml file is not exist. Please check the file name or path.") - except Exception as e: - logger.error("{}.".format(e)) - raise RuntimeError("The yaml file format is not correct. Please refer to document.") - - elif isinstance(config, DotDict): - val = config - else: - assert False, f"not supported type {config}" - - return process_and_check_config(val) - - -@deprecated(version="2.0") -def parse_to_prune(model, config): - """Keep target pruned layers.""" - modules = {} - if config["names"] is None or config["names"] == []: - config["names"] = [".*"] - for raw in config["names"]: - try: - pattern = re.compile(raw) - except: - assert False, f"regular expression match does not support {raw}" - for name, module in filter(lambda t: pattern.search(t[0]), model.named_modules()): - if type(module).__name__ in config["prune_layer_type"]: - modules[name] = module - return modules - - -@deprecated(version="2.0") -def parse_not_to_prune(modules, config): - """Drop non pruned layers.""" - exclude_names = config["extra_excluded_names"] - exclude_names.extend(config["excluded_names"]) - - patterns = [re.compile(s) for s in exclude_names] - if len(patterns) <= 0: - return modules - new_module = {} - for name in modules.keys(): - if any([p.search(name) for p in patterns]): - continue - new_module[name] = modules[name] - return new_module diff --git a/neural_compressor/experimental/pytorch_pruner/pruner.py b/neural_compressor/experimental/pytorch_pruner/pruner.py deleted file mode 100644 index a2ad1ad1e95..00000000000 --- a/neural_compressor/experimental/pytorch_pruner/pruner.py +++ /dev/null @@ -1,356 +0,0 @@ -"""Pruner module.""" - -# !/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2022 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import torch -from deprecated import deprecated - -from .logger import logger -from .patterns import get_pattern -from .scheduler import get_scheduler - -PRUNERS = {} - - -@deprecated(version="2.0") -def register_pruners(name): - """Class decorator to register a Pruner subclass to the registry. - - Decorator function used before a Pattern subclass. - Make sure that the Pruner class decorated by this function can be registered in PRUNERS. - - Args: - cls (class): The subclass of register. - name: A string. Define the pruner type. - - Returns: - cls: The class of register. - """ - - def register(pruner): - PRUNERS[name] = pruner - return pruner - - return register - - -@deprecated(version="2.0") -def get_pruner(modules, config): - """Get registered pruner class. - - Get a Pruner object from PRUNERS. - - Args: - modules: A dict {"module_name": Tensor}. Store the pruning modules' weights. - config: A config dict object. Contains the pruner information. - - Returns: - A Pruner object. - - Raises: AssertionError: Currently only support pruners which have been registered in PRUNERS. - """ - name = config["prune_type"] - if name not in PRUNERS.keys(): - assert False, f"does not support {name}, currently only support {PRUNERS.keys()}" - return PRUNERS[name](modules, config) - - -@deprecated(version="2.0") -class Pruner: - """Pruning Pruner. - - The class which executes pruning process. - 1. Defines pruning functions called at step begin/end, epoch begin/end. - 2. Defines the pruning criteria. - - Args: - modules: A dict {"module_name": Tensor}. Store the pruning modules' weights. - config: A config dict object. Contains the pruner information. - - Attributes: - modules: A dict {"module_name": Tensor}. Store the pruning modules' weights. - config: A config dict object. Contains the pruner information. - masks: A dict {"module_name": Tensor}. Store the masks for modules' weights. - scores: A dict {"module_name": Tensor}. Store the score for modules' weights, - which are used to decide pruning parts with a criteria. - pattern: A Pattern object. Defined in ./patterns.py - scheduler: A scheduler object. Defined in ./scheduler.py - current_sparsity_ratio: A float. Current model's sparsity ratio, initialized as zero. - global_step: A integer. The total steps the model has run. - start_step: A integer. When to trigger pruning process. - end_step: A integer. When to end pruning process. - update_frequency_on_step: A integer. The pruning frequency, which's valid when iterative - pruning is enabled. - target_sparsity_ratio: A float. The final sparsity after pruning. - max_sparsity_ratio_per_layer: A float. Sparsity ratio maximum for every module. - """ - - def __init__(self, modules, config): - """Initialize.""" - self.modules = modules - self.config = config - self.masks = {} - self.scores = {} - self.reg = None ##TODO need to add reg - self.pattern = get_pattern(config) - self.scheduler = get_scheduler(config) - self.current_sparsity_ratio = 0.0 - self._init() - - def _init(self): - """Auxiliary function for initializing.""" - self.global_step = -1 - self.start_step = self.config["start_step"] - self.end_step = self.config["end_step"] - self.update_frequency_on_step = self.config["update_frequency_on_step"] - ##this is different with original code - self.total_prune_cnt = (self.end_step - self.start_step + 1) // self.update_frequency_on_step - self.completed_pruned_cnt = 0 - self.masks = {} - for key in self.modules.keys(): - module = self.modules[key] - self.masks[key] = torch.ones(module.weight.shape).to(module.weight.device) ##TODO support bias or others - - self.target_sparsity_ratio = self.config["target_sparsity"] - - self.max_sparsity_ratio_per_layer = self.config["max_sparsity_ratio_per_layer"] - - def on_epoch_begin(self, epoch): - """Functions called in the beginning of each epoch.""" - pass - - def mask_weights(self): - """Functions called when masks are applied on corresponding modules' weights. - - Weights are multiplied with masks. This is the formal pruning process. - """ - with torch.no_grad(): - for key in self.modules.keys(): - module = self.modules[key] - module.weight.data = module.weight.data * self.masks[key] - - def on_step_begin(self, local_step): - """Functions called on the beginning of each step. - - Judge if the current step should execute a pruning process. - If so, using scores and criteria to update the masks and pruning the model. - Or, simply train the model with its original structure. - """ - self.global_step += 1 - if not self.check_is_pruned_step(self.global_step): - return - - if self.current_sparsity_ratio > self.target_sparsity_ratio: - return - - current_target_sparsity_ratio = self.scheduler.update_sparsity_ratio( - self.target_sparsity_ratio, self.completed_pruned_cnt, self.total_prune_cnt, self.masks - ) - logger.info(f"current target ratio is {current_target_sparsity_ratio}") - self.update_scores() - self.completed_pruned_cnt += 1 - if self.scores == {}: - return - self.masks = self.pattern.get_masks( - self.scores, current_target_sparsity_ratio, self.masks, self.max_sparsity_ratio_per_layer - ) - self.mask_weights() - - self.current_sparsity_ratio = self.pattern.get_sparsity_ratio(self.masks) - logger.info(f"current sparsity ratio is {self.current_sparsity_ratio}") - - def on_step_end(self): - """Functions called in the end of each step.""" - pass - - def on_epoch_end(self): - """Functions called in the end of each epoch.""" - pass - - def on_before_optimizer_step(self): - """Functions called before the optimizer.step().""" - pass - - def on_after_optimizer_step(self): - """Functions called after the optimizer.step(). - - Prune the model after optimization. - """ - self.mask_weights() - - def on_train_begin(self, dataloader=None): - """Functions called in the beginning of training.""" - pass - - def on_train_end(self): - """Functions called in the end of each training.""" - pass - - def on_before_eval(self): - """Functions called in the beginning of evaluation.""" - pass - - def on_after_eval(self): - """Functions called in the end of evaluation.""" - pass - - def check_is_pruned_step(self, step): - """Decide whether the current step should execute a pruning process.""" - if step < self.start_step or step > self.end_step: - return False - if int(step - self.start_step) % self.update_frequency_on_step == 0: - return True - return False - - def update_scores(self): - """Update self.scores.""" - pass - - -@deprecated(version="2.0") -@register_pruners("magnitude") -class MagnitudePruner(Pruner): - """Pruning Pruner. - - A Pruner class derived from Pruner. In this pruner, the scores are calculated based on weights. - - Args: - modules: A dict {"module_name": Tensor}. Store the pruning modules' weights. - config: A config dict object. Contains the pruner information. - - Attributes: - Inherit from parent class Pruner. - """ - - def __init__(self, modules, config): - """Initialize.""" - super(MagnitudePruner, self).__init__(modules, config) - self.scores = {} - - def update_scores(self): - """Update self.scores.""" - with torch.no_grad(): - for key in self.modules.keys(): - p = self.modules[key].weight.data - self.scores[key] = p - - -@deprecated(version="2.0") -@register_pruners("snip") -class SnipPruner(Pruner): - """Pruning Pruner. - - A Pruner class derived from Pruner. In this pruner, the scores are calculated based on SNIP. - Please refer to SNIP: Single-shot Network Pruning based on Connection Sensitivity - (https://arxiv.org/abs/1810.02340) - - Args: - modules: A dict {"module_name": Tensor}. Store the pruning modules' weights. - config: A config dict object. Contains the pruner information. - - Attributes: - Inherit from parent class Pruner. - """ - - def __init__(self, modules, config): - """Initialize.""" - super(SnipPruner, self).__init__(modules, config) - assert self.config.end_step > 0, "gradient based criteria does not work on step 0" - self.scores = {} - - def on_after_optimizer_step(self): - """Functions called after the optimizer.step(). - - Prune the model after optimization and update the scores based on weights and gradients. - """ - self.mask_weights() - with torch.no_grad(): - for key in self.modules.keys(): - p = self.modules[key].weight - self.scores[key] = torch.abs(p * p.grad) - - -@deprecated(version="2.0") -@register_pruners("snip_momentum") -class SnipMomentumPruner(Pruner): - """Pruning Pruner. - - A Pruner class derived from Pruner. In this pruner, the scores are calculated based on SNIP. - Moreoever, the score map is updated with a momentum like process. - - Args: - modules: A dict {"module_name": Tensor}. Store the pruning modules' weights. - config: A config dict object. Contains the pruner information. - - Attributes: - Inherit from parent class Pruner. - """ - - def __init__(self, modules, config): - """Initialize.""" - super(SnipMomentumPruner, self).__init__(modules, config) - assert self.config.end_step > 0, "gradient based criteria does not work on step 0" - # self.scores = {} - for key in modules.keys(): - p = modules[key].weight - self.scores[key] = torch.zeros(p.shape).to(p.device) - - def on_after_optimizer_step(self): - """Functions called after the optimizer.step(). - - Prune the model after optimization and update the scores based on weights and gradients. - """ - self.mask_weights() - with torch.no_grad(): - for key in self.modules.keys(): - p = self.modules[key].weight - self.scores[key] *= 0.9 ##magic number - self.scores[key] += 1.0 * torch.abs(p * p.grad) - - -@deprecated(version="2.0") -@register_pruners("pattern_lock") -class PatternLockPruner(Pruner): - """Pruning Pruner. - - A Pruner class derived from Pruner. In this pruner, original model's sparsity pattern will be fixed while training. - This pruner is useful when you want to train a sparse model without change its original structure. - - Args: - modules: A dict {"module_name": Tensor}. Store the pruning modules' weights. - config: A config dict object. Contains the pruner information. - - Attributes: - Inherit from parent class Pruner. - """ - - def __init__(self, modules, config): - """Initialize.""" - super(PatternLockPruner, self).__init__(modules, config) - assert self.config.end_step == self.config.start_step, "pattern_lock pruner only supports one shot mode" - - def on_step_begin(self, local_step): - """Functions called on the beginning of each step.""" - self.global_step += 1 - if not self.check_is_pruned_step(self.global_step): - return - self.masks = self.pattern.get_pattern_lock_masks(self.modules) - - def on_after_optimizer_step(self): - """Functions called after the optimizer.step().""" - self.mask_weights() diff --git a/neural_compressor/experimental/pytorch_pruner/pruning.py b/neural_compressor/experimental/pytorch_pruner/pruning.py deleted file mode 100644 index 6a91e67a6c2..00000000000 --- a/neural_compressor/experimental/pytorch_pruner/pruning.py +++ /dev/null @@ -1,169 +0,0 @@ -"""Pruning module.""" - -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2022 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import torch.nn -from deprecated import deprecated - -from .logger import logger -from .prune_utils import parse_not_to_prune, parse_to_prune, process_config -from .pruner import get_pruner - - -@deprecated(version="2.0") -class Pruning: - """Pruning. - - The main class that users will used in codes to do pruning. - Contain at least one Pruner object. - - Args: - config: a string. The path to a config file. For config file template, please refer to - https://github.com/intel/neural-compressor/tree/master/examples/pytorch/nlp/huggingface_models/text-classification/pruning/pytorch_pruner/eager/ - - Attributes: - model: The model object to prune. - config_file_path: A string. The path to a config file. - pruners: A list. A list of Pruner objects. - pruner_info: A config dict object. Contains pruners' information. - """ - - def __init__(self, config): - """Initialize.""" - self.model = None - self.config_file_path = config - self.pruners = [] - self.pruner_info = process_config(self.config_file_path) - - def update_items_for_all_pruners(self, **kwargs): - """Functions which add User-defined arguments to the original configurations. - - The original config of pruning is read from a file. - However, users can still modify configurations by passing key-value arguments in this function. - Please note that the key-value arguments' keys are analysable in current configuration. - """ - for item in self.pruner_info: - for key in kwargs: - if key in item.keys(): - item[key] = kwargs[key] - - def prepare(self): - """Align with old API's calling pipeline.""" - pass - - def get_sparsity_ratio(self): - """Functions that calculate a modules/layers sparsity. - - Returns: - Three floats. - elementwise_over_matmul_gemm_conv refers to zero elements' ratio in pruning layers. - elementwise_over_all refers to zero elements' ratio in all layers in the model. - blockwise_over_matmul_gemm_conv refers to all-zero blocks' ratio in pruning layers. - """ - pattern_sparsity_cnt = 0 - element_sparsity_cnt = 0 - for pruner in self.pruners: - modules = pruner.modules - sparsity_ratio = pruner.pattern.get_sparsity_ratio(pruner.masks) - cnt = 0 - for key in modules.keys(): - cnt += modules[key].weight.numel() - pattern_sparsity_cnt += int(cnt * sparsity_ratio) - for key in pruner.masks.keys(): - element_sparsity_cnt += torch.sum(pruner.masks[key] == 0).data.item() - - linear_conv_cnt = 0 - param_cnt = 0 - for name, module in self.model.named_modules(): - if type(module).__name__ in ["Linear"] or "Conv" in type(module).__name__: - linear_conv_cnt += module.weight.numel() - - for n, param in self.model.named_parameters(): - param_cnt += param.numel() - blockwise_over_matmul_gemm_conv = float(pattern_sparsity_cnt) / linear_conv_cnt - elementwise_over_matmul_gemm_conv = float(element_sparsity_cnt) / linear_conv_cnt - elementwise_over_all = float(element_sparsity_cnt) / param_cnt - - return elementwise_over_matmul_gemm_conv, elementwise_over_all, blockwise_over_matmul_gemm_conv - - def _generate_pruners(self): - """Functions that obtain Pruner objects.""" - assert isinstance(self.model, torch.nn.Module) - - for info in self.pruner_info: - modules = parse_to_prune(self.model, info) - modules = parse_not_to_prune(modules, info) - if modules == {}: - logger.warning("one pruner hooks no layers, please have a check") - - self.pruners.append(get_pruner(modules, info)) - info["modules"] = [key for key in modules.keys()] - info["len_of_modules"] = len(info["modules"]) - logger.info(info) - - def on_train_begin(self): - """Functions called in the beginning of training process. - - Before training, ensure that pruners are generated. - """ - self._generate_pruners() ##TODO is there better place to place - - def on_epoch_begin(self, epoch): - """Functions called in the beginning of every epoch.""" - for pruner in self.pruners: - pruner.on_epoch_begin(epoch) - - def on_step_begin(self, local_step): - """Functions called in the beginning of every step.""" - for pruner in self.pruners: - pruner.on_step_begin(local_step) - - def on_before_optimizer_step(self): - """Functions called before optimizer.step().""" - for pruner in self.pruners: - pruner.on_before_optimizer_step() - - def on_step_end(self): - """Functions called in the end of every step.""" - for pruner in self.pruners: - pruner.on_step_end() - - def on_epoch_end(self): - """Functions called in the end of every epoch.""" - for pruner in self.pruners: - pruner.on_epoch_end() - - def on_train_end(self): - """Functions called in the end of training.""" - for pruner in self.pruners: - pruner.on_train_end() - - def on_before_eval(self): - """Functions called in the beginning of evaluation.""" - for pruner in self.pruners: - pruner.on_before_eval() - - def on_after_eval(self): - """Functions called in the end of evaluation.""" - for pruner in self.pruners: - pruner.on_after_eval() - - def on_after_optimizer_step(self): - """Functions called after optimizer.step().""" - for pruner in self.pruners: - pruner.on_after_optimizer_step() diff --git a/neural_compressor/experimental/pytorch_pruner/scheduler.py b/neural_compressor/experimental/pytorch_pruner/scheduler.py deleted file mode 100644 index 8d7461c0c17..00000000000 --- a/neural_compressor/experimental/pytorch_pruner/scheduler.py +++ /dev/null @@ -1,172 +0,0 @@ -"""Scheduler module.""" - -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2022 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import math - -from deprecated import deprecated - -SCHEDULERS = {} - - -@deprecated(version="2.0") -def register_scheduler(name): - """Class decorator used to register a Scheduler subclass to the registry. - - Decorator function used before a Scheduler subclass. - Make sure that the Scheduler class decorated by this function can be registered in SCHEDULERS. - - Args: - cls (class): The class of register. - name: A string. Define the scheduler type. - - Returns: - cls: The class of register. - """ - - def register(scheduler): - SCHEDULERS[name] = scheduler - return scheduler - - return register - - -@deprecated(version="2.0") -def get_scheduler(config): - """Get registered scheduler class. - - Get a scheduler object from SCHEDULERS. - - Args: - config: A config dict object. Contains the scheduler information. - - Returns: - A Scheduler object. - """ - name = "iterative" - if config.start_step == config.end_step: - name = "oneshot" - return SCHEDULERS[name](config) - - -@deprecated(version="2.0") -class Scheduler: - """Pruning Scheduler. - - The class which defines a sparsity changing process during pruning. - Mainly contains two types: - 1. iterative scheduler. Prune the model from dense to target sparsity gradually. - 2. one-shot scheduler. Prune the model in a single step and reach the target sparsity. - - Args: - config: A config dict object. Contains the scheduler information. - - Attributes: - config: A config dict object. Contains the scheduler information. - """ - - def __init__(self, config): - """Initialize.""" - self.config = config - - def update_sparsity_ratio(self, aggressive_ratio, current_prune_step, total_prune_steps, masks): - """To be implemented in subclasses.""" - raise NotImplementedError - - -@deprecated(version="2.0") -@register_scheduler("oneshot") -class OneshotScheduler(Scheduler): - """Pruning Scheduler. - - A Scheduler class derived from Scheduler. - Prune the model to target sparsity once. - - Args: - config: A config dict object. Contains the scheduler information. - - Attributes: - Inherit from parent class Scheduler. - """ - - def __init__(self, config): - """Initialize.""" - super(OneshotScheduler, self).__init__(config) - - def update_sparsity_ratio(self, aggressive_ratio, current_prune_step, total_prune_steps, masks): - """Return the aggressive ratio.""" - return aggressive_ratio - - -@deprecated(version="2.0") -@register_scheduler("iterative") -class IterativeScheduler(Scheduler): - """Pruning Scheduler. - - A Scheduler class derived from Scheduler. - Prune the model to from dense to target sparsity in several steps. - - Args: - config: A config dict object. Contains the scheduler information. - - Attributes: - Inherit from parent class Scheduler. - """ - - def __init__(self, config): - """Initialize.""" - super(IterativeScheduler, self).__init__(config) - # self.decay_type = config["sparsity_decay_type"] - - def update_sparsity_ratio(self, target_ratio, current_prune_step, total_prune_steps, masks): - """Obtain new target sparsity ratio according to the step. - - Args: - target_ratio: A float. The target sparsity ratio. - current_prune_step: A integer. The current pruning step. - total_prune_steps: A integer. The total steps included in the pruning progress. - masks: A dict{"module_name": Tensor}. The masks for modules' weights. - - Returns: - A float. the target sparsity ratio the model will reach after the next pruning step. - """ - aggressive_ratio = target_ratio - # if self.config.prune_domain == "global": - # aggressive_ratio += 0.02 - - aggressive_ratio = min(self.config.max_sparsity_ratio_per_layer, aggressive_ratio) # legacy issue - - decay_type = self.config.sparsity_decay_type - if decay_type == "cos": - current_target_sparsity = (aggressive_ratio) * ( - 1.0 - math.cos(float(current_prune_step) / total_prune_steps * (math.pi / 2)) - ) - elif decay_type == "exp": - target_dense_change_ratio = (1.0 - aggressive_ratio) ** (1 / total_prune_steps) - current_target_sparsity = 1.0 - target_dense_change_ratio**current_prune_step - - elif decay_type == "linear": - current_target_sparsity = (aggressive_ratio) * float(current_prune_step) / total_prune_steps - - elif decay_type == "cube": - current_target_sparsity = (aggressive_ratio) * ((float(current_prune_step) / total_prune_steps) ** 3) - else: - assert False, "{} is not supported".format(decay_type) - - current_target_sparsity = min(target_ratio, current_target_sparsity) - return current_target_sparsity diff --git a/neural_compressor/experimental/quantization.py b/neural_compressor/experimental/quantization.py deleted file mode 100644 index 0d029eb2231..00000000000 --- a/neural_compressor/experimental/quantization.py +++ /dev/null @@ -1,485 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ============================================================================== -"""Neural Compressor Quantization API.""" - -import os -import pickle -import random - -import numpy as np -from deprecated import deprecated - -from ..conf.config import QuantConf -from ..conf.dotdict import DotDict, deep_get, deep_set -from ..conf.pythonic_config import Config -from ..model import BaseModel -from ..model.model import get_model_fwk_name -from ..model.tensorflow_model import TensorflowQATModel -from ..utils import logger -from ..utils.create_obj_from_config import create_dataloader -from ..utils.utility import time_limit -from .component import Component -from .strategy import EXP_STRATEGIES - - -@deprecated(version="2.0") -class Quantization(Component): - """This class provides easy use API for quantization. - - It automatically searches for optimal quantization recipes for low precision model inference, - achieving best tuning objectives like inference performance within accuracy loss constraints. - Tuner abstracts out the differences of quantization APIs across various DL frameworks - and brings a unified API for automatic quantization that works on frameworks including - tensorflow, pytorch and mxnet. - Since DL use cases vary in the accuracy metrics (Top-1, MAP, ROC etc.), loss criteria - (<1% or <0.1% etc.) and tuning objectives (performance, memory footprint etc.). - Tuner class provides a flexible configuration interface via YAML for users to specify - these parameters. - - Args: - conf_fname_or_obj (string or obj): The path to the YAML configuration file or - QuantConf class containing accuracy goal, tuning objective and preferred - calibration & quantization tuning space etc. - """ - - def __init__(self, conf_fname_or_obj=None): - """Quantization constructor.""" - super(Quantization, self).__init__() - if isinstance(conf_fname_or_obj, QuantConf): - self.conf = conf_fname_or_obj - elif isinstance(conf_fname_or_obj, Config): - self.conf = QuantConf() - self.conf.map_pyconfig_to_cfg(conf_fname_or_obj) - else: - self.conf = QuantConf(conf_fname_or_obj) - self._init_with_conf() - - seed = self.cfg.tuning.random_seed - random.seed(seed) - np.random.seed(seed) - self._calib_dataloader = None - self._calib_func = None - - def _create_eval_dataloader(self, cfg): - """Create default evaluation dataloader if eval_func is not set.""" - # when eval_func is set, will be directly used and eval_dataloader can be None - if self._eval_func is None: - if self._eval_dataloader is None: - eval_dataloader_cfg = deep_get(cfg, "evaluation.accuracy.dataloader") - if eval_dataloader_cfg is None: - logger.info( - "Because both eval_dataloader_cfg and user-defined eval_func are None," - " automatically setting 'tuning.exit_policy.performance_only = True'." - ) - deep_set(cfg, "tuning.exit_policy.performance_only", True) - logger.info( - "The cfg.tuning.exit_policy.performance_only is: {}".format( - cfg.tuning.exit_policy.performance_only - ) - ) - else: - if deep_get(cfg, "evaluation.accuracy.iteration") == -1 and "dummy_v2" in deep_get( - cfg, "evaluation.accuracy.dataloader.dataset", {} - ): - deep_set(cfg, "evaluation.accuracy.iteration", 10) - - self._eval_dataloader = create_dataloader(self.framework, eval_dataloader_cfg) - if os.environ.get("PERFORMANCE_ONLY") in ["0", "1"]: - performance_only = bool(int(os.environ.get("PERFORMANCE_ONLY"))) - deep_set(cfg, "tuning.exit_policy.performance_only", performance_only) - logger.info( - "Get environ 'PERFORMANCE_ONLY={}'," - " force setting 'tuning.exit_policy.performance_only = True'.".format(performance_only) - ) - - def _create_calib_dataloader(self, cfg): - """Create default calibration dataloader if train_func is not set.""" - approach_cfg = deep_get(cfg, "quantization.approach") - - if self._calib_dataloader is None and self._calib_func is None: - if approach_cfg in ["post_training_static_quant", "post_training_auto_quant"]: - calib_dataloader_cfg = deep_get(cfg, "quantization.calibration.dataloader") - - if approach_cfg == "post_training_auto_quant" and calib_dataloader_cfg is None: - logger.error( - "dataloader is required for 'post_training_auto_quant'. " - "use 'post_training_dynamic_quant' instead if no dataloader provided." - ) - assert calib_dataloader_cfg is not None, ( - "dataloader field of calibration field of quantization section " - "in yaml file should be configured as calib_dataloader property is NOT set!" - ) - - if deep_get(calib_dataloader_cfg, "shuffle"): - logger.warning("Reset `shuffle` field to False when post_training_static_quant" " is selected.") - deep_set(calib_dataloader_cfg, "shuffle", False) - elif approach_cfg == "quant_aware_training": - calib_dataloader_cfg = deep_get(cfg, "quantization.train.dataloader") - assert calib_dataloader_cfg is not None, ( - "dataloader field of train field of quantization section " - "in yaml file should be configured as calib_dataloader property is NOT set!" - ) - else: - calib_dataloader_cfg = None - - if calib_dataloader_cfg: - self._calib_dataloader = create_dataloader(self.framework, calib_dataloader_cfg) - - def pre_process(self): - """Prepare dataloaders, qfuncs for Component.""" - cfg = self.conf.usr_cfg - assert isinstance(self._model, BaseModel), "need set your Model for quantization...." - - self._create_eval_dataloader(cfg) - self._create_calib_dataloader(cfg) - strategy = cfg.tuning.strategy.name.lower() - if cfg.quantization.quant_level == 0: - strategy = "conservative" - logger.info("On the premise that the accuracy meets the conditions, improve the performance.") - - if strategy == "mse_v2": - if not (self.framework.startswith("tensorflow") or self.framework == "pytorch_fx"): - strategy = "basic" - logger.warning(f"MSE_v2 does not support {self.framework} now, use basic instead.") - logger.warning("Only tensorflow, pytorch_fx is supported by MSE_v2 currently.") - assert strategy in EXP_STRATEGIES, "Tuning strategy {} is NOT supported".format(strategy) - - _resume = None - # check if interrupted tuning procedure exists. if yes, it will resume the - # whole auto tune process. - self.resume_file = ( - os.path.abspath(os.path.expanduser(cfg.tuning.workspace.resume)) - if cfg.tuning.workspace and cfg.tuning.workspace.resume - else None - ) - if self.resume_file: - assert os.path.exists(self.resume_file), "The specified resume file {} doesn't exist!".format( - self.resume_file - ) - with open(self.resume_file, "rb") as f: - _resume = pickle.load(f).__dict__ - - self.strategy = EXP_STRATEGIES[strategy]( - self._model, - self.conf, - self._calib_dataloader, - self._calib_func, - self._eval_dataloader, - self._eval_func, - _resume, - self.hooks, - ) - - if getattr(self._calib_dataloader, "distributed", False): - self.register_hook("on_train_begin", self.strategy.adaptor._pre_hook_for_hvd) - - def execute(self): - """Quantization execute routine based on strategy design.""" - try: - with time_limit(self.conf.usr_cfg.tuning.exit_policy.timeout): - logger.debug("Dump user yaml configuration:") - logger.debug(self.conf.usr_cfg) - self.strategy.traverse() - except KeyboardInterrupt: - pass - except Exception as e: - logger.error("Unexpected exception {} happened during tuning.".format(repr(e))) - import traceback - - traceback.print_exc() - finally: - if self.strategy.best_qmodel: - logger.info( - "Specified timeout or max trials is reached! " - "Found a quantized model which meet accuracy goal. Exit." - ) - self.strategy.deploy_config() - else: - logger.error( - "Specified timeout or max trials is reached! " - "Not found any quantized model which meet accuracy goal. Exit." - ) - - return self.strategy.best_qmodel - - def __call__(self): - """Automatic quantization tuning main entry point. - - This interface works on all the DL frameworks that neural_compressor supports - and provides three usages: - a) Fully yaml configuration: User specifies all the info through yaml, - including dataloaders used in calibration and evaluation phases - and quantization tuning settings. - - For this usage, only model parameter is mandatory. - - b) Partial yaml configuration: User specifies dataloaders used in calibration - and evaluation phase by code. - The tool provides built-in dataloaders and evaluators, user just need provide - a dataset implemented __iter__ or __getitem__ methods and invoke dataloader() - with dataset as input parameter to create neural_compressor dataloader before calling this - function. - - After that, User specifies fp32 "model", calibration dataset "calib_dataloader" - and evaluation dataset "eval_dataloader". - The calibrated and quantized model is evaluated with "eval_dataloader" - with evaluation metrics specified in the configuration file. The evaluation tells - the tuner whether the quantized model meets the accuracy criteria. If not, - the tuner starts a new calibration and tuning flow. - - For this usage, model, calib_dataloader and eval_dataloader parameters are mandatory. - - c) Partial yaml configuration: User specifies dataloaders used in calibration phase - by code. - This usage is quite similar with b), just user specifies a custom "eval_func" - which encapsulates the evaluation dataset by itself. - The calibrated and quantized model is evaluated with "eval_func". - The "eval_func" tells the tuner whether the quantized model meets - the accuracy criteria. If not, the Tuner starts a new calibration and tuning flow. - - For this usage, model, calib_dataloader and eval_func parameters are mandatory. - - Returns: - quantized model: best qanitized model found, otherwise return None - """ - return super(Quantization, self).__call__() - - fit = __call__ - - def dataset(self, dataset_type, *args, **kwargs): - """Get dataset according to dataset_type.""" - from ..data import Datasets - - return Datasets(self.framework)[dataset_type](*args, **kwargs) - - @property - def calib_dataloader(self): - """Get `calib_dataloader` attribute.""" - return self._calib_dataloader - - @calib_dataloader.setter - def calib_dataloader(self, dataloader): - """Set Data loader for calibration, mandatory for post-training quantization. - - It is iterable and the batched data should consists of a tuple like - (input, label) if the calibration dataset containing label, or yield (input, _) - for label-free calibration dataset, the input in the batched data will be used for - model inference, so it should satisfy the input format of specific model. - In calibration process, label of data loader will not be used and - neither the postprocess and metric. User only need to set - calib_dataloader when calib_dataloader can not be configured from yaml file. - - Args: - dataloader(generator): user are supported to set a user defined dataloader - which meet the requirements that can yield tuple of - (input, label)/(input, _) batched data. Another good - practice is to use neural_compressor.experimental.common.DataLoader - to initialize a neural_compressor dataloader object. Notice - neural_compressor.experimental.common.DataLoader is just a wrapper of the - information needed to build a dataloader, it can't yield - batched data and only in this setter method - a 'real' calib_dataloader will be created, - the reason is we have to know the framework info - and only after the Quantization object created then - framework information can be known. - Future we will support creating iterable dataloader - from neural_compressor.experimental.common.DataLoader - """ - from .common import _generate_common_dataloader - - self._calib_dataloader = _generate_common_dataloader(dataloader, self.framework) - - @property - def metric(self): - """Get `metric` attribute.""" - assert False, "Should not try to get the value of `metric` attribute." - return None - - @metric.setter - def metric(self, user_metric): - """Set metric class and neural_compressor will initialize this class when evaluation. - - neural_compressor have many built-in metrics, but user can set specific metric through - this api. The metric class should take the outputs of the model or - postprocess(if have) as inputs, neural_compressor built-in metric always take - (predictions, labels) as inputs for update, - and user_metric.metric_cls should be sub_class of neural_compressor.metric.BaseMetric - or user defined metric object - Args: - user_metric(neural_compressor.experimental.common.Metric): - user_metric should be object initialized from - neural_compressor.experimental.common.Metric, in this method the - user_metric.metric_cls will be registered to - specific frameworks and initialized. - """ - if deep_get(self.conf.usr_cfg, "evaluation.accuracy.metric"): - logger.warning( - "Override the value of `metric` field defined in yaml file" - " as user defines the value of `metric` attribute by code." - ) - - from ..metric import METRICS - from .common import Metric as NCMetric - - if isinstance(user_metric, NCMetric): - name = user_metric.name - metric_cls = user_metric.metric_cls - metric_cfg = {name: {**user_metric.kwargs}} - else: - for i in ["reset", "update", "result"]: - assert hasattr(user_metric, i), "Please realise {} function" "in user defined metric".format(i) - metric_cls = type(user_metric).__name__ - name = "user_" + metric_cls - metric_cfg = {name: id(user_metric)} - deep_set(self.conf.usr_cfg, "evaluation.accuracy.metric", metric_cfg) - self.conf.usr_cfg = DotDict(self.conf.usr_cfg) - metrics = METRICS(self.framework) - metrics.register(name, metric_cls) - self._metric = user_metric - - @property - def objective(self): - """Get `objective` attribute.""" - assert False, "Should not try to get the value of `objective` attribute." - return None - - @objective.setter - def objective(self, user_objective): - """Set objective, neural_compressor supports built-in objectives and user defined objective. - - The built-in objectives include Accuracy, Performance, Footprint and ModelSize. - """ - if deep_get(self.conf.usr_cfg, "tuning.multi_objectives.objective") or deep_get( - self.conf.usr_cfg, "tuning.objective" - ): - logger.warning( - "Override the value of `objective` field defined in yaml file" - " as user defines the value of `objective` attribute by code." - ) - - user_obj_cfg = ( - "tuning.objective" - if deep_get(self.conf.usr_cfg, "tuning.objective") - else "tuning.multi_objectives.objective" - ) - from ..objective import objective_custom_registry - - objective_cls = type(user_objective) - name = user_objective.__class__.__name__ - objective_cfg = name if deep_get(self.conf.usr_cfg, "tuning.objective") else [name] - deep_set(self.conf.usr_cfg, user_obj_cfg, objective_cfg) - self.conf.usr_cfg = DotDict(self.conf.usr_cfg) - objective_custom_registry(name, objective_cls) - - @property - def postprocess(self, user_postprocess): - """Get `postprocess` attribute.""" - assert False, "Should not try to get the value of `postprocess` attribute." - return None - - @postprocess.setter - def postprocess(self, user_postprocess): - """Set postprocess class and neural_compressor will initialize this class when evaluation. - - The postprocess class should take the outputs of the model as inputs, and - output (predictions, labels) as inputs for metric update. - user_postprocess.postprocess_cls should be sub_class of neural_compressor.data.BaseTransform. - - Args: - user_postprocess: neural_compressor.experimental.common.Postprocess - user_postprocess should be object initialized from - neural_compressor.experimental.common.Postprocess, - in this method the user_postprocess.postprocess_cls will be - registered to specific frameworks and initialized. - """ - from .common import Postprocess as NCPostprocess - - assert isinstance( - user_postprocess, NCPostprocess - ), "please initialize a neural_compressor.experimental.common.Postprocess and set...." - postprocess_cfg = {user_postprocess.name: {**user_postprocess.kwargs}} - if deep_get(self.conf.usr_cfg, "evaluation.accuracy.postprocess"): - logger.warning( - "Override the value of `postprocess` field defined in yaml file" - " as user defines the value of `postprocess` attribute by code." - ) - deep_set(self.conf.usr_cfg, "evaluation.accuracy.postprocess.transform", postprocess_cfg) - from neural_compressor.data import TRANSFORMS - - postprocesses = TRANSFORMS(self.framework, "postprocess") - postprocesses.register(user_postprocess.name, user_postprocess.postprocess_cls) - - # BELOW API TO BE DEPRECATED! - @property - def q_func(self): - """Get `q_func` attribute.""" - assert False, "Should not try to get the value of `q_func` attribute." - return None - - @q_func.setter - def q_func(self, user_q_func): - """Calibrate quantization parameters for Post-training static quantization. - - It is optional and only takes effect when user choose - "post_training_static_quant" approach in yaml. - - Args: - user_q_func: This function takes "model" as input parameter - and executes entire inference process or training process with self - contained training hyper-parameters.. - """ - self._calib_func = user_q_func - - calib_func = q_func - - @property - def model(self): - """Override model getter method to handle quantization aware training case.""" - return self._model - - @model.setter - def model(self, user_model): - """Override model setter method to handle quantization aware training case. - - Args: - user_model: user are supported to set model from original framework model format - (eg, tensorflow frozen_pb or path to a saved model), - but not recommended. Best practice is to set from a initialized - neural_compressor.experimental.common.Model. - If tensorflow model is used, model's inputs/outputs will be - auto inferenced, but sometimes auto inferenced - inputs/outputs will not meet your requests, - set them manually in config yaml file. - Another corner case is slim model of tensorflow, - be careful of the name of model configured in yaml file, - make sure the name is in supported slim model list. - """ - approach_cfg = deep_get(self.cfg, "quantization.approach") - if not self.framework: - self.framework = get_model_fwk_name(user_model) - if self.framework == "tensorflow" and approach_cfg == "quant_aware_training": - if type(user_model) == str: - self._model = TensorflowQATModel(user_model) - else: - self._model = TensorflowQATModel(user_model._model) - else: - Component.model.__set__(self, user_model) - - def __repr__(self): - """Return the class string.""" - return "Quantization" diff --git a/neural_compressor/experimental/scheduler.py b/neural_compressor/experimental/scheduler.py deleted file mode 100644 index 44434a07b2c..00000000000 --- a/neural_compressor/experimental/scheduler.py +++ /dev/null @@ -1,394 +0,0 @@ -"""Scheduler class.""" - -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import os - -from deprecated import deprecated - -from ..conf.dotdict import DotDict, deep_set -from ..model import BaseModel -from ..model.model import get_model_fwk_name -from ..utils import logger -from .benchmark import Benchmark -from .common import Model as NCModel -from .component import Component -from .distillation import Distillation -from .graph_optimization import Graph_Optimization -from .model_conversion import ModelConversion -from .pruning import Pruning -from .quantization import Quantization - -SUPPORTED_COMPONENTS = [Quantization, Pruning, Graph_Optimization, ModelConversion, Benchmark, Component] - - -@deprecated(version="2.0") -class Scheduler(object): - """Scheduler for neural_compressor component pipeline execution. - - Neural Compressor supports several separate components: Quantization, Pruning, Benchmarking. - This scheduler will sequentially execute specified components by the order of - appending. This interface provides an unique entry to pipeline execute all supported - components. - - There are two typical usages: - 1) if all information are set in user configuration yaml files by using neural_compressor built-in - dataloaders/datasets/metrics, the code usage is like below: - - prune = Pruning('/path/to/pruning.yaml') - quantizer = Quantization('/path/to/quantization.yaml') - scheduler = Scheduler() - scheduler.model('/path/to/model') - scheduler.append(prune) - scheduler.append(quantizer) - opt_model = scheduler() - opt_model.save() - - - 2) if neural_compressor built-in dataloaders/datasets/metrics could not fully meet user requirements, - customized dataloaders/datasets/metrics are needed, the code usage is like below: - - prune = Pruning('/path/to/pruning.yaml') - prune.train_func = ... # optional if it is configured in user yaml. - prune.eval_dataloader = ... # optional if it is configured in user yaml. - prune.eval_func = ... # optional if it is configured in user yaml. - - quantizer = Quantization('/path/to/quantization.yaml') - quantizer.metric = ... # optional if it is configured in user yaml. - quantizer.calib_dataloader = ... # optional if it is configured in user yaml. - quantizer.eval_dataloader = ... # optional if it is configured in user yaml. - - scheduler = Scheduler() - scheduler.model('/path/to/model') - scheduler.append(prune) - scheduler.append(quantizer) - opt_model = scheduler() - opt_model.save() - """ - - def __init__(self): - """Initialize the attributes.""" - self._model = None - self._train_func = None - self._eval_func = None - self.components = [] - - def append(self, *args): - """Add neural_compressor component into pipeline for sequential execution. - - Args: - conf_fname_or_obj (string or obj): The path to user configuration yaml file or - conf class. - calib_dataloader (generator): Optional. Data loader for calibration of Post- - Training Static Quantization, - or None for Post-Training Dynamic Quantization, - or Data loader for training phase of Quantization- - aware Training, - or Data loader for training phase of Pruning, - if its corresponding field is not configured in yaml. - eval_dataloader (generator): Optional. Data loader for evaluation phase of all - neural_compressor components, if its corresponding field is not - configured in yaml and eval_func is not specified. - postprocess (Postprocess): Optional. Object initialized from common.Postprocess. - metric (Metric): Optional. Object initialized from common.Metric. - q_func (func): Optional. Training function for Quantization-Aware - Training and Pruning cases if user doesn't provide - training info in user configuration yaml file. - eval_func (func): Optional. Evaluation function for Quantization-Aware - Training and Pruning, being None for other cases. - kwargs (named arguments): Reserved for interface extension. - """ - for item in args: - assert any([isinstance(item, supported_component) for supported_component in SUPPORTED_COMPONENTS]) - self.components.append(item) - - def __call__(self): - """Do sequential pipeline execution. - - NOTE: it's user responsibility to ensure the output of each component could be fed as - the input of next component. - - Returns: - optimized model: best optimized model generated, otherwise return None - """ - assert self.model, "Scheduler class's model property should be set " "before invoking this __call__() function" - model = self.model - assert len(self.components) > 0 - logger.info("Start sequential pipeline execution.") - for i, component in enumerate(self.components): - # print appropriate ordinal number representation (1st, 2nd, 3rd) for each step - ordinal = lambda n: "%d%s" % (n, "tsnrhtdd"[(n // 10 % 10 != 1) * (n % 10 < 4) * n % 10 :: 4]) - logger.info("The {} step being executing is {}.".format(ordinal(i), repr(component).upper())) - - component.model = model - if self._train_func is not None: - component.train_func = self._train_func - if self._eval_func is not None: - component.eval_func = self._eval_func - model = component() - - return model - - fit = __call__ - - def combine(self, *args): - """Combine neural_compressor components into a new component. - - Args: - args (Component): Components to be combined together. Input Component should be - supported in Neural Compressor and pass the sanity check during combine. The illegal combination - (e.g. Components uses different frameworks) returns an error. - - Returns: - Combined Component: The return component is created as base Component class. - It syncs input components configuration and creates dataloaders/functions - accordingly. - """ - assert len(args) > 1, "Combine requires at least 2 components. Please check your inputs." - # check if input components are good for combine - self._combination_sanity_check(*args) - # create component for the combination - combination = [] - for arg in args: - combination += [arg.__class__.__name__] if arg.combination is None else arg.combination - new_component = Component(combination=combination) - self._combine_components(*args, dist_component=new_component) - - return new_component - - def _combination_sanity_check(self, *args): - """Check sanity of the combination.""" - TEMP_SUPPORTED_COMPONENTS = ["Quantization", "Pruning", "Distillation"] - checked_components = [] - for component in args: - component_class = component.__class__.__name__ - if component_class in TEMP_SUPPORTED_COMPONENTS and component_class not in checked_components: - checked_components.append(component_class) - else: - logger.error("The combination of {} is not supported.".format(checked_components + [component_class])) - - def _combine_components(self, *args, dist_component=None): - """Actual implementation of combine(). - - It loops input component and sync-up status to distance component. - - Args: - args(tuple): input components. - dist_component(Component): the distance component for the combination - - Returns: None - """ - assert len(args) > 0, "Empty input detected in combine." - framework = args[0].framework - device = args[0].cfg.device - train_cfg = DotDict() - eval_cfg = DotDict() - tuning_cfg = DotDict() - model_cfg = DotDict() - quantization_cfg = DotDict() - has_distillation = False - for combine_component in args: - if isinstance(combine_component, Distillation): - has_distillation = True - - for combine_component in args: - # check if config is valid - assert combine_component.framework == framework, ( - "Combined components should have " - "same framework. Detect different frameworks: {} and {} are used.".format( - framework, combine_component.framework - ) - ) - assert ( - combine_component.cfg.device == device - ), "Combined components should have " "same device. Detect different device: {} and {} are used.".format( - device, combine_component.cfg.device - ) - - # sync configs - component_name = combine_component.__class__.__name__.lower() - component_cfg = getattr(combine_component.cfg, component_name, None) - assert combine_component is not None, "Please ensure field {} is configured " "in input yaml".format( - component_name - ) - - # in case of key train/evaluation not exist, return an empty DotDict - component_train_cfg = component_cfg.get("train", DotDict()) - - # TODO: Assumption here: train phase is defined inside component yaml field. - # But eval is defined at root yaml field. - component_eval_cfg = combine_component.cfg.get("evaluation", DotDict()) - component_tuning_cfg = combine_component.cfg.get("tuning", DotDict()) - component_model_cfg = combine_component.cfg.get("model", DotDict()) - component_quantization_cfg = ( - combine_component.cfg.get("quantization", DotDict()) if component_name == "quantization" else DotDict() - ) - - combine_component._model = self._model - if component_eval_cfg and component_train_cfg: - # create attributes if eval and train are defined in component config - combine_component.pre_process() - elif isinstance(combine_component, Pruning): - # if no eval/train configuration. Make minimal initialization - combine_component.generate_hooks() - combine_component.generate_pruners() - elif isinstance(combine_component, Distillation): - # if no eval/train configuration. Make minimal initialization - combine_component.create_criterion() - combine_component.create_optimizer() - combine_component.generate_hooks() - - # If distillation is combined, set training configs - if has_distillation: - if isinstance(combine_component, Distillation): - component_train_cfg.criterion = combine_component.train_cfg.criterion - component_train_cfg.optimizer = combine_component.train_cfg.optimizer - dist_component.criterion = combine_component.train_cfg.criterion - dist_component.on_after_compute_loss = combine_component.on_after_compute_loss - dist_component.teacher_model = combine_component.teacher_model - self._sync_config(train_cfg, component_train_cfg) - # Only sync train_cfg of distillation because criterion should include only one element - else: - component_train_cfg = DotDict() - - self._sync_config(train_cfg, component_train_cfg) - self._sync_config(eval_cfg, component_eval_cfg) - self._sync_config(tuning_cfg, component_tuning_cfg) - self._sync_config(model_cfg, component_model_cfg) - self._sync_config(quantization_cfg, component_quantization_cfg) - - # sync hooks - if combine_component.hooks is None: - continue - for k, v in combine_component.hooks.items(): - dist_component.register_hook(k, v) - - # sync to dist component - dist_component_cfg = DotDict() - if dist_component is not None: - deep_set(dist_component_cfg, "train", train_cfg) - deep_set(dist_component_cfg, "evaluation", eval_cfg) - deep_set(dist_component_cfg, "tuning", tuning_cfg) - deep_set(dist_component_cfg, "device", device) - deep_set(dist_component_cfg, "model", model_cfg) - deep_set(dist_component_cfg, "quantization", quantization_cfg) - dist_component._model = self._model - dist_component.framework = framework - dist_component.cfg = dist_component_cfg - - def _sync_config(self, dist_config, src_config): - """Sync the configuration between src and dist. - - It updates the missing keys in dist config from src config. - And check the value for same keys between src and dist. - - Args: - dist_config(DotDict): dist configuration to sync - src_config(DotDict): src configuration to sync - - Returns: None - """ - # sync the config if dist and src configs are not empty - if dist_config and src_config: - # update missing keys in dist_config - for key in src_config.keys() - dist_config.keys(): - dist_config[key] = src_config[key] - # check the same keys in dist and src whether is valid - for key in src_config.keys() & dist_config.keys(): - if isinstance(dist_config[key], dict) and isinstance(src_config[key], dict): - self._sync_config(dist_config[key], src_config[key]) - elif dist_config[key] != src_config[key]: - logger.warning( - "Find different value {} and {} on key {}.".format(dist_config[key], src_config[key], key) - + " Use first key-value ({}: {}) pair as default".format(key, dist_config[key]) - ) - # update src config to dist if dist is empty. - elif not dist_config and src_config: - dist_config.update(src_config) - - @property - def model(self): - """Getter of model. - - Returns: - The model used in the Scheduler process. - """ - return self._model - - @model.setter - def model(self, user_model): - """Set the user model and dispatch to framework specific internal model object. - - Args: - user_model: user are supported to set model from original framework model format - (eg, tensorflow frozen_pb or path to a saved model), - but not recommended. Best practice is to set from a initialized - neural_compressor.experimental.common.Model. - If tensorflow model is used, model's inputs/outputs will be - auto inferenced, but sometimes auto inferenced - inputs/outputs will not meet your requests, - set them manually in config yaml file. - Another corner case is slim model of tensorflow, - be careful of the name of model configured in yaml file, - make sure the name is in supported slim model list. - """ - if not isinstance(user_model, BaseModel): - logger.warning("Force convert framework model to neural_compressor model.") - self._model = NCModel(user_model) - else: - self._model = user_model - - @property - def train_func(self): - """Do not support get train_func.""" - assert False, "Should not try to get the value of `train_func` attribute." - return None - - @train_func.setter - def train_func(self, user_train_func): - """Training function. - - Args: - user_train_func: This function takes "model" as input parameter - and executes entire training process with self - contained training hyper-parameters. If training_func set, - an evaluation process must be triggered and user should - set eval_dataloader with metric configured or directly eval_func - to make evaluation of the model executed. training_func will return - a trained model. - """ - self._train_func = user_train_func - - @property - def eval_func(self): - """Do not support get eval_func.""" - assert False, "Should not try to get the value of `eval_func` attribute." - return None - - @eval_func.setter - def eval_func(self, user_eval_func): - """Evaluate function. - - Args: - user_eval_func: This function takes "model" as input parameter - and executes entire evaluation process with self - contained metrics. If eval_func set, - an evaluation process must be triggered - to make evaluation of the model executed. - """ - self._eval_func = user_eval_func diff --git a/neural_compressor/experimental/strategy/__init__.py b/neural_compressor/experimental/strategy/__init__.py deleted file mode 100644 index 4a9c0bd8253..00000000000 --- a/neural_compressor/experimental/strategy/__init__.py +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Intel Neural Compressor Strategy.""" - -from .strategy import EXP_STRATEGIES -from os.path import dirname, basename, isfile, join -import glob - -modules = glob.glob(join(dirname(__file__), "*.py")) - -for f in modules: - if isfile(f) and not f.startswith("__") and not f.endswith("__init__.py"): - __import__(basename(f)[:-3], globals(), locals(), level=1) - -__all__ = ["EXP_STRATEGIES"] diff --git a/neural_compressor/experimental/strategy/auto_mixed_precision.py b/neural_compressor/experimental/strategy/auto_mixed_precision.py deleted file mode 100644 index af60752d714..00000000000 --- a/neural_compressor/experimental/strategy/auto_mixed_precision.py +++ /dev/null @@ -1,169 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""The auto-mixed precision strategy.""" - -import copy -from collections import OrderedDict - -import numpy as np -from deprecated import deprecated - -from ...utils import logger -from .strategy import TuneStrategy, strategy_registry -from .utils.tuning_sampler import FallbackTuningSampler, OpTypeWiseTuningSampler -from .utils.tuning_structs import OpTuningConfig - - -@deprecated(version="2.0") -@strategy_registry -class AutoMixedPrecisionTuneStrategy(TuneStrategy): - """Tuning strategy for auto mixed precision.""" - - def next_tune_cfg(self): - """Generate the next tuning config. - - Tuning configurations are generated according to the following rules: - 1. First, it tries to convert all ops into target date type as many as possible. - 2. If the accuracy does not meets the requirements, it starts the stage of fallback - which converts ops into higher precision. - - Yields: - tune_config (dict): A dict containing the tuning configuration. - """ - from copy import deepcopy - - # filter quantization dtype - # TODO align with the old mixed-precison - target_dtypes = ( - self.cfg.graph_optimization.precisions - if self.cfg.graph_optimization - else self.cfg.mixed_precision.precisions - ) - target_dtypes = list(set(target_dtypes) - set(["fp32"])) - tuning_space = self.tuning_space - initial_op_tuning_cfg = {} - for item in tuning_space.root_item.options: - if item.item_type == "op": - op_name, op_type = item.name - initial_op_tuning_cfg[item.name] = OpTuningConfig(op_name, op_type, "fp32", tuning_space) - - if not target_dtypes: - target_dtypes = ["bf16"] - # step1. target_dtype AMAP, collect the ops that support target_dtype - bf16_items_name = [] - op_tuning_cfg = {} - for idx, target_dtype in enumerate(target_dtypes): - bf16_items = tuning_space.query_items_by_quant_mode(target_dtype) - if len(bf16_items) == 0 and not (idx == len(target_dtypes) - 1 and len(bf16_items_name) == 0): - continue - bf16_items_name = [item.name for item in bf16_items] - op_tuning_cfg = deepcopy(initial_op_tuning_cfg) - for op_name_type in bf16_items_name: - op_tuning_cfg[op_name_type] = OpTuningConfig( - op_name_type[0], op_name_type[1], target_dtype, tuning_space - ) - calib_sampling_size = 1 - op_tuning_cfg["calib_sampling_size"] = calib_sampling_size - yield op_tuning_cfg - - # step2. fallback - target_dtype = "fp32" - fallback_items_name_lst = bf16_items_name[::-1] - if fallback_items_name_lst: - logger.info(f"Start to fallback op to {target_dtype} one by one.") - self._fallback_started() - op_dtypes = OrderedDict(zip(fallback_items_name_lst, [target_dtype] * len(fallback_items_name_lst))) - initial_op_tuning_cfg = deepcopy(op_tuning_cfg) - fallback_sampler = FallbackTuningSampler( - tuning_space, - tuning_order_lst=[], - initial_op_tuning_cfg=initial_op_tuning_cfg, - op_dtypes=op_dtypes, - accumulate=False, - ) - op_fallback_acc_impact = OrderedDict() - for op_index, op_tuning_cfg in enumerate(fallback_sampler): - op_tuning_cfg["calib_sampling_size"] = calib_sampling_size - yield op_tuning_cfg - acc, _ = self.last_tune_result - op_fallback_acc_impact[fallback_items_name_lst[op_index]] = acc - - # do accumulated fallback according to the order in the previous stage - if len(op_fallback_acc_impact) > 0: - ordered_ops = sorted( - op_fallback_acc_impact.keys(), - key=lambda key: op_fallback_acc_impact[key], - reverse=self.higher_is_better, - ) - op_dtypes = OrderedDict(zip(ordered_ops, [target_dtype] * len(fallback_items_name_lst))) - logger.info("Start to accumulate fallback to {target_dtype}.") - initial_op_tuning_cfg = deepcopy(op_tuning_cfg) - fallback_sampler = FallbackTuningSampler( - tuning_space, - tuning_order_lst=[], - initial_op_tuning_cfg=initial_op_tuning_cfg, - op_dtypes=op_dtypes, - accumulate=True, - ) - for op_tuning_cfg in fallback_sampler: - op_tuning_cfg["calib_sampling_size"] = calib_sampling_size - yield op_tuning_cfg - - def traverse(self): - """Traverse the tuning space according to auto-mixed precision strategy.""" - # get fp32 model baseline - self._eval_baseline() - - trials_count = 0 - for op_tuning_cfg in self.next_tune_cfg(): - # add tune_cfg here as quantize use tune_cfg - tune_cfg = self._tune_cfg_converter(op_tuning_cfg) - trials_count += 1 - tuning_history = self._find_tuning_history(tune_cfg) - if tuning_history and trials_count < self.cfg.tuning.exit_policy.max_trials: - self.last_tune_result = tuning_history["last_tune_result"] - self.best_tune_result = tuning_history["best_tune_result"] - logger.warn("Find evaluated tuning config, skip.") - continue - - logger.debug("Dump current mixed precision configuration:") - logger.debug(tune_cfg) - self.last_qmodel = self.adaptor.quantize(tune_cfg, self.model, self.calib_dataloader, self.q_func) - assert self.last_qmodel - # Return the last quantized model as a result. if performance only. - if self.cfg.tuning.exit_policy.performance_only: - self.best_qmodel = self.last_qmodel - self._add_tuning_history(copy.deepcopy(tune_cfg), (-1, [0]), q_config=self.last_qmodel.q_config) - return - self.last_tune_cfg = copy.deepcopy(tune_cfg) - if self.eval_dataloader or self.eval_func: - q_config = copy.deepcopy(self.last_qmodel.q_config) - self.last_tune_result = self._evaluate(self.last_qmodel) - self.cur_best_acc, self.cur_best_tuning_cfg = self.update_best_op_tuning_cfg(op_tuning_cfg) - need_stop = self.stop(self.cfg.tuning.exit_policy.timeout, trials_count) - # record the tuning history - saved_tune_cfg = copy.deepcopy(tune_cfg) - saved_last_tune_result = copy.deepcopy(self.last_tune_result) - self._add_tuning_history(saved_tune_cfg, saved_last_tune_result, q_config=q_config) - else: - # If the eval_dataloader was not specified under the config yaml file, - # We only converted the model with customized precisions. - self.best_qmodel = self.last_qmodel - need_stop = True - - if need_stop: - break diff --git a/neural_compressor/experimental/strategy/basic.py b/neural_compressor/experimental/strategy/basic.py deleted file mode 100644 index 88b291d08a2..00000000000 --- a/neural_compressor/experimental/strategy/basic.py +++ /dev/null @@ -1,184 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""The basic tuning strategy.""" -from collections import OrderedDict -from copy import deepcopy - -from deprecated import deprecated - -from ...utils import logger -from .strategy import TuneStrategy, strategy_registry -from .utils.constant import TUNING_ITEMS_LST -from .utils.tuning_sampler import FallbackTuningSampler, OpTypeWiseTuningSampler -from .utils.tuning_structs import OpTuningConfig - - -@deprecated(version="2.0") -@strategy_registry -class BasicTuneStrategy(TuneStrategy): - """The basic tuning strategy. - - There are three stages executed by Basic strategy sequentially, - and the tuning process ends once the condition meets the exit policy. - """ - - def next_tune_cfg(self): - """Generate and yield the next tuning config with below order. - - 1. OP Type Wise Tuning: tries to quantize the OPs as many as possible - and traverse all OP type wise tuning configs - 2. Fallback OP One by One: it performs high-precision OP (FP32, BF16 ...) - fallbacks one by one based on the tuning config with the best result - in the previous stage, and records the impact of each OP. - 3. Fallback Multiple OPs Accumulated: first sorted the OPs list - according to the impact score in stage II, and tries to incrementally - fallback multiple OPs to high precision according to the sorted OP list. - - Returns: - tune_config (dict): A dict containing the tuning configuration for quantization. - """ - tuning_space = self.tuning_space - calib_sampling_size_lst = tuning_space.root_item.get_option_by_name("calib_sampling_size").options - for calib_sampling_size in calib_sampling_size_lst: - # Initialize the tuning config for each op according to the quantization approach. - op_item_dtype_dict, quant_mode_wise_items, initial_op_tuning_cfg = self.initial_tuning_cfg() - # Optype-wise tuning tuning items: the algorithm/scheme/granularity of activation(weight) - early_stop_tuning = False - stage1_cnt = 0 - quant_ops = quant_mode_wise_items.get("static", []) - quant_ops += quant_mode_wise_items.get("dynamic", []) - stage1_max = 1e9 # TODO set a more appropriate value - op_type_wise_tuning_sampler = OpTypeWiseTuningSampler( - tuning_space, [], [], op_item_dtype_dict, initial_op_tuning_cfg - ) - for index, op_tuning_cfg in enumerate(op_type_wise_tuning_sampler): - logger.debug(f"[OP TYPE WISE STAGE], Trial {index + 1}") - op_tuning_cfg["calib_sampling_size"] = calib_sampling_size - # Apply all recipes, if not got the qmodel that meet the requirements, discard it. - if index == 1 and not self.applied_all_recipes_flag: - logger.info("Apply all recipes.") - self.applied_all_recipes_flag = True - yield self.apply_all_tuning_recipes(deepcopy(self.cur_best_tuning_cfg)) - stage1_cnt += 1 - if early_stop_tuning and stage1_cnt > stage1_max: - logger.info("Early stopping the stage 1.") - break - yield op_tuning_cfg - - # Apply all recipes, if not got the qmodel that meet the requirements, discard it. - if stage1_cnt == 1 and not self.applied_all_recipes_flag: - logger.info("Apply all recipes.") - self.applied_all_recipes_flag = True - yield self.apply_all_tuning_recipes(deepcopy(self.cur_best_tuning_cfg)) - - # Fallback the ops supported both static and dynamic from static to dynamic - # Tuning items: None - if self.cfg.quantization.approach == "post_training_auto_quant": - static_dynamic_items = [ - item - for item in tuning_space.query_items_by_quant_mode("static") - if item in tuning_space.query_items_by_quant_mode("dynamic") - ] - if static_dynamic_items: - logger.info("Fallback all ops that support both dynamic and static to dynamic.") - else: - logger.info("Non ops that support both dynamic") - - new_op_tuning_cfg = deepcopy(self.cur_best_tuning_cfg) - for item in static_dynamic_items: - new_op_tuning_cfg[item.name] = self._initial_dynamic_cfg_based_on_static_cfg( - new_op_tuning_cfg[item.name] - ) - new_op_tuning_cfg["calib_sampling_size"] = calib_sampling_size - yield new_op_tuning_cfg - - logger.info("Apply recipe one by one.") - for tune_cfg in self.apply_recipe_one_by_one(deepcopy(self.cur_best_tuning_cfg)): - yield tune_cfg - best_op_tuning_cfg_stage1 = deepcopy(self.cur_best_tuning_cfg) - - # Fallback - for target_dtype in ["bf16", "fp32"]: - target_type_lst = set(tuning_space.query_items_by_quant_mode(target_dtype)) - fallback_items_lst = [item for item in quant_ops if item in target_type_lst] - if fallback_items_lst: - logger.info(f"Start to fallback op to {target_dtype} one by one.") - self._fallback_started() - fallback_items_name_lst = [item.name for item in fallback_items_lst][::-1] # from bottom to up - op_dtypes = OrderedDict(zip(fallback_items_name_lst, [target_dtype] * len(fallback_items_name_lst))) - initial_op_tuning_cfg = deepcopy(best_op_tuning_cfg_stage1) - fallback_sampler = FallbackTuningSampler( - tuning_space, - tuning_order_lst=[], - initial_op_tuning_cfg=initial_op_tuning_cfg, - op_dtypes=op_dtypes, - accumulate=False, - ) - op_fallback_acc_impact = OrderedDict() - for op_index, op_tuning_cfg in enumerate(fallback_sampler): - op_tuning_cfg["calib_sampling_size"] = calib_sampling_size - yield op_tuning_cfg - acc, _ = self.last_tune_result - op_fallback_acc_impact[fallback_items_name_lst[op_index]] = acc - - # Fallback OPs accumulated according to the order in the previous stage - if len(op_fallback_acc_impact) > 0: - ordered_ops = sorted( - op_fallback_acc_impact.keys(), - key=lambda key: op_fallback_acc_impact[key], - reverse=self.higher_is_better, - ) - op_dtypes = OrderedDict(zip(ordered_ops, [target_dtype] * len(fallback_items_name_lst))) - logger.info(f"Start to accumulate fallback to {target_dtype}.") - initial_op_tuning_cfg = deepcopy(best_op_tuning_cfg_stage1) - fallback_sampler = FallbackTuningSampler( - tuning_space, - tuning_order_lst=[], - initial_op_tuning_cfg=initial_op_tuning_cfg, - op_dtypes=op_dtypes, - accumulate=True, - ) - for op_tuning_cfg in fallback_sampler: - op_tuning_cfg["calib_sampling_size"] = calib_sampling_size - yield op_tuning_cfg - - def _initial_dynamic_cfg_based_on_static_cfg(self, op_static_cfg: OpTuningConfig): - op_state = op_static_cfg.get_state() - op_name = op_static_cfg.op_name - op_type = op_static_cfg.op_type - op_name_type = (op_name, op_type) - op_quant_mode = "dynamic" - tuning_space = self.tuning_space - dynamic_state = {} - for att in ["weight", "activation"]: - if att not in op_state: - continue - # Add dtype - full_path = self.tuning_space.get_op_default_path_by_pattern(op_name_type, op_quant_mode) - dynamic_state[att + "_dtype"] = self.tuning_space.ops_data_type[op_name_type][full_path[att]] - for method_name, method_val in op_state[att].items(): - att_and_method_name = (att, method_name) - if att_and_method_name not in TUNING_ITEMS_LST: - continue - if tuning_space.query_item_option(op_name_type, full_path[att], att_and_method_name, method_val): - dynamic_state[att_and_method_name] = method_val - else: - quant_mode_item = tuning_space.get_item_by_path((op_name_type, *full_path[att])) - if quant_mode_item and quant_mode_item.get_option_by_name(att_and_method_name): - tuning_item = quant_mode_item.get_option_by_name(att_and_method_name) - dynamic_state[att_and_method_name] = tuning_item.options[0] if tuning_item else None - return OpTuningConfig(op_name, op_type, op_quant_mode, tuning_space, kwargs=dynamic_state) diff --git a/neural_compressor/experimental/strategy/bayesian.py b/neural_compressor/experimental/strategy/bayesian.py deleted file mode 100644 index 58c7176c301..00000000000 --- a/neural_compressor/experimental/strategy/bayesian.py +++ /dev/null @@ -1,432 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""The Bayesian tuning strategy.""" - -import warnings -from copy import deepcopy - -import numpy as np -from deprecated import deprecated -from scipy.optimize import minimize -from sklearn.gaussian_process import GaussianProcessRegressor -from sklearn.gaussian_process.kernels import Matern - -from ...utils import logger -from .strategy import TuneStrategy, strategy_registry -from .utils.tuning_sampler import OpWiseTuningSampler - - -@deprecated(version="2.0") -@strategy_registry -class BayesianTuneStrategy(TuneStrategy): - """The Bayesian tuning strategy.""" - - def __init__( - self, model, conf, q_dataloader, q_func=None, eval_dataloader=None, eval_func=None, dicts=None, q_hooks=None - ): - """Init the BaySian tuning strategy.""" - super().__init__(model, conf, q_dataloader, q_func, eval_dataloader, eval_func, dicts, q_hooks) - self.bayes_opt = None - - def __getstate__(self): - """Magic method for pickle saving. - - Returns: - dict: Saved dict for resuming - """ - for history in self.tuning_history: - if self._same_yaml(history["cfg"], self.cfg): - history["bayes_opt"] = self.bayes_opt - save_dict = super().__getstate__() - return save_dict - - def _params_to_tune_configs(self, params): - op_tuning_cfg = {} - calib_sampling_size_lst = self.tuning_space.root_item.get_option_by_name("calib_sampling_size").options - for op_name_type, configs in self.op_configs.items(): - if len(configs) == 1: - op_tuning_cfg[op_name_type] = configs[0] - else: - op_tuning_cfg[op_name_type] = configs[min(len(configs) - 1, int(params[op_name_type[0]]))] - if len(calib_sampling_size_lst) > 1: - calib_sampling_size = calib_sampling_size_lst[min(len(configs) - 1, int(params["calib_sampling_size"]))] - else: - calib_sampling_size = calib_sampling_size_lst[0] - op_tuning_cfg["calib_sampling_size"] = calib_sampling_size - return op_tuning_cfg - - def next_tune_cfg(self): - """Generate the next tuning config according to bayesian search algorithm. - - This strategy comes from the Bayesian optimization package and changed it to a discrete version. - It uses Gaussian processes to define the prior/posterior distribution over the black-box - function with the tuning history and then finds the tuning configuration that maximizes - the expected improvement. - - Returns: - tune_config (dict): A dict containing the tuning configuration for quantization. - """ - params = None - pbounds = {} - tuning_space = self.tuning_space - calib_sampling_size_lst = tuning_space.root_item.get_option_by_name("calib_sampling_size").options - op_item_dtype_dict, quant_mode_wise_items, initial_op_tuning_cfg = self.initial_tuning_cfg() - op_wise_pool = OpWiseTuningSampler(tuning_space, [], [], op_item_dtype_dict, initial_op_tuning_cfg) - self.op_configs = op_wise_pool.get_opwise_candidate() - - for op_name_type, configs in self.op_configs.items(): - if len(configs) > 1: - pbounds[op_name_type[0]] = (0, len(configs)) - if len(calib_sampling_size_lst) > 1: - pbounds["calib_sampling_size"] = (0, len(calib_sampling_size_lst)) - if len(pbounds) == 0: - yield self._params_to_tune_configs(params) - return - if self.bayes_opt is None: - self.bayes_opt = BayesianOptimization(pbounds=pbounds, random_seed=self.cfg.tuning.random_seed) - while True: - params = self.bayes_opt.gen_next_params() - logger.debug("Dump current bayesian params:") - logger.debug(params) - yield self._params_to_tune_configs(params) - try: - self.bayes_opt._space.register(params, self.last_tune_result[0]) - except KeyError: - logger.debug("Find registered params, skip it.") - pass - - -# Util part -# Bayesian opt acq function - - -@deprecated(version="2.0") -def acq_max(ac, gp, y_max, bounds, random_seed, n_warmup=10000, n_iter=10): - """Find the maximum of the acquisition function parameters. - - Args: - ac: The acquisition function object that return its point-wise value. - gp: A gaussian process fitted to the relevant data. - y_max: The current maximum known value of the target function. - bounds: The variables bounds to limit the search of the acq max. - random_seed: instance of np.RandomState random number generator - n_warmup: number of times to randomly sample the acquisition function - n_iter: number of times to run scipy.minimize - - Returns: - x_max: The arg max of the acquisition function. - """ - # Warm up with random points - x_tries = np.random.uniform(bounds[:, 0], bounds[:, 1], size=(n_warmup, bounds.shape[0])) - ys = ac(x_tries, gp=gp, y_max=y_max) - x_max = x_tries[ys.argmax()] - max_acq = ys.max() - - # Explore the parameter space more thoroughly - x_seeds = np.random.uniform(bounds[:, 0], bounds[:, 1], size=(n_iter, bounds.shape[0])) - for x_try in x_seeds: - # Find the minimum of minus the acquisition function - res = minimize( - lambda x: -ac(x.reshape(1, -1), gp=gp, y_max=y_max), x_try.flatten(), bounds=bounds, method="L-BFGS-B" - ) - - # See if success - if not res.success: - continue - - if isinstance(res.fun, float): - res.fun = np.array([res.fun]) - # Store it if better than previous minimum(maximum). - if max_acq is None or -res.fun[0] >= max_acq: - x_max = res.x - max_acq = -res.fun[0] - - # Clip output to make sure it lies within the bounds. Due to floating - # point technicalities this is not always the case. - return np.clip(x_max, bounds[:, 0], bounds[:, 1]) - - -@deprecated(version="2.0") -def _hashable(x): - """Ensure that an point is hashable by a python dict.""" - return tuple(map(float, x)) - - -# Target space part -@deprecated(version="2.0") -class TargetSpace(object): - """Holds the param-space coordinates (X) and target values (Y). - - Allows for constant-time appends while ensuring no duplicates are added. - """ - - def __init__(self, pbounds, random_seed=9527): - """Construct a TargetSpace. - - Args: - target_func (function): Function to be maximized. - pbounds (dict): Dictionary with parameters names as keys and a tuple with minimum and maximum values. - random_seed (int): Optionally specify a seed for a random number generator - """ - self.random_seed = random_seed - # Get the name of the parameters - names = list(pbounds.keys()) - self._keys = deepcopy(names) - # Create an array with parameters bounds - self._bounds = np.array([pbounds[name] for name in names], dtype=np.float32) - - # preallocated memory for X and Y points - self._params = np.empty(shape=(0, self.dim)) - self._target = np.empty(shape=(0)) - - # keep track of unique points we have seen so far - self._cache = {} - - def __contains__(self, x): - """Check if param x is cached in this space.""" - return _hashable(x) in self._cache - - def __len__(self): - """Get the total count of stored items.""" - assert len(self._params) == len(self._target) - return len(self._target) - - @property - def empty(self): - """Check if the space is empty.""" - return len(self) == 0 - - @property - def params(self): - """Get all params stored in this space.""" - return self._params - - @property - def target(self): - """Get all target values in this space.""" - return self._target - - @property - def dim(self): - """Get the dimension of this space.""" - return len(self._keys) - - @property - def keys(self): - """Get all keys of this space.""" - return self._keys - - @property - def bounds(self): - """Get the bounds of this space.""" - return self._bounds - - def params_to_array(self, params): - """Generate an array from params. - - Args: - params (Dict): The dict contains keys in `self.keys`, and - corresponding param. - - Returns: - np.array: An array contains all params. - """ - try: - assert set(params) == set(self.keys) - except AssertionError: - raise ValueError( - "Parameters' keys ({}) do ".format(list(params.keys())) - + "not match the expected set of keys ({}).".format(self.keys) - ) - return np.asarray([params[key] for key in self.keys]) - - def array_to_params(self, x): - """Generate an params' dict from array. - - Args: - x (np.array): The array contains all params. - - Returns: - dict: the dict contains keys and the params corresponding to it. - """ - try: - assert len(x) == len(self.keys) - except AssertionError: - raise ValueError( - "Size of array ({}) is different than the ".format(len(x)) - + "expected number of parameters ({}).".format(len(self.keys)) - ) - return dict(zip(self.keys, x)) - - def _as_array(self, x): - try: - x = np.asarray(x, dtype=float) - except TypeError: - x = self.params_to_array(x) - - x = x.ravel() - try: - assert x.size == self.dim - except AssertionError: - raise ValueError( - "Size of array ({}) is different than the ".format(len(x)) - + "expected number of parameters ({}).".format(len(self.keys)) - ) - return x - - def register(self, params, target): - """Append a point and its target value to the known data. - - Runs in amortized constant time. - - Args: - params (ndarray): a single point, with len(params) == self.dim - target (float): target function value - - Raises: - KeyError: if the point is not unique - """ - x = self._as_array(params) - if x in self: - raise KeyError("Params point {} is not unique".format(x)) - - # Insert data into unique dictionary - self._cache[_hashable(x.ravel())] = target - - self._params = np.concatenate([self._params, x.reshape(1, -1)]) - self._target = np.concatenate([self._target, [target]]) - - def get_target(self, params): - """Get the target value of params. - - Args: - params (ndarray): a single point, with len(params) == self.dim - - Returns: - target (float): target function value. - """ - x = self._as_array(params) - target = self._cache[_hashable(x)] - return target - - def random_sample(self): - """Create random points within the bounds of the space. - - Returns: - data (ndarray): [num x dim] array points with dimensions corresponding to `self._keys` - """ - # TODO: support integer, category, and basic scipy.optimize constraints - data = np.empty((1, self.dim)) - for col, (lower, upper) in enumerate(self._bounds): - data.T[col] = np.random.uniform(lower, upper, size=1) # pylint: disable=unsupported-assignment-operation - return data.ravel() - - def max(self): - """Get maximum target value found and corresponding parameters.""" - try: - res = {"target": self.target.max(), "params": dict(zip(self.keys, self.params[self.target.argmax()]))} - except ValueError: - res = {} - return res - - def res(self): - """Get all target values found and corresponding parameters.""" - params = [dict(zip(self.keys, p)) for p in self.params] - - return [{"target": target, "params": param} for target, param in zip(self.target, params)] - - -# Tuning part -@deprecated(version="2.0") -class BayesianOptimization: - """The class for bayesian optimization. - - This class takes the parameters bounds in order to find which values for - the parameters yield the maximum value using bayesian optimization. - """ - - def __init__(self, pbounds, random_seed=9527, verbose=2): - """Init bayesian optimization. - - Args: - pbounds (dict): Dictionary with parameters names as keys and a tuple with - minimum and maximum values. - random_seed (int, optional): The seed for random searching. Default to 9527. - verbose (int, optional): The level of verbosity. Default to 2. - """ - self._random_seed = random_seed - # Data structure containing the bounds of its domain, - # and a record of the points we have evaluated. - self._space = TargetSpace(pbounds, random_seed) - - # Internal GP regressor - self._gp = GaussianProcessRegressor( - kernel=Matern(nu=2.5), - alpha=1e-6, - normalize_y=True, - n_restarts_optimizer=5, - random_state=self._random_seed, - ) - self._verbose = verbose - - @property - def space(self): - """Get the target space.""" - return self._space - - @property - def max(self): - """Get the maximum value of target space.""" - return self._space.max() - - @property - def res(self): - """Get the minimum value of target space.""" - return self._space.res() - - @staticmethod - def _ucb(x, gp, y_max, kappa=2.576): - with warnings.catch_warnings(): - warnings.simplefilter("ignore") - mean, std = gp.predict(x, return_std=True) - return mean + kappa * std - - def suggest(self): - """Suggest the most promising points.""" - if len(set(self._space.target)) < 2: - return self._space.array_to_params(self._space.random_sample()) - - # Sklearn's GP throws a large number of warnings at times, but - # we don't really need to see them here. - with warnings.catch_warnings(): - warnings.simplefilter("ignore") - self._gp.fit(self._space.params, self._space.target) - - # Finding argmax of the acquisition function. - suggestion = acq_max( - ac=self._ucb, - gp=self._gp, - y_max=self._space.target.max(), - bounds=self._space.bounds, - random_seed=self._random_seed, - ) - return self._space.array_to_params(suggestion) - - def gen_next_params(self): - """Get the next parameter.""" - next_params = self.suggest() - return next_params diff --git a/neural_compressor/experimental/strategy/exhaustive.py b/neural_compressor/experimental/strategy/exhaustive.py deleted file mode 100644 index 9a5f78d46be..00000000000 --- a/neural_compressor/experimental/strategy/exhaustive.py +++ /dev/null @@ -1,50 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""The exhaustive tuning strategy.""" -from deprecated import deprecated - -from .strategy import TuneStrategy, strategy_registry -from .utils.tuning_sampler import OpWiseTuningSampler - - -@deprecated(version="2.0") -@strategy_registry -class ExhaustiveTuneStrategy(TuneStrategy): - """The exhaustive tuning strategy.""" - - def next_tune_cfg(self): - """Generate and yield the next tuning config using exhaustive search in tuning space. - - It sequentially traverse all possible quantization tuning configurations - in a tuning space. From the perspective of the impact on performance, - we currently only traverse all possible quantization tuning configs. - Same reason as Bayesian, fallback datatypes are not included for now. - - Returns: - tune_config (dict): A dict containing the tuning configuration for quantization. - """ - tuning_space = self.tuning_space - calib_sampling_size_lst = tuning_space.root_item.get_option_by_name("calib_sampling_size").options - for calib_sampling_size in calib_sampling_size_lst: - op_item_dtype_dict, quant_mode_wise_items, initial_op_tuning_cfg = self.initial_tuning_cfg() - op_wise_tuning_sampler = OpWiseTuningSampler( - tuning_space, [], [], op_item_dtype_dict, initial_op_tuning_cfg - ) - for op_tuning_cfg in op_wise_tuning_sampler: - op_tuning_cfg["calib_sampling_size"] = calib_sampling_size - yield op_tuning_cfg - return diff --git a/neural_compressor/experimental/strategy/mse.py b/neural_compressor/experimental/strategy/mse.py deleted file mode 100644 index 31a4363d840..00000000000 --- a/neural_compressor/experimental/strategy/mse.py +++ /dev/null @@ -1,226 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""MSE tuning strategy.""" -from collections import OrderedDict -from copy import deepcopy -from typing import List - -import numpy as np -from deprecated import deprecated - -from ...utils import logger -from .strategy import TuneStrategy, strategy_registry -from .utils.tuning_sampler import FallbackTuningSampler, OpTypeWiseTuningSampler -from .utils.tuning_structs import OpTuningConfig - - -@deprecated(version="2.0") -@strategy_registry -class MSETuneStrategy(TuneStrategy): - """The tuning strategy using MSE policy in tuning space. - - The MSE strategy needs to get the tensors for each OP of raw FP32 models and the quantized model based on - the best model-wise tuning configuration. It then calculates the MSE (Mean Squared Error) for each OP, sorts - those OPs according to the MSE value, and performs the op-wise fallback in this order. - """ - - def __init__( - self, model, conf, q_dataloader, q_func=None, eval_dataloader=None, eval_func=None, dicts=None, q_hooks=None - ): - """Init an mse tuning strategy.""" - super().__init__(model, conf, q_dataloader, q_func, eval_dataloader, eval_func, dicts, q_hooks) - self.ordered_ops = None - - def __getstate__(self): - """Magic method for pickle saving. - - Returns: - save_dict: Saved dict for resuming - """ - for history in self.tuning_history: - if self._same_yaml(history["cfg"], self.cfg): - history["ordered_ops"] = self.ordered_ops - save_dict = super().__getstate__() - return save_dict - - def _mse_metric_gap(self, fp32_tensor, dequantize_tensor): - """Calculate the euclidean distance between fp32 tensor and int8 dequantize tensor. - - Args: - fp32_tensor (tensor): The FP32 tensor. - dequantize_tensor (tensor): The INT8 dequantize tensor. - """ - fp32_max = np.max(fp32_tensor) - fp32_min = np.min(fp32_tensor) - dequantize_max = np.max(dequantize_tensor) - dequantize_min = np.min(dequantize_tensor) - fp32_tensor = (fp32_tensor - fp32_min) / (fp32_max - fp32_min) - dequantize_tensor = (dequantize_tensor - dequantize_min) / (dequantize_max - dequantize_min) - diff_tensor = fp32_tensor - dequantize_tensor - euclidean_dist = np.sum(diff_tensor**2) - return euclidean_dist / fp32_tensor.size - - def mse_impact_lst(self, op_list: List, fp32_model, best_qmodel): - """Calculate and generate the MSE impact list. - - Args: - op_list (List[Tuple(str, str)]): List of ops in format of [(op_name, op_type), ...]. - fp32_model (Model): The original FP32 model before quantization. - current_best_model (Model): The currently best quantized model. - - Returns: - ordered_op_name_types (List[Tuple(str, str)]): The sorted list of ops by its MSE - impaction, in the same format of 'op_list'. - """ - op_name_lst = [element[0] for element in op_list] - op_mapping = {} - for op_name, op_type in list(op_list): - op_mapping[op_name] = (op_name, op_type) - current_best_tune_cfg = self._tune_cfg_converter(self.cur_best_tuning_cfg) - fp32_dump_content = self.adaptor.inspect_tensor( - fp32_model, - self.calib_dataloader, - op_name_lst, - [1], - inspect_type="activation", - save_to_disk=True, - save_path="./nc_workspace/", - quantization_cfg=current_best_tune_cfg, - ) - fp32_tensor_dict = fp32_dump_content["activation"][0] - best_qmodel = self.adaptor.quantize(current_best_tune_cfg, self.model, self.calib_dataloader, self.q_func) - quant_dump_content = self.adaptor.inspect_tensor( - best_qmodel, - self.calib_dataloader, - op_name_lst, - [1], - inspect_type="activation", - save_to_disk=True, - save_path="./nc_workspace/", - quantization_cfg=current_best_tune_cfg, - ) - dequantize_tensor_dict = quant_dump_content["activation"][0] - ops_mse = { - op: self._mse_metric_gap( - list(fp32_tensor_dict[op].values())[0], list(dequantize_tensor_dict[op].values())[0] - ) - for op in fp32_tensor_dict - } - ordered_op_names = sorted(ops_mse.keys(), key=lambda key: ops_mse[key], reverse=self.higher_is_better) - - ordered_op_name_types = [op_mapping[name] for name in ordered_op_names] - return ordered_op_name_types - - def next_tune_cfg(self): - """Generate and yield the next tuning config. - - Returns: - tune_config (dict): A dict containing the tuning configuration for quantization. - """ - tuning_space = self.tuning_space - calib_sampling_size_lst = tuning_space.root_item.get_option_by_name("calib_sampling_size").options - for calib_sampling_size in calib_sampling_size_lst: - op_item_dtype_dict, quant_mode_wise_items, initial_op_tuning_cfg = self.initial_tuning_cfg() - # Optype-wise tuning - early_stop_tuning = True - stage1_cnt = 0 - int8_ops = quant_mode_wise_items["static"] if "static" in quant_mode_wise_items else [] - int8_ops += quant_mode_wise_items["dynamic"] if "dynamic" in quant_mode_wise_items else [] - stage1_max = min(5, len(int8_ops)) # TODO set a more appropriate value - op_wise_tuning_sampler = OpTypeWiseTuningSampler( - tuning_space, [], [], op_item_dtype_dict, initial_op_tuning_cfg - ) - for op_tuning_cfg in op_wise_tuning_sampler: - stage1_cnt += 1 - if early_stop_tuning and stage1_cnt > stage1_max: - logger.info("Early stopping the stage 1.") - break - op_tuning_cfg["calib_sampling_size"] = calib_sampling_size - yield op_tuning_cfg - - # Fallback the ops supported both static and dynamic from static to dynamic - static_dynamic_items = [ - item - for item in tuning_space.query_items_by_quant_mode("static") - if item in tuning_space.query_items_by_quant_mode("dynamic") - ] - if static_dynamic_items: - logger.info("Fallback all ops that support both dynamic and static to dynamic.") - else: - logger.info("No op support both dynamic and static") - - def dynamic_op_tuning_cfg_from_static(op_tuning_cfg: OpTuningConfig): - new_op_tuning_cfg = deepcopy(op_tuning_cfg) - new_op_tuning_cfg.op_quant_mode = "dynamic" - return new_op_tuning_cfg - - new_op_tuning_cfg = deepcopy(self.cur_best_tuning_cfg) - for item in static_dynamic_items: - new_op_tuning_cfg[item.name] = dynamic_op_tuning_cfg_from_static(new_op_tuning_cfg[item.name]) - new_op_tuning_cfg["calib_sampling_size"] = calib_sampling_size - yield new_op_tuning_cfg - - best_op_tuning_cfg_stage1 = deepcopy(self.cur_best_tuning_cfg) - - # Fallback to float point datatypes ('bf16' or 'fp32') - for target_dtype in ["bf16", "fp32"]: - fallback_items_lst = [ - item for item in int8_ops if item in tuning_space.query_items_by_quant_mode(target_dtype) - ] - if fallback_items_lst: - logger.info(f"Start to fallback op to {target_dtype} one by one.") - # Replace it with sorted items list - fallback_items_name_lst = [item.name for item in fallback_items_lst] - # TODO check the best_qmodel - ordered_op_name_types = self.mse_impact_lst(fallback_items_name_lst, self.model, self.best_qmodel) - self.ordered_ops = [op_name for (op_name, op_type) in ordered_op_name_types] - op_dtypes = OrderedDict(zip(ordered_op_name_types, [target_dtype] * len(fallback_items_name_lst))) - initial_op_tuning_cfg = deepcopy(best_op_tuning_cfg_stage1) - fallback_sampler = FallbackTuningSampler( - tuning_space, - tuning_order_lst=[], - initial_op_tuning_cfg=initial_op_tuning_cfg, - op_dtypes=op_dtypes, - accumulate=False, - ) - op_fallback_acc_impact = OrderedDict() - for op_index, op_tuning_cfg in enumerate(fallback_sampler): - op_tuning_cfg["calib_sampling_size"] = calib_sampling_size - yield op_tuning_cfg - acc, _ = self.last_tune_result - op_fallback_acc_impact[fallback_items_name_lst[op_index]] = acc - - # Do accumulated fallback according to the order in the previous stage - if len(op_fallback_acc_impact) > 0: - ordered_ops = sorted( - op_fallback_acc_impact.keys(), - key=lambda key: op_fallback_acc_impact[key], - reverse=self.higher_is_better, - ) - op_dtypes = OrderedDict(zip(ordered_ops, [target_dtype] * len(fallback_items_name_lst))) - logger.info(f"Start to accumulate fallback to {target_dtype}.") - initial_op_tuning_cfg = deepcopy(best_op_tuning_cfg_stage1) - fallback_sampler = FallbackTuningSampler( - tuning_space, - tuning_order_lst=[], - initial_op_tuning_cfg=initial_op_tuning_cfg, - op_dtypes=op_dtypes, - accumulate=True, - ) - for op_tuning_cfg in fallback_sampler: - op_tuning_cfg["calib_sampling_size"] = calib_sampling_size - yield op_tuning_cfg diff --git a/neural_compressor/experimental/strategy/mse_v2.py b/neural_compressor/experimental/strategy/mse_v2.py deleted file mode 100644 index e78d159ed8e..00000000000 --- a/neural_compressor/experimental/strategy/mse_v2.py +++ /dev/null @@ -1,225 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""The MSE_V2 tuning strategy.""" -from collections import OrderedDict -from copy import deepcopy -from time import time - -from deprecated import deprecated - -from ...utils import logger -from .strategy import TuneStrategy, strategy_registry -from .utils.tuning_sampler import OpTypeWiseTuningSampler -from .utils.tuning_structs import OpTuningConfig - - -@deprecated(version="2.0") -@strategy_registry -class MSE_V2TuneStrategy(TuneStrategy): - """The `mse_v2` tuning strategy. - - MSE_v2 is a strategy with a two stages fallback and revert fallback. - Note that, only tensorflow framework and pytorch FX backend is currently supported for mse_v2 - tuning strategy. - """ - - def _tuning_record_msg(self, records): - records_str_lst = [[str(e) for e in record] for record in records] - record_msg = "\n".join(",".join(record) for record in records_str_lst) - return record_msg - - def next_tune_cfg(self): - """Generate and yield the next tuning config with below order. - - 1. In the fallback stage, it uses multi-batch data to score the op impact - and then fallback the op with the highest score util found the quantized model - that meets accuracy criteria. - 2. In the revert fallback stage, it also scores - the impact of fallback OPs in the previous stage and selects the op - with the lowest score to revert the fallback until the quantized model - that does not meets accuracy criteria. - - Returns: - tune_config (dict): A dict containing the tuning configuration for quantization. - """ - best_op_tuning_cfg = None - if len(self.metric_name) == 1 or self.metric_weight is not None: - best_acc = float("-inf") if self.higher_is_better else float("inf") - else: - best_acc = [ - float("-inf") if higher_is_better else float("inf") for higher_is_better in self.metric_criterion - ] - - tuning_space = self.tuning_space - initial_op_tuning_cfg = {} - for item in tuning_space.root_item.options: - if item.item_type == "op": - op_name, op_type = item.name - initial_op_tuning_cfg[item.name] = OpTuningConfig(op_name, op_type, "fp32", tuning_space) - calib_sampling_size_lst = tuning_space.root_item.get_option_by_name("calib_sampling_size").options - for calib_sampling_size in calib_sampling_size_lst: - # Collect the ops that support static and dynamic - quant_mode_wise_items = OrderedDict() - query_order = ["static", "dynamic", "bf16", "fp16", "fp32"] - pre_items = set() - for quant_mode in query_order: - items = tuning_space.query_items_by_quant_mode(quant_mode) - filtered_items = [item for item in items if item not in pre_items] - pre_items = pre_items.union(set(items)) - quant_mode_wise_items[quant_mode] = filtered_items - - def initial_op_quant_mode(items_lst, target_quant_mode, op_item_dtype_dict): - for item in items_lst: - op_item_dtype_dict[item.name] = target_quant_mode - - op_item_dtype_dict = OrderedDict() - for quant_mode, quant_mode_items in quant_mode_wise_items.items(): - initial_op_quant_mode(quant_mode_items, quant_mode, op_item_dtype_dict) - - # Optype-wise tuning - early_stop_tuning = True - stage1_cnt = 0 - int8_ops = quant_mode_wise_items["dynamic"] + quant_mode_wise_items["static"] - stage1_max = 2 # TODO set a more appropriate value - op_wise_tuning_sampler = OpTypeWiseTuningSampler( - tuning_space, [], [], op_item_dtype_dict, initial_op_tuning_cfg - ) - for op_tuning_cfg in op_wise_tuning_sampler: - stage1_cnt += 1 - if early_stop_tuning and stage1_cnt > stage1_max: - logger.info("Early stopping the stage 1.") - break - op_tuning_cfg["calib_sampling_size"] = calib_sampling_size - yield op_tuning_cfg - - # Fallback the ops supported both static and dynamic from static to dynamic - static_dynamic_items = [ - item - for item in tuning_space.query_items_by_quant_mode("static") - if item in tuning_space.query_items_by_quant_mode("dynamic") - ] - if static_dynamic_items: - logger.info("Fallback all ops that support both dynamic and static to dynamic.") - else: - logger.info("No op support both dynamic and static") - - def dynamic_op_tuning_cfg_from_static(op_tuning_cfg: OpTuningConfig): - new_op_tuning_cfg = deepcopy(op_tuning_cfg) - new_op_tuning_cfg.op_quant_mode = "dynamic" - return new_op_tuning_cfg - - new_op_tuning_cfg = deepcopy(self.cur_best_tuning_cfg) - for item in static_dynamic_items: - new_op_tuning_cfg[item.name] = dynamic_op_tuning_cfg_from_static(new_op_tuning_cfg[item.name]) - new_op_tuning_cfg["calib_sampling_size"] = calib_sampling_size - yield new_op_tuning_cfg - - # Fallback one by one by op sensitivity(mse) - # 1. while the accuracy requirements not met: # to improve the accuracy - # 1) calculate the sensitivity of int8 ops in current state. - # 2) fallback the op with higher sensitivity accumulatively - # 2. after the accuracy requirements met: # to improve the performance - # 1) calculate the sensitivity of fp32 ops in the current state - # 2) re-quantize the op with lower sensitivity accumulatively - tune_cfg = deepcopy(self.cur_best_tuning_cfg) - requantize_cfg = deepcopy(self._tune_cfg_converter(self.cur_best_tuning_cfg)) - self.output_op_names = self.adaptor.get_output_op_names(self.last_qmodel) - self.confidence_batches = ( - self.cfg.tuning.strategy.confidence_batches - if self.cfg.tuning.strategy.confidence_batches is not None - else 2 - ) - tune_cfg_backup = deepcopy(tune_cfg) - quant_ops_in_tune_cfg = self._collect_ops_by_quant_mode( - tune_cfg, "dynamic" - ) + self._collect_ops_by_quant_mode(tune_cfg, "static") - op_quant_cfgs = {op_info: tune_cfg_backup[op_info] for op_info in quant_ops_in_tune_cfg} - fallback_records = [] - self.re_quant = True - while not self.objectives.compare(self.last_tune_result, self.baseline): - # Record the time of calculating the sensitivity - start = time() - ops_lst = self.adaptor.calculate_op_sensitivity( - self.model, - self.calib_dataloader, - deepcopy(self._tune_cfg_converter(tune_cfg)), - self.output_op_names, - self.confidence_batches, - fallback=True, - ) - logger.debug(f"*** The op sensitivity analysis took {time() - start:.2f}s.") - select_op_info = ops_lst[0] - logger.info( - f"*** The op {select_op_info} have the highest sensitivity in the current state, \ - fallback it to fp32." - ) - tune_cfg[select_op_info] = OpTuningConfig( - select_op_info[0], select_op_info[1], "fp32", self.tuning_space - ) - # Record the fallback history - if not fallback_records: - fallback_records = [[select_op_info]] - else: - fallback_records.append(fallback_records[-1] + [select_op_info]) - logger.debug(f"*** The fallback ops record: \n{self._tuning_record_msg(fallback_records)}") - yield tune_cfg - - logger.info("*** The accuracy meeting the accuracy requirements, stop fallback ops.") - while self.objectives.compare(self.last_tune_result, self.baseline): - if len(fallback_records) == 0 or len(fallback_records[-1]) <= 1: - logger.info("*** Stop re-quant due to no int8 op or only 1 int8 op left.") - break - logger.info("*** Start to re-quant the fallback op in the previous stage.") - # Track the current fallback ops - tmp_fallback_ops = fallback_records[-1] if fallback_records else [] - start = time() - ops_lst = self.adaptor.calculate_op_sensitivity( - self.model, - self.calib_dataloader, - deepcopy(self._tune_cfg_converter(tune_cfg)), - self.output_op_names, - self.confidence_batches, - fallback=False, - requantize_cfgs=requantize_cfg["op"], - ) - logger.debug(f"*** The op sensitivity analysis took {time() - start:.2f}s.") - if not ops_lst: - logger.warning("No op to be requantized") - break - for select_op_info in ops_lst: - # assert select_op_info in tmp_fallback_ops, f"{select_op_info} not in fallback list." - if select_op_info not in tmp_fallback_ops: - logger.debug(f"{select_op_info} not in fallback list.") - continue - - new_fallback_ops = deepcopy(tmp_fallback_ops) - new_fallback_ops.remove(select_op_info) - if new_fallback_ops not in fallback_records: - logger.info( - f"*** The op {select_op_info} have the lowest sensitivity in the current state, \ - re-quantize it." - ) - tune_cfg[select_op_info] = op_quant_cfgs[select_op_info] - fallback_records.append(new_fallback_ops) - logger.debug(f"*** The fallback ops record: \n{self._tuning_record_msg(fallback_records)}") - yield tune_cfg - break - else: - logger.debug(f"*** Skip re-qaunt {select_op_info}, due the config has been evallated.") - continue - self.re_quant = False - logger.info("*** The accuracy not meeting the accuracy requirements, stop re-quantize ops.") diff --git a/neural_compressor/experimental/strategy/random.py b/neural_compressor/experimental/strategy/random.py deleted file mode 100644 index 2420981e539..00000000000 --- a/neural_compressor/experimental/strategy/random.py +++ /dev/null @@ -1,56 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""The random tuning strategy.""" -import numpy as np -from deprecated import deprecated - -from ...utils import logger -from .strategy import TuneStrategy, strategy_registry -from .utils.tuning_sampler import FallbackTuningSampler, OpWiseTuningSampler -from .utils.tuning_structs import OpTuningConfig - - -@deprecated(version="2.0") -@strategy_registry -class RandomTuneStrategy(TuneStrategy): - """The random tuning strategy.""" - - def next_tune_cfg(self): - """Generate and yield the next tuning config by random searching in tuning space. - - Random strategy is used to randomly choose quantization tuning configurations - from the tuning space. As with the Exhaustive strategy, it also only considers - quantization tuning configs to generate a better-performance quantized model. - - Returns: - tune_config (dict): A dict containing the tuning configuration for quantization. - """ - tuning_space = self.tuning_space - op_item_dtype_dict, quant_mode_wise_items, initial_op_tuning_cfg = self.initial_tuning_cfg() - op_wise_tuning_sampler = OpWiseTuningSampler(tuning_space, [], [], op_item_dtype_dict, initial_op_tuning_cfg) - op_tuning_cfg_lst = list(op_wise_tuning_sampler) - op_tuning_cfg_cnt = len(op_tuning_cfg_lst) - calib_sampling_size_lst = tuning_space.root_item.get_option_by_name("calib_sampling_size").options - calib_sampling_size_cnt = len(calib_sampling_size_lst) - while True: - calib_index = np.random.choice(calib_sampling_size_cnt) - calib_sampling_size = calib_sampling_size_lst[calib_index] - op_tuning_cfg_index = np.random.choice(op_tuning_cfg_cnt) - op_tuning_cfg = op_tuning_cfg_lst[op_tuning_cfg_index] - op_tuning_cfg["calib_sampling_size"] = calib_sampling_size - yield op_tuning_cfg - return diff --git a/neural_compressor/experimental/strategy/strategy.py b/neural_compressor/experimental/strategy/strategy.py deleted file mode 100644 index dbf62a613b7..00000000000 --- a/neural_compressor/experimental/strategy/strategy.py +++ /dev/null @@ -1,1394 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""The base class for tuning strategy.""" - -import copy -import math -import os -import pickle -from abc import abstractmethod -from collections import OrderedDict, defaultdict -from copy import deepcopy -from pathlib import Path -from time import time - -import numpy as np -import yaml -from deprecated import deprecated - -from neural_compressor.adaptor.tensorflow import TensorFlowAdaptor -from neural_compressor.config import options - -from ...adaptor import FRAMEWORKS -from ...algorithm import ALGORITHMS, AlgorithmScheduler -from ...conf.dotdict import DotDict, deep_get, deep_set -from ...objective import MultiObjective -from ...utils import logger -from ...utils.create_obj_from_config import create_eval_func, create_train_func -from ...utils.utility import GLOBAL_STATE, MODE, Statistics, equal_dicts, fault_tolerant_file -from ...version import __version__ -from .utils.constant import FALLBACK_RECIPES_SET -from .utils.tuning_space import TuningSpace -from .utils.tuning_structs import OpTuningConfig - -EXP_STRATEGIES = {} - - -@deprecated(version="2.0") -def strategy_registry(cls): - """Class decorator used to register all TuneStrategy subclasses. - - Args: - cls (class): The class of register. - - Returns: - cls: The class of register. - """ - assert cls.__name__.endswith( - "TuneStrategy" - ), "The name of subclass of TuneStrategy should end with 'TuneStrategy' substring." - if cls.__name__[: -len("TuneStrategy")].lower() in EXP_STRATEGIES: - raise ValueError("Cannot have two strategies with the same name") - EXP_STRATEGIES[cls.__name__[: -len("TuneStrategy")].lower()] = cls - return cls - - -@deprecated(version="2.0") -@strategy_registry -class TuneStrategy(object): - """Basic class for tuning strategy.""" - - def __init__( - self, - model, - conf, - q_dataloader=None, - q_func=None, - eval_dataloader=None, - eval_func=None, - resume=None, - q_hooks=None, - ): - """Init the TuneStrategy. - - Args: - model: The FP32 model specified for low precision tuning. - conf: The Conf class instance includes all user configurations. - q_dataloader: Data loader for calibration, mandatory for post-training quantization. Defaults to None. - q_func: Training function for quantization aware training. Defaults to None. Defaults to None. - eval_dataloader: Data loader for evaluation. Defaults to None. - eval_func: The evaluation function provided by user. This function takes model as parameter, and - evaluation dataset and metrics should be encapsulated in this function implementation and - outputs a higher-is-better accuracy scalar value. - resume: The dict containing resume information. Defaults to None. - q_hooks: The dict of training hooks, supported keys are: on_epoch_begin, on_epoch_end, on_step_begin, - on_step_end. Their values are functions to be executed in adaptor layer.. Defaults to None. - last_qmodel: The quantized model that generated from the last tuning. - best_qmodel: The best quantized model that generated during the tuning process. - """ - self.model = model - self.cfg = conf.usr_cfg - self.cfg_bk = copy.deepcopy(self.cfg) - self.history_path = self._create_path(self.cfg.tuning.workspace.path, "./history.snapshot") - self.deploy_path = self._create_path(self.cfg.tuning.workspace.path, "deploy.yaml") - self.eval_dataloader = eval_dataloader - self.calib_dataloader = q_dataloader - self.q_func = q_func - self.q_hooks = q_hooks - self.eval_func = eval_func - GLOBAL_STATE.STATE = MODE.QUANTIZATION - framework, framework_specific_info = self._set_framework_info(q_dataloader, q_func) - self.adaptor = FRAMEWORKS[framework](framework_specific_info) - self.framework = framework - - self.set_q_func() - self._set_objectives() - self.tune_data = {} - self.tune_result_record = [] - self.tuning_history = [] - self.tuning_result_data = [] - # The tuning history ever made, structured like below: - # [ - # { - # 'version': __version__, - # 'cfg': cfg1, - # 'framework': tensorflow - # 'baseline': baseline1, - # 'last_tune_result': last_tune_result1, - # 'best_tune_result': best_tune_result1, - # 'history': [ - # # tuning history under same yaml config - # {'tune_cfg': tune_cfg1, 'tune_result': \ - # tune_result1, 'q_config': q_config1, ...}, - - # ..., - # ], - # # new fields added by subclass for resuming - # ..., - # }, - # # tuning history under different yaml configs - # ..., - # ] - - self.baseline = None - self.last_tune_result = None - self.last_qmodel = None - self.last_tune_cfg = None - self.best_qmodel = None - self.best_tune_result = None - self.best_tuning_cfg = None # track the best tuning config correspondence to the best quantized model - self.cur_best_acc = self.initial_best_acc() # track the current best accuracy - self.cur_best_tuning_cfg = {} # track tuning cfg with the current best accuracy - self.re_quant = False - - self.capability = self.adaptor.query_fw_capability(model) - logger.debug(self.capability) - self.set_tuning_space(conf) - - # For algo scheduler - self.algo_scheduler = AlgorithmScheduler(self.cfg.quantization.recipes) - self.algo_scheduler.dataloader = self.calib_dataloader # reuse the calibration iteration - self.algo_scheduler.origin_model = self.model - self.algo_scheduler.adaptor = self.adaptor - - self._optype_statistics = None - self.fallback_stats_baseline = None - self.fallback_stats = None - self.tuning_times = 0 - self.fallback_start_point = 0 - self.metric_met_point = 0 - - # for recipes - # {recipe name: the list of supported value} - self._tuning_recipes = OrderedDict() - # {recipe name: the default value when not tuning} - self._tuning_recipes_default_values = {} - # {recipe name: the value specified by user} - self._not_tuning_recipes_values = {} - self._initialize_recipe() - self.applied_all_recipes_flag = False - if resume is not None: - self.setup_resume(resume) - - @abstractmethod - def next_tune_cfg(self): - """Interface for generate the next tuning config. - - The generator of yielding next tuning config to traverse by concrete strategies or quantization level - according to last tuning result and traverse logic. - - It should be implemented by the sub-class. - - Yields: - tune_config (dict): It's a dict containing the tuning configuration to traverse. - """ - raise NotImplementedError - - def _initialize_recipe(self): - """Divide the recipe into two categories tuning/not tuning.""" - from ...utils.constant import RECIPES as fwk_recipes - from ...utils.constant import RECIPES_PRIORITY as fwk_recipes_priority - from .utils.utility import get_adaptor_name - - # get all recipes supported by adaptor. - adaptor_name = get_adaptor_name(self.adaptor) - adaptor_recipes = fwk_recipes["common"] - # TODO WA due to smooth quant only supported by ort/pt currently. - if not adaptor_name not in ["onnx", "pytorch"]: - adaptor_recipes.pop("smooth_quant", None) - for adaptor_name_key, adaptor_recipes_val in fwk_recipes.items(): - if adaptor_name_key.startswith(adaptor_name): - adaptor_recipes.update(adaptor_recipes_val) - # divide it into two categories: - # tuning lst: the value is equal to the default value - # not tuning list: the value is not equal to the default value - logger.info(f"Adaptor has {len(adaptor_recipes)} recipes.") - logger.debug(adaptor_recipes) - usr_recipes_cfg = self.cfg_bk.quantization.recipes if self.cfg_bk.quantization.recipes else {} - for recipe_name, recipe_val in usr_recipes_cfg.items(): - # for not tuning recipes, use the value specified by user. - if recipe_name in adaptor_recipes and recipe_val != adaptor_recipes[recipe_name][0]: - self._not_tuning_recipes_values[recipe_name] = recipe_val - # sorted the recipes and set the default value to be used before recipe tuning - for recipe_name in fwk_recipes_priority: - if recipe_name in adaptor_recipes and recipe_name not in self._not_tuning_recipes_values: - # TODO skip tuning smooth_quant first - if recipe_name == "smooth_quant": - continue - self._tuning_recipes[recipe_name] = adaptor_recipes[recipe_name] - self._tuning_recipes_default_values[recipe_name] = adaptor_recipes[recipe_name][0] - logger.info(f"{len(self._not_tuning_recipes_values)} recipes specified by user.") - logger.debug(self._not_tuning_recipes_values) - logger.info(f"{len(self._tuning_recipes)} recipes require future tuning.") - logger.debug(self._tuning_recipes) - - def _fallback_ops(self, tune_cfg, recipe_op_lst, tuning_space): - """Fallback ops in recipe op list.""" - for op_name_type in recipe_op_lst: - tune_cfg.update({op_name_type: OpTuningConfig(op_name_type[0], op_name_type[1], "fp32", tuning_space)}) - return tune_cfg - - def apply_all_tuning_recipes(self, tune_cfg): - """Apply all tunable recipes with their value.""" - tune_cfg["recipe_cfgs"] = tune_cfg.get("recipe_cfgs", {}) - for recipe_name, recipe_val_lst in self._tuning_recipes.items(): - tune_cfg["recipe_cfgs"][recipe_name] = recipe_val_lst[-1] - if ( - recipe_name in FALLBACK_RECIPES_SET - and "recipes_ops" in self.capability - and len(self.capability["recipes_ops"].get(recipe_name, [])) > 0 - ): - logger.info(f"Applied recipe {recipe_name}.") - tune_cfg = self._fallback_ops(tune_cfg, self.capability["recipes_ops"][recipe_name], self.tuning_space) - return tune_cfg - - def apply_recipe_one_by_one(self, tune_cfg): - """Apply the tunable recipes one by one. - - For recipes only have two options, apply the last one. - For recipes with multiple values. such as alpha of smooth quant, apply it one by one. - """ - from .utils.tuning_sampler import TuningSamplerRegistry - - all_registered_samplers = TuningSamplerRegistry.sampler_dict - for recipe_name, recipe_vals in self._tuning_recipes.items(): - if ( - recipe_name in FALLBACK_RECIPES_SET - and "recipes_ops" in self.capability - and len(self.capability["recipes_ops"].get(recipe_name, [])) > 0 - ): - logger.info(f"Applied recipe {recipe_name} with value {recipe_vals[-1]}") - new_tune_cfg = self._fallback_ops( - copy.deepcopy(tune_cfg), self.capability["recipes_ops"][recipe_name], self.tuning_space - ) - yield new_tune_cfg - if recipe_name == "smooth_quant": - sq_args = {"smooth_quant": True} - if "recipe_cfgs" not in new_tune_cfg: - new_tune_cfg["recipe_cfgs"] = sq_args - else: - new_tune_cfg["recipe_cfgs"].update(sq_args) - new_tune_cfg["recipe_cfgs"] = sq_args - yield new_tune_cfg - - def set_param_for_pre_quantization_algos(self, algo_scheduler, tune_cfg, fp32_model) -> None: - """Set the parameter for pre-quantization algos, such as smooth quantization. - - Args: - algo_scheduler: algo scheduler - tune_cfg: the tuning config - fp32_model: the fp32 model - """ - algo_scheduler.origin_model = fp32_model - algo_scheduler.calib_iter = tune_cfg["calib_iteration"] - algo_scheduler.q_model = fp32_model - - recipe_cfgs = tune_cfg.get("recipe_cfgs", None) - algo_scheduler.reset_exec_algorithms() - if recipe_cfgs and recipe_cfgs.get("smooth_quant", False): # pragma: no cover - # skip assign alpha to sq first. - # set the alpha to 0.5 by default - smooth_quant_args = recipe_cfgs.get("smooth_quant_args", {"alpha": 0.5}) - sq_algo = ALGORITHMS()["smooth_quant"] - sq_algo.alpha = smooth_quant_args["alpha"] - if "folding" not in smooth_quant_args: - smooth_quant_args["folding"] = True if self.framework in ["pytorch", "pytorch_fx"] else False - logger.info("SmoothQuant args 'folding' is not set, it's {} now.".format(smooth_quant_args["folding"])) - if self.framework == "pytorch_ipex": - smooth_quant_args["folding"] = None # will reset it to True if IPEX version < 2.1. - sq_algo.folding = smooth_quant_args["folding"] - logger.debug(f"Set smooth quant with alpha {smooth_quant_args['alpha']} as the pre-quantization algo.") - algo_scheduler.append_algorithm("pre_quantization", sq_algo) - - def set_param_for_post_quantization_algos(self, algo_scheduler, tune_cfg, pre_optimized_model, q_model) -> None: - """Set the parameter for post-quantization algos, such as bias correction, weight correction. - - Args: - algo_scheduler: algo scheduler - tune_cfg: the tuning config. - pre_optimized_model: the pre-optimized model - q_model: the quantized model - """ - algo_scheduler.origin_model = pre_optimized_model - # if no pre-process algos, return the fp32 model directly. - algo_scheduler.q_model = q_model - - algo_scheduler.reset_exec_algorithms() - recipe_cfgs = tune_cfg.get("recipe_cfgs", None) - # for fast_bias_correction - if recipe_cfgs and recipe_cfgs.get("fast_bias_correction", False): - fbc_algo = ALGORITHMS()["fast_bias_correction"] - fbc_algo.quantization_cfg = deepcopy(tune_cfg) - algo_scheduler.append_algorithm("post_quantization", fbc_algo) - logger.debug("Add fast bias correction as the post quantization algo.") - # for weight correction - if recipe_cfgs and recipe_cfgs.get("weight_correction", False): - w_algo = ALGORITHMS()["weight_correction"] - w_algo.quantization_cfg = deepcopy(tune_cfg) - algo_scheduler.append_algorithm("post_quantization", w_algo) - logger.debug("Add weight correction as the post quantization algo.") - - def traverse(self): - """Traverse the tuning space. - - The main traverse logic which could be override by some concrete strategy which needs more hooks. - """ - self._eval_baseline() - trials_count = 0 - traverse_start_time = time() - for op_tuning_cfg in self.next_tune_cfg(): - tuning_start_time = time() - tune_cfg = self._tune_cfg_converter(op_tuning_cfg) - trials_count += 1 - tuning_history = self._find_tuning_history(tune_cfg) - if tuning_history and trials_count < self.cfg.tuning.exit_policy.max_trials: - self.last_tune_result = tuning_history["last_tune_result"] - self.best_tune_result = tuning_history["best_tune_result"] - logger.warn("Find evaluated tuning config, skip.") - continue - self._remove_redundant_qmodel() - logger.debug("Dump current tuning configuration:") - logger.debug(tune_cfg) - self.tuning_times += 1 - # set the parameter for pre quantization algos and run - self.set_param_for_pre_quantization_algos(self.algo_scheduler, tune_cfg, self.model) - self.model = self.algo_scheduler("pre_quantization") - # quantize - q_model = self.adaptor.quantize(copy.deepcopy(tune_cfg), self.model, self.calib_dataloader, self.q_func) - assert self.adaptor.pre_optimized_model - # set the parameter for post quantization algos and run - self.set_param_for_post_quantization_algos( - self.algo_scheduler, tune_cfg, self.adaptor.pre_optimized_model, q_model - ) - self.last_qmodel = self.algo_scheduler("post_quantization") - self.last_tune_cfg = copy.deepcopy(tune_cfg) - # Remove the reference to model - self.algo_scheduler.reset_exec_algorithms() - assert self.last_qmodel - # Return the last quantized model as a result. if performance only. - if self.cfg.tuning.exit_policy.performance_only: - self.best_qmodel = self.last_qmodel - self._add_tuning_history(copy.deepcopy(tune_cfg), (-1, [0]), q_config=self.last_qmodel.q_config) - return - self.last_tune_result = self._evaluate(self.last_qmodel) - self.cur_best_acc, self.cur_best_tuning_cfg = self.update_best_op_tuning_cfg(op_tuning_cfg) - need_stop = self.stop(self.cfg.tuning.exit_policy.timeout, trials_count) - - # record the tuning history - saved_tune_cfg = copy.deepcopy(tune_cfg) - saved_last_tune_result = copy.deepcopy(self.last_tune_result) - self._add_tuning_history(saved_tune_cfg, saved_last_tune_result, q_config=q_model.q_config) - self.tune_result_record.append(copy.deepcopy(self.last_tune_result)) - self.tune_cfg = tune_cfg - now_time = time() - acc_res_msg = "" - performace_res_msg = "" - if self.tuning_result_data: - acc_res_msg = "[ " + "| ".join(self.tuning_result_data[0]) + " ]" - performace_res_msg = "[ " + "| ".join(self.tuning_result_data[1]) + " ]" - logger.debug(f"*** The accuracy of last tuning is: {acc_res_msg}") - logger.debug(f"*** The performance of last tuning is: {performace_res_msg}") - logger.debug(f"*** The last tuning time: {(now_time - tuning_start_time):.2f} s") - logger.debug(f"*** The tuning process lasted time: {(now_time - traverse_start_time):.2f} s") - - self._dump_tuning_process_statistics() - if need_stop: - if self.re_quant: - logger.info("*** Do not stop the tuning process, re-quantize the ops.") - continue - # recover the best quantized model from tuning config - self._recover_best_qmodel_from_tuning_cfg() - if self.cfg.tuning.diagnosis: - logger.debug("*** Start to do diagnosis (inspect tensor).") - self._diagnosis() - if self.use_multi_objective and len(self.tune_result_record) > 1 and self.best_tune_result is not None: - best_trail, best_result = self.objectives.best_result( - self.tune_result_record, copy.deepcopy(self.baseline) - ) - if best_result != self.best_tune_result: - from neural_compressor.utils.utility import recover - - self.best_qmodel = recover( - self.model.model, - os.path.join(self.cfg.tuning.workspace.path, "history.snapshot"), - best_trail, - ) - logger.debug("*** Update the best qmodel by recovering from history.") - self.best_tune_result = best_result - self._dump_tuning_process_statistics() - break - self._recover_best_qmodel_from_tuning_cfg() - - def _remove_redundant_qmodel(self): - """Remove the redundant quantized model to reduce memory use. - - During the tuning process, the strategy only keeps the best tuning config - instead of the best quantized model to reduce memory use. - """ - self.last_qmodel = None - self.best_qmodel = None - - def _can_create_eval_func_from_cfg(self): - """Determine whether an eval function can be created from cfg. - - Returns: - Returns True if the eval func can be created from config, False otherwise. - """ - if ( - self.cfg.evaluation - and self.cfg.evaluation.accuracy - and (self.cfg.evaluation.accuracy.metric or self.cfg.evaluation.accuracy.multi_metrics) - and self.eval_dataloader - ): - return True - return False - - def _eval_baseline(self): - """Evaluate the fp32 model if needed.""" - if not self._can_create_eval_func_from_cfg() and not self.eval_func: - logger.info( - "Neither evaluation function nor metric is defined." - " Generate a quantized model with default quantization configuration." - ) - self.cfg.tuning.exit_policy.performance_only = True - logger.info("Force setting 'tuning.exit_policy.performance_only = True'.") - - if not self.cfg.tuning.exit_policy.performance_only: - # get fp32 model baseline - if self.baseline is None: - logger.info("Get FP32 model baseline.") - self._fp32_model = self.model - self.baseline = self._evaluate(self.model) - self.objectives.baseline = self.baseline - # record the FP32 baseline - self._add_tuning_history() - self.show_baseline_info() - - def _recover_best_qmodel_from_tuning_cfg(self): - """Recover the best quantized model from tuning config.""" - if self.best_tuning_cfg and not self.best_qmodel: - self.best_qmodel = self.adaptor.quantize( - copy.deepcopy(self.best_tuning_cfg), self.model, self.calib_dataloader, self.q_func - ) - - def _fallback_started(self): - self.fallback_start_point = self.tuning_times - - def _update_optype_statistics(self): - self._optype_statistics = defaultdict(lambda: defaultdict(int)) - - for op_name_type, op_tune_cfg in self.tune_cfg["op"].items(): - optype = op_name_type[1] - quant_mode = op_tune_cfg["activation"]["quant_mode"] - if isinstance(quant_mode, tuple) or isinstance(quant_mode, list): - quant_mode = quant_mode[0] - dtype = "INT8" if quant_mode in ("static", "dynamic") else quant_mode.upper() - self._optype_statistics[optype]["Total"] += 1 - self._optype_statistics[optype][dtype] += 1 - return - - def _dump_tuning_process_statistics(self): - self._update_optype_statistics() - - logger.debug("Current tuning process statistics:") - logger.debug(f"Total Tuning Times: {self.tuning_times}") - logger.debug("Fallback started at Tune {}".format(self.fallback_start_point)) - logger.debug("Objective(s) met at Tune {}".format(self.metric_met_point)) - - fallback_stats = self._calculate_fallback_op_count() - if self.fallback_stats_baseline is None: - self.fallback_stats_baseline = fallback_stats - logger.debug(f"Fallbacked ops count: {self.fallback_stats_baseline - fallback_stats}") - - if isinstance(self.adaptor, TensorFlowAdaptor): - self._compare_optype_statistics() - - return - - def _calculate_fallback_op_count(self, target_dtype="INT8"): - fallback_stats = defaultdict(int) - - for optype in self._optype_statistics: - for dtype, count in self._optype_statistics[optype].items(): - fallback_stats[dtype] += count - - return fallback_stats[target_dtype] - - def _compare_optype_statistics(self, fields=None, optypes=None, skip_fields=None, skip_optypes=None): - assert fields is None or skip_fields is None - assert optypes is None or skip_optypes is None - if not isinstance(self.adaptor, TensorFlowAdaptor): - logger.debug("OpType statistics comparison is only available for TensorFlow adaptor.") - return - - adaptor_statistics = self.adaptor.optype_statistics - - def _field_skipped(field): - if fields is not None: - return field not in fields - elif skip_fields is not None: - return field in skip_fields - - def _optype_skipped(optype): - if optypes is not None: - return optype not in optypes - elif skip_optypes is not None: - return optype in skip_optypes - - field_names = adaptor_statistics[0][1:] - adaptor_data = { - line[0].lower(): {dtype: count for dtype, count in zip(field_names, line[1:])} - for line in adaptor_statistics[1] - } - strategy_data = self._optype_statistics - - # compare adaptor statistics to strategy statistics - logger.debug("Statistics difference between adaptor and tuning config:") - has_difference = False - difference_count = 0 - for optype in adaptor_data: - if optype not in strategy_data or _optype_skipped(optype): - continue - for field in field_names: - if _field_skipped(field): - continue - adaptor_count = adaptor_data[optype][field] - strategy_count = strategy_data[optype][field] - if adaptor_count != strategy_count: - has_difference = True - if field == "INT8": - difference_count += abs(strategy_count - adaptor_count) - logger.debug( - "\t{}: [adaptor: {} | tune_cfg: {}]".format((optype, field), adaptor_count, strategy_count) - ) - if not has_difference: - logger.debug("\tNone") - logger.debug(f"\tDifference(s) in total: {difference_count}") - return - - def initial_tuning_cfg(self): - """Init the tuning config. - - Initialize the tuning config according to the quantization approach. - - Returns: - op_item_dtype_dict (OrderedDict): key is (op_name, op_type); value is quantization mode. - quant_mode_wise_items (OrderedDict): key is quant_mode/precision; value is item list. - initial_op_tuning_cfg (OrderedDict): key is (op_name, op_type); value is the initialized tuning config. - """ - from .utils.constant import auto_query_order, dynamic_query_order, static_query_order - from .utils.tuning_space import initial_tuning_cfg_with_quant_mode - - if self.cfg.quantization.approach == "post_training_auto_quant": - query_order = auto_query_order - elif self.cfg.quantization.approach == "post_training_dynamic_quant": - query_order = dynamic_query_order - elif self.cfg.quantization.approach == "post_training_static_quant": - query_order = static_query_order - elif self.cfg.quantization.approach == "quant_aware_training": - logger.info("!!! Currently, the qat tuning is not supported by strategy.") - query_order = auto_query_order - - quant_mode_wise_items = OrderedDict() # mode, op_item_lst - pre_items = set() - # Collect op items supported the specified mode. - for quant_mode in query_order: - items = self.tuning_space.query_items_by_quant_mode(quant_mode) - filtered_items = list(filter(lambda item: item not in pre_items, items)) - pre_items = pre_items.union(set(items)) - quant_mode_wise_items[quant_mode] = filtered_items - - def initial_op_quant_mode(items_lst, target_quant_mode, op_item_dtype_dict): - for item in items_lst: - op_item_dtype_dict[item.name] = target_quant_mode - - op_item_dtype_dict = OrderedDict() - for quant_mode, quant_mode_items in quant_mode_wise_items.items(): - initial_op_quant_mode(quant_mode_items, quant_mode, op_item_dtype_dict) - - initial_op_tuning_cfg = {} - for op_name_type, quant_mode in op_item_dtype_dict.items(): - initial_op_tuning_cfg[op_name_type] = initial_tuning_cfg_with_quant_mode( - op_name_type, quant_mode, self.tuning_space - ) - return op_item_dtype_dict, quant_mode_wise_items, initial_op_tuning_cfg - - def show_baseline_info(self): - """Display the accuracy and duration of the the baseline model.""" - if self.baseline: - self.tune_data["baseline"] = self.baseline[0] if isinstance(self.baseline[0], list) else [self.baseline[0]] - for name, data in zip(self.metric_name, self.tune_data["baseline"]): - self.tune_data[name] = [data] - if self.metric_weight: - # baseline is weighted accuracy - self.tune_data["Weighted accuracy"] = [ - np.mean(np.array(self.tune_data["baseline"]) * self.metric_weight) - ] - self.tune_data["baseline"] = self.tune_data["Weighted accuracy"] - baseline_msg = ( - "[Accuracy:" - + "".join([" {:.4f}".format(i) for i in self.tune_data["baseline"]]) - + "".join( - [ - ", {}: {:.4f}".format(x, y) - for x, y in zip(self.objectives.representation, self.baseline[1]) - if x != "Accuracy" - ] - ) - + "]" - ) - else: # pragma: no cover - if self.metric_weight: - self.tune_data["Weighted accuracy"] = ["n/a"] - self.tune_data["baseline"] = ["n/a"] - - for name, data in zip(self.metric_name, self.tune_data["baseline"]): - self.tune_data[name] = ["n/a"] - baseline_msg = "n/a" - logger.info("FP32 baseline is: {}".format(baseline_msg)) - - def initial_best_acc(self): - """Init the best accuracy. - - Returns: - The initial value of best accuracy. - """ - if len(self.metric_name) == 1 or self.metric_weight is not None: - best_acc = float("-inf") if self.higher_is_better else float("inf") - else: - best_acc = [ - float("-inf") if higher_is_better else float("inf") for higher_is_better in self.metric_criterion - ] - return best_acc - - def _tune_cfg_converter(self, op_tuning_cfg): - """Convert op_tuning_cfg for adaptor. - - Args: - op_tuning_cfg (Dict): the op tuning config. - """ - tune_cfg = {"op": OrderedDict()} - for op_name_type, op_config in op_tuning_cfg.items(): - if isinstance(op_config, OpTuningConfig): - tune_cfg["op"][op_name_type] = op_config.get_state() - op_cap_lst = self.capability["opwise"][op_name_type] - # Add pattern for diagnosis - for op_cap in op_cap_lst: - if "pattern" in op_cap: - op_pattern = {} - op_pattern["sequence"] = ( - op_cap["pattern"]["sequence"][0] if "sequence" in op_cap["pattern"] else None - ) - op_pattern["precision"] = ( - op_cap["pattern"]["precision"][0] if "precision" in op_cap["pattern"] else None - ) - tune_cfg["op"][op_name_type]["pattern"] = op_pattern - else: - tune_cfg[op_name_type] = op_config - tune_cfg["calib_sampling_size"] = op_tuning_cfg["calib_sampling_size"] - if self.calib_dataloader is not None: - # For the accelerate's DataLoaderShard, use total_batch_size instead of batch_size - bs = getattr(self.calib_dataloader, "batch_size") or getattr(self.calib_dataloader, "total_batch_size") - assert bs > 0, f"Calibration dataloader's batch size should be greater than one but got {bs}" - tune_cfg["calib_iteration"] = math.ceil(int(tune_cfg["calib_sampling_size"]) / bs) - else: - tune_cfg["calib_iteration"] = 1 - tune_cfg["advance"] = self.cfg.quantization.advance - tune_cfg["approach"] = self.cfg.quantization.approach - # Add the recipe config - tune_cfg["recipe_cfgs"] = tune_cfg.get("recipe_cfgs", {}) - # For not tuning recipe, tune cfg use it directly - tune_cfg["recipe_cfgs"].update(self._not_tuning_recipes_values) - # WA for get the smooth quant args - if "smooth_quant_args" in self.cfg_bk.quantization.recipes: - tune_cfg["recipe_cfgs"]["smooth_quant_args"] = self.cfg_bk.quantization.recipes["smooth_quant_args"] - # For tuning recipe, use the default value if it not specified by recipe tuning sampler. - for recipe_name, recipe_val in self._tuning_recipes_default_values.items(): - if recipe_name not in tune_cfg["recipe_cfgs"]: - tune_cfg["recipe_cfgs"][recipe_name] = recipe_val - return tune_cfg - - def set_tuning_space(self, conf): - """Create the tuning space. - - Create the tuning space based on the framework capability and user configuration. - - Args: - conf: The Conf class instance includes all user configurations. - """ - calib_sampling_size_lst = self.cfg.quantization.calibration.sampling_size - calib_sampling_size_lst = [int(calib_sampling_size) for calib_sampling_size in calib_sampling_size_lst] - if self.calib_dataloader: - # For the accelerate's DataLoaderShard, use total_batch_size instead of batch_size - bs = getattr(self.calib_dataloader, "batch_size") or getattr(self.calib_dataloader, "total_batch_size") - assert bs > 0, f"Calibration dataloader's batch size should be greater than one but got {bs}" - self.calib_iter = [math.ceil(int(x) / bs) for x in calib_sampling_size_lst] - else: - self.calib_iter = 1 - # create tuning space - adaptor_cap = {"calib": {"calib_sampling_size": calib_sampling_size_lst}, "op": self.capability["opwise"]} - self.tuning_space = TuningSpace(adaptor_cap, conf=conf, framework=self.framework) - - def setup_resume(self, resume): - """Resume the best quantized model from tuning history. - - Args: - resume: The dict containing resume information. - """ - self.__dict__.update(resume) - for history in self.tuning_history: - if self._same_yaml(history["cfg"], self.cfg): - self.__dict__.update({k: v for k, v in history.items() if k not in ["version", "history"]}) - logger.info("Start to resume tuning process.") - # resume the best tuning model if needed - try: - index = history["id"] - 1 - resume_tuning_cfg = history["history"][index]["tune_cfg"] - self.best_qmodel = self.adaptor.quantize( - resume_tuning_cfg, self.model, self.calib_dataloader, self.q_func - ) - except: - logger.debug("Can not resume the best quantize model from history.") - - break - - def set_q_func(self): - """Set the training function for quantization aware training.""" - if self.q_func is None and self.cfg.quantization.approach == "quant_aware_training": - train_cfg = self.cfg.quantization.train - assert train_cfg, ( - "train field of quantization section in yaml file must " - "be configured for quantization aware training if q_func is NOT set." - ) - assert self.calib_dataloader, ( - "dataloader field of train field of quantization " "section in yaml file must be configured." - ) - self.q_func = create_train_func( - self.framework, self.calib_dataloader, self.adaptor, train_cfg, hooks=self.q_hooks - ) - - def _create_path(self, custom_path, filename): - new_path = os.path.join(os.path.abspath(os.path.expanduser(custom_path)), filename) - path = Path(os.path.dirname(new_path)) - path.mkdir(exist_ok=True, parents=True) - return new_path - - def _set_framework_info(self, q_dataloader, q_func=None): - framework_specific_info = { - "device": self.cfg.device, - "approach": self.cfg.quantization.approach, - "random_seed": self.cfg.tuning.random_seed, - "performance_only": self.cfg.tuning.exit_policy.performance_only, - } - framework = self.cfg.model.framework.lower() - framework_specific_info.update({"backend": self.cfg.model.get("backend", "default")}) - framework_specific_info.update({"format": self.cfg.model.get("quant_format", "default")}) - framework_specific_info.update({"domain": self.cfg.model.get("domain", "auto")}) - - self.mixed_precision_mode = bool("mixed_precision" in self.cfg) or bool("graph_optimization" in self.cfg) - - if "tensorflow" in framework: - framework_specific_info.update( - { - "inputs": self.cfg.model.inputs, - "outputs": self.cfg.model.outputs, - "workspace_path": self.cfg.tuning.workspace.path, - "recipes": self.cfg.quantization.recipes, - "use_bf16": self.cfg.use_bf16 if self.cfg.use_bf16 is not None else False, - } - ) - for item in ["scale_propagation_max_pooling", "scale_propagation_concat"]: - if item not in framework_specific_info["recipes"]: - framework_specific_info["recipes"].update({item: True}) - if self.cfg.model.backend == "itex": - self.cfg.model.framework = "tensorflow_itex" - framework = "tensorflow_itex" - if "keras" in framework: - framework_specific_info.update( - { - "workspace_path": self.cfg.tuning.workspace.path, - } - ) - if framework == "mxnet": - framework_specific_info.update({"q_dataloader": q_dataloader}) - if "onnx" in framework.lower(): - if self.mixed_precision_mode: - framework_specific_info.update({"approach": "post_training_dynamic_quant"}) - framework_specific_info.update({"deploy_path": os.path.dirname(self.deploy_path)}) - framework_specific_info.update({"workspace_path": self.cfg.tuning.workspace.path}) - framework_specific_info.update({"recipes": self.cfg.quantization.recipes}) - framework_specific_info.update({"reduce_range": self.cfg.reduce_range}) - framework_specific_info.update({"recipes": self.cfg.quantization.get("recipes", {})}) - if framework.lower() == "onnxrt_qdq" or framework_specific_info["backend"] == "onnxrt_trt_ep": - framework_specific_info.update({"format": "QDQ"}) - framework = "onnxrt_qdq" - if framework == "pytorch_ipex" or framework == "pytorch" or framework == "pytorch_fx": - if self.cfg.model.backend == "ipex": - self.cfg.model.framework = "pytorch_ipex" - framework = "pytorch_ipex" - elif self.cfg.model.backend == "default": - self.cfg.model.framework = "pytorch_fx" - framework = "pytorch_fx" - if self.mixed_precision_mode: - framework_specific_info.update({"approach": "post_training_dynamic_quant"}) - framework_specific_info.update({"q_dataloader": q_dataloader}) - framework_specific_info.update({"use_bf16": self.cfg.use_bf16 if self.cfg.use_bf16 is not None else True}) - framework_specific_info.update({"workspace_path": os.path.dirname(self.deploy_path)}) - if ( - self.cfg["quantization"]["op_wise"] is not None - and "default_qconfig" in self.cfg["quantization"]["op_wise"] - ): - framework_specific_info.update( - {"default_qconfig": self.cfg["quantization"]["op_wise"]["default_qconfig"]} - ) - framework_specific_info.update({"q_func": q_func}) - framework_specific_info.update({"example_inputs": self.cfg.quantization.example_inputs}) - return framework, framework_specific_info - - def _set_objectives(self): - self.higher_is_better = bool(self.cfg.tuning.accuracy_criterion.higher_is_better) - self.use_multi_objective = ( - deep_get(self.cfg, "tuning.multi_objectives") and len(self.cfg.tuning.multi_objectives.objective) > 1 - ) - objectives = ( - [i.lower() for i in self.cfg.tuning.multi_objectives.objective] - if self.use_multi_objective - else [self.cfg.tuning.objective.lower()] - ) - self.metric_weight = deep_get(self.cfg, "evaluation.accuracy.multi_metrics.weight") - self.metric_name = ( - ["Accuracy"] - if not deep_get(self.cfg, "evaluation.accuracy.multi_metrics") - else self.cfg.evaluation.accuracy.multi_metrics.keys() - {"weight", "higher_is_better"} - ) - if len(self.metric_name) == 1: - self.metric_criterion = [self.higher_is_better] - elif not deep_get(self.cfg, "evaluation.accuracy.multi_metrics.higher_is_better"): - # default is True - self.metric_criterion = [True] * len(self.metric_name) - else: - self.metric_criterion = deep_get(self.cfg, "evaluation.accuracy.multi_metrics.higher_is_better") - - self.objectives = MultiObjective( - objectives, - self.cfg.tuning.accuracy_criterion, - self.metric_criterion, - self.metric_weight, - deep_get(self.cfg, "tuning.multi_objectives.higher_is_better"), - deep_get(self.cfg, "tuning.multi_objectives.weight"), - ) - - def _same_yaml(self, src_yaml, dst_yaml): - """Check if the two yamls are the same. - - The check will exclude those keys which do not really impact the tuning result, such as - tensorboard, workspace, resume options under the tuning section of YAML. - """ - if equal_dicts(src_yaml, dst_yaml, ignore_keys=["tuning"]) and equal_dicts( - src_yaml.tuning, - src_yaml.tuning, - compare_keys=["objective", "accuracy_criterion", "random_seed", "exit_policy"], - ): - return True - - return False - - def update_best_op_tuning_cfg(self, op_tuning_cfg): - """Track and update the best tuning config with correspondence accuracy result. - - Args: - op_tuning_cfg: The tuning config. - - Returns: - The current best tuning results and corresponding configurations. - """ - acc, _ = self.last_tune_result - if self.cur_best_tuning_cfg is None: - self.cur_best_tuning_cfg = copy.deepcopy(op_tuning_cfg) - if not isinstance(acc, list) and ( - (self.higher_is_better and acc >= self.cur_best_acc) - or (not self.higher_is_better and acc <= self.cur_best_acc) - ): - self.cur_best_acc = acc - self.cur_best_tuning_cfg = copy.deepcopy(op_tuning_cfg) - elif len(self.metric_name) > 1 and self.metric_weight is not None: - acc = np.mean(np.array(acc) * self.metric_weight) - if (self.higher_is_better and acc >= self.cur_best_acc) or ( - not self.higher_is_better and acc <= self.cur_best_acc - ): - self.cur_best_acc = acc - self.cur_best_tuning_cfg = copy.deepcopy(op_tuning_cfg) - elif len(self.metric_name) > 1 and self.metric_weight is None: - if all( - [ - acc_i >= best_i if higher_is_better else acc_i <= best_i - for acc_i, best_i, higher_is_better in zip(acc, self.cur_best_acc, self.metric_criterion) - ] - ): - self.cur_best_acc = acc - self.cur_best_tuning_cfg = copy.deepcopy(op_tuning_cfg) - logger.debug(f"Best acc is {self.cur_best_acc}.") - return self.cur_best_acc, self.cur_best_tuning_cfg - - def deploy_config(self): - """Save the configuration locally for deployment.""" - acc_dataloader_cfg = deep_get(self.cfg, "evaluation.accuracy.dataloader") - perf_dataloader_cfg = deep_get(self.cfg, "evaluation.performance.dataloader") - # use acc dataloader if perf dataloader is not configured - if perf_dataloader_cfg is None: - perf_dataloader_cfg = acc_dataloader_cfg - - self.deploy_cfg = OrderedDict() - # int8 dataloader graph transform - if ( - deep_get(perf_dataloader_cfg, "transform.QuantizedInput") is not None - or deep_get(acc_dataloader_cfg, "transform.QuantizedInput") is not None - ): - self.best_qmodel, scale = self.adaptor.quantize_input(self.best_qmodel) - deep_set(perf_dataloader_cfg, "transform.QuantizedInput.dtype", "int8") - deep_set(perf_dataloader_cfg, "transform.QuantizedInput.scale", scale) - deep_set(acc_dataloader_cfg, "transform.QuantizedInput.dtype", "int8") - deep_set(acc_dataloader_cfg, "transform.QuantizedInput.scale", scale) - - self.deploy_cfg["model"] = self.cfg.model - self.deploy_cfg["device"] = self.cfg.device - if self.cfg.evaluation is not None: - deep_set(self.cfg, "evaluation.performance.dataloader", perf_dataloader_cfg) - deep_set(self.cfg, "evaluation.accuracy.dataloader", acc_dataloader_cfg) - self.deploy_cfg["evaluation"] = self.cfg.evaluation - - def setup_yaml(): - represent_dict_order = lambda self, data: self.represent_mapping("tag:yaml.org,2002:map", data.items()) - yaml.add_representer(OrderedDict, represent_dict_order) - yaml.add_representer(DotDict, represent_dict_order) - - setup_yaml() - with open(self.deploy_path, "w+") as f: - yaml.dump(self.deploy_cfg, f) - logger.info("Save deploy yaml to {}".format(self.deploy_path)) - - @property - def evaluation_result(self): - """Evaluate the given model. - - Returns: - The objective value evaluated. - """ - return self._evaluate(self.model) - - def _evaluate(self, model): - """Interface of evaluating model. - - Args: - model (object): The model to be evaluated. - - Returns: - Objective: The objective value evaluated. - """ - if self.eval_func: - if self.cfg.tuning.tensorboard: - # Pytorch can insert observer to model in this hook. - # Tensorflow don't support this mode for now - model = self.adaptor._pre_eval_hook(model) - val = self.objectives.evaluate(self.eval_func, model if self.framework == "pytorch_ipex" else model.model) - if self.cfg.tuning.tensorboard: - # post_eval_hook to deal the tensor - self.adaptor._post_eval_hook(model, accuracy=val[0]) - else: - assert ( - self.cfg.evaluation - and self.cfg.evaluation.accuracy - and (self.cfg.evaluation.accuracy.metric or self.cfg.evaluation.accuracy.multi_metrics) - ), ("metric or multi_metrics field of accuracy field of evaluation" " section should not be empty") - - postprocess_cfg = self.cfg.evaluation.accuracy.postprocess - metric_cfg = ( - self.cfg.evaluation.accuracy.metric - if self.cfg.evaluation.accuracy.metric - else self.cfg.evaluation.accuracy.multi_metrics - ) - iteration = -1 if self.cfg.evaluation.accuracy.iteration is None else self.cfg.evaluation.accuracy.iteration - eval_func = create_eval_func( - self.framework, - self.eval_dataloader, - self.adaptor, - metric_cfg, - postprocess_cfg, - iteration, - tensorboard=self.cfg.tuning.tensorboard, - fp32_baseline=self.baseline is None, - ) - - if getattr(self.eval_dataloader, "distributed", False): - if "tensorflow" in self.framework: - import horovod.tensorflow as hvd - elif self.framework in ["pytorch_ipex", "pytorch", "pytorch_fx"]: - import horovod.torch as hvd - else: - raise NotImplementedError( - "Currently only TensorFlow and PyTorch " "support distributed inference in PTQ." - ) - hvd.init() - try: - len_dataloader = len(self.eval_dataloader) - except: - logger.info( - "The length of the distributed dataloader is unknown." - "When the iteration of evaluation dataloader in each " - "process is inconsistent, an error may occur." - ) - else: - list_len_dataloader = hvd.allgather_object(len_dataloader) - if hvd.rank() == 0: - for i in range(len(list_len_dataloader) - 1): - if list_len_dataloader[i] != list_len_dataloader[i + 1]: - raise AttributeError( - "The evaluation dataloader's iteration is" - "different between processes, please reset " - "dataloader's batch_size." - ) - val = self.objectives.evaluate(eval_func, model) - if isinstance(val[0], list): - assert all( - [np.isscalar(i) for i in val[0]] - ), "The eval_func should return a scalar or list of scalar, " "but not {}!".format( - str([type(i) for i in val[0]]) - ) - else: - assert np.isscalar(val[0]), "The eval_func should return a scalar or list of scalar, " "but not {}!".format( - str(type(val[0])) - ) - - return val - - def __getstate__(self): - """Magic method for pickle saving. - - Returns: - dict: Saved dict for resuming - """ - return {"tuning_history": self.tuning_history} - - def __setstate__(self, d): - """Magic method for pickle loading. - - Args: - d (dict): The dict to load. - """ - self.__dict__.update(d) - - def stop(self, timeout, trials_count): - """Check if need to stop traverse. - - Check if need to stop traversing the tuning space, either accuracy goal is met or timeout is reach. - - Returns: - bool: True if need stop, otherwise False - """ - need_stop = False - if self.cfg.tuning.exit_policy.performance_only or self.objectives.compare( - self.best_tune_result, self.baseline - ): - self.best_tune_result = self.last_tune_result - self.best_qmodel = self.last_qmodel - self.best_tuning_cfg = copy.deepcopy(self.last_tune_cfg) - logger.debug(f"*** Update the best qmodel with the result {self.best_tune_result}") - if self.metric_met_point == 0: - self.metric_met_point = self.tuning_times - - # track the model with highest acc - if self.best_tune_result and self.last_tune_result: # (acc, [perf]) - if self.re_quant and self.objectives.accuracy_meets(): - self.best_tune_result = self.last_tune_result - self.best_qmodel = self.last_qmodel - self.best_tuning_cfg = copy.deepcopy(self.last_tune_cfg) - logger.debug(f"*** Update the best qmodel with the result {self.best_tune_result}.") - else: - logger.debug("*** Accuracy not meets the requirements, do not update the best qmodel.") - - if self.last_tune_result: - last_tune = ( - self.last_tune_result[0] if isinstance(self.last_tune_result[0], list) else [self.last_tune_result[0]] - ) - - for name, data in zip(self.metric_name, last_tune): - if len(self.tune_data[name]) == 1: - self.tune_data[name].append(data) - else: - self.tune_data[name][1] = data - - if self.metric_weight and len(last_tune) > 1: - weighted_acc = np.mean(np.array(last_tune) * self.metric_weight) - - if len(self.tune_data["Weighted accuracy"]) == 1: - self.tune_data["Weighted accuracy"].append(weighted_acc) - else: - self.tune_data["Weighted accuracy"][1] = weighted_acc - - last_tune = [weighted_acc] - - last_tune_msg = ( - "[Accuracy (int8|fp32):" - + "".join( - [" {:.4f}|{:.4f}".format(last, base) for last, base in zip(last_tune, self.tune_data["baseline"])] - ) - + "".join( - [ - ", {} (int8|fp32): {:.4f}|{:.4f}".format(x, y, z) - for x, y, z in zip(self.objectives.representation, self.last_tune_result[1], self.baseline[1]) - if x != "Accuracy" - ] - ) - + "]" - ) - else: # pragma: no cover - last_tune_msg = "n/a" - for name in self.tune_data.keys() - {"baseline"}: - if len(self.tune_data[name]) == 1: - self.tune_data[name].append("n/a") - else: - self.tune_data[name][1] = "n/a" - - if self.best_tune_result: - best_tune = ( - self.best_tune_result[0] if isinstance(self.best_tune_result[0], list) else [self.best_tune_result[0]] - ) - - for name, data in zip(self.metric_name, best_tune): - if len(self.tune_data[name]) == 2: - self.tune_data[name].append(data) - else: - self.tune_data[name][2] = data - - if self.metric_weight and len(best_tune) > 1: - weighted_acc = np.mean(np.array(best_tune) * self.metric_weight) - - if len(self.tune_data["Weighted accuracy"]) == 2: - self.tune_data["Weighted accuracy"].append(weighted_acc) - else: # pragma: no cover - self.tune_data["Weighted accuracy"][2] = weighted_acc - - best_tune = [weighted_acc] - - best_tune_msg = ( - "[Accuracy:" - + "".join([" {:.4f}".format(best) for best in best_tune]) - + "".join( - [ - ", {}: {:.4f}".format(x, y) - for x, y in zip(self.objectives.representation, self.best_tune_result[1]) - if x != "Accuracy" - ] - ) - + "]" - ) - - else: - best_tune_msg = "n/a" - for name in self.tune_data.keys() - {"baseline"}: - if len(self.tune_data[name]) == 2: - self.tune_data[name].append("n/a") - else: - self.tune_data[name][2] = "n/a" - - logger.info("Tune {} result is: {}, Best tune result is: {}".format(trials_count, last_tune_msg, best_tune_msg)) - output_data = [ - [ - info_type, - ( - "{:.4f} ".format(self.tune_data[info_type][0]) - if not isinstance(self.tune_data[info_type][0], str) - else self.tune_data[info_type][0] - ), - ( - "{:.4f} ".format(self.tune_data[info_type][1]) - if not isinstance(self.tune_data[info_type][1], str) - else self.tune_data[info_type][1] - ), - ( - "{:.4f} ".format(self.tune_data[info_type][2]) - if not isinstance(self.tune_data[info_type][2], str) - else self.tune_data[info_type][2] - ), - ] - for info_type in self.tune_data.keys() - if info_type != "baseline" - ] - - output_data.extend( - [ - [ - obj, - "{:.4f} ".format(self.baseline[1][i]) if self.baseline else "n/a", - "{:.4f} ".format(self.last_tune_result[1][i]) if self.last_tune_result else "n/a", - "{:.4f} ".format(self.best_tune_result[1][i]) if self.best_tune_result else "n/a", - ] - for i, obj in enumerate(self.objectives.representation) - ] - ) - self.tuning_result_data = output_data - Statistics( - output_data, - header="Tune Result Statistics", - field_names=["Info Type", "Baseline", "Tune {} result".format(trials_count), "Best tune result"], - ).print_stat() - - if self.cfg.tuning.exit_policy.performance_only: - need_stop = True - elif timeout == 0 and self.best_tune_result: - need_stop = True - elif trials_count >= self.cfg.tuning.exit_policy.max_trials: - need_stop = True - else: - need_stop = False - - return need_stop - - def _save(self): - """Save current tuning state to snapshot for resuming.""" - logger.info("Save tuning history to {}.".format(self.history_path)) - with fault_tolerant_file(self.history_path) as f: - pickle.dump(self, f, protocol=pickle.HIGHEST_PROTOCOL) - - def _find_tuning_history(self, tune_cfg): - """Check if the specified tune_cfg is evaluated or not on same yaml config. - - Args: - tune_cfg (dict): The tune_cfg to check if evaluated before. - - Returns: - tuning_history or None: The tuning history containing evaluated tune_cfg. - """ - for tuning_history in self.tuning_history: - # only check if a tune_cfg is evaluated under same yam config, excluding - # some fields in tuning section of yaml, such as tensorboard, snapshot, resume. - if self._same_yaml(tuning_history["cfg"], self.cfg): - for history in tuning_history["history"]: - if history and history["tune_cfg"] == tune_cfg: - return tuning_history - - return None - - def _find_history(self, tune_cfg): - """Check if the specified tune_cfg is evaluated or not on same yaml config. - - Returns: - history or None: The history containing evaluated tune_cfg. - """ - for tuning_history in self.tuning_history: - # only check if a tune_cfg is evaluated under same yam config, excluding - # some fields in tuning section of yaml, such as tensorboard, snapshot, resume. - if self._same_yaml(tuning_history["cfg"], self.cfg): - for history in tuning_history["history"]: - if history and history["tune_cfg"] == tune_cfg: - return history - return None - - def _find_self_tuning_history(self): - """Find self history dict. - - Returns: - history or None: The history for self. - """ - for tuning_history in self.tuning_history: - # only check if a tune_cfg is evaluated under same yam config, excluding - # some fields in tuning section of yaml, such as tensorboard, snapshot, resume. - if self._same_yaml(tuning_history["cfg"], self.cfg): - return tuning_history - - return None - - def _add_tuning_history(self, tune_cfg=None, tune_result=None, **kwargs): - """Add tuning config to tuining history. - - Note this record is added under same yaml config. - """ - found = False - d = {"tune_cfg": tune_cfg, "tune_result": tune_result} - for tuning_history in self.tuning_history: - if self._same_yaml(tuning_history["cfg"], self.cfg): - d.update(kwargs) - tuning_history["history"].append(d) - tuning_history["last_tune_result"] = self.last_tune_result - tuning_history["best_tune_result"] = self.best_tune_result - tuning_history["cfg"] = self.cfg - found = True - break - - if not found: - tuning_history = {} - tuning_history["version"] = __version__ - tuning_history["cfg"] = self.cfg - tuning_history["baseline"] = self.baseline - tuning_history["last_tune_result"] = self.last_tune_result - tuning_history["best_tune_result"] = self.best_tune_result - tuning_history["history"] = [] - if tune_cfg and tune_result: - d.update(kwargs) - tuning_history["history"].append(d) - self.tuning_history.append(tuning_history) - - self._save() - - def _collect_ops_by_quant_mode(self, tune_cfg, quant_mode): - ops_lst = [] - for op_info, op_config in tune_cfg.items(): - if isinstance(op_config, OpTuningConfig) and quant_mode in op_config.op_quant_mode: - ops_lst.append(op_info) - return ops_lst - - def _diagnosis(self): - import logging - - logger = logging.getLogger("neural_compressor") - iteration_list = [1] - inspect_type = "all" - save_to_disk = True - save_path = os.path.join(options.workspace, "inspect_saved") - inspect_node_lst, updated_cfg = self.adaptor.diagnosis_helper( - self._fp32_model, self.last_qmodel, self.tune_cfg, save_path=save_path - ) - op_list = [] - if not op_list: - op_list = list(inspect_node_lst) - else: - op_list = list(set(op_list).intersection(inspect_node_lst)) - - logger.debug(f"*** Start to inspect tensor :{op_list} in fp32 model.") - self.adaptor.inspect_tensor( - self._fp32_model, - dataloader=self.calib_dataloader, - op_list=op_list, - iteration_list=iteration_list, - inspect_type=inspect_type, - save_to_disk=save_to_disk, - save_path=save_path + "/fp32/", - quantization_cfg=updated_cfg, - ) - - logger.debug(f"*** Start to inspect tensor :{op_list} in quantized model.") - self.adaptor.inspect_tensor( - self.last_qmodel, - dataloader=self.calib_dataloader, - op_list=op_list, - iteration_list=iteration_list, - inspect_type=inspect_type, - save_to_disk=save_to_disk, - save_path=save_path + "/quan/", - quantization_cfg=updated_cfg, - ) diff --git a/neural_compressor/experimental/strategy/utils/__init__.py b/neural_compressor/experimental/strategy/utils/__init__.py deleted file mode 100644 index 1b730c7ded2..00000000000 --- a/neural_compressor/experimental/strategy/utils/__init__.py +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Intel Neural Compressor Strategy Utils.""" - -from .tuning_sampler import TuningSampler, OpWiseTuningSampler, OpTypeWiseTuningSampler, FallbackTuningSampler -from .tuning_structs import OpTuningConfig -from .tuning_space import TuningItem, TuningSpace diff --git a/neural_compressor/experimental/strategy/utils/constant.py b/neural_compressor/experimental/strategy/utils/constant.py deleted file mode 100644 index 44ac03e79be..00000000000 --- a/neural_compressor/experimental/strategy/utils/constant.py +++ /dev/null @@ -1,47 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2023 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Strategy constant.""" - -PRECISION_SET = { - "bf16", - "fp16", - "fp32", -} -QUANT_MODE_SET = {"static", "dynamic"} -QUNAT_BIT_SET = {"int8", "uint8", "int4", "uint4"} - -TUNING_ITEMS_LST = [ - ("activation", "scheme"), - ("activation", "algorithm"), - ("activation", "granularity"), - ("weight", "scheme"), - ("weight", "algorithm"), - ("weight", "granularity"), - "sampling_size", -] - -PRECISION_SET_V2_0 = {"fp32", "bf16"} - -auto_query_order = ["static", "dynamic", "bf16", "fp16", "fp32"] -static_query_order = ["static", "bf16", "fp16", "fp32"] -dynamic_query_order = ["dynamic", "bf16", "fp16", "fp32"] - - -FALLBACK_RECIPES_SET = { - "first_conv_or_matmul_quantization", - "last_conv_or_matmul_quantization" "pre_post_process_quantization", -} diff --git a/neural_compressor/experimental/strategy/utils/tuning_sampler.py b/neural_compressor/experimental/strategy/utils/tuning_sampler.py deleted file mode 100644 index 293d4446d79..00000000000 --- a/neural_compressor/experimental/strategy/utils/tuning_sampler.py +++ /dev/null @@ -1,494 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Tuning sampler.""" - -import copy -from collections import OrderedDict, defaultdict, deque -from itertools import product -from typing import Any, Dict, List - -from deprecated import deprecated - -from ....utils import logger -from .tuning_space import TuningSpace, pattern_to_internal, pattern_to_path, quant_mode_from_pattern -from .tuning_structs import OpTuningConfig - -TUNING_ITEM_PRIORITY = [ - ("activation", "scheme"), - ("activation", "algorithm"), - ("activation", "granularity"), - ("activation", "compute_dtype"), - ("weight", "scheme"), - ("weight", "algorithm"), - ("weight", "granularity"), -] - - -@deprecated(version="2.0") -class TuningSamplerRegistry: - """Class decorator used to register all TuningSampler subclasses.""" - - sampler_dict = {} - - @classmethod - def register(cls, name): - """Register new tuning sampler. - - Args: - name: the name of new tuning sampler. - """ - - def decorator(sampler): - assert name not in cls.sampler_dict, "Cannot have two sampler with the same name." - cls.sampler_dict[name] = sampler - - return decorator - - -@deprecated(version="2.0") -class TuningOrder: - """Not displayed in API Docs.""" - - def __init__(self): - """For future use.""" - pass - - -@deprecated(version="2.0") -class TuningSampler: - """Not displayed in API Docs. - - Basic class of tuning sampler. - """ - - def __init__( - self, - tuning_space: TuningSpace, - tuning_order_lst: List[TuningOrder], - initial_op_tuning_cfg: Dict, - kwargs: Dict = {}, - ): - """Init tuning sampler. - - Args: - tuning_space: The tuning space. - tuning_order_lst: The traverse orders. - initial_op_tuning_cfg: The initialized tuning config. - kwargs: other args. - """ - self.tuning_space = tuning_space - self.tuning_order_lst = tuning_order_lst - self.initial_op_tuning_cfg = initial_op_tuning_cfg - self.queue = deque() - # (op_name, op_type): [full_path1, full_path2,...] - self.op_complete_path = {} - - def __iter__(self, tune_cfg=None): - """Interface for generate the next tuning config.""" - pass - - def _set_dtype(self, op_name_type, config_args): - has_weight = op_name_type in self.tuning_space.ops_attr["weight"] - path = self.op_complete_path[op_name_type].get("activation", None) - config_args["activation_dtype"] = self.tuning_space.ops_data_type[op_name_type][path] - if has_weight: - path = self.op_complete_path[op_name_type].get("weight", None) - config_args["weight_dtype"] = self.tuning_space.ops_data_type[op_name_type][path] - - -@deprecated(version="2.0") -class ModelWiseTuningSampler(TuningSampler): - """Not displayed in API Docs.""" - - def __init__( - self, - tuning_space: TuningSpace, - tuning_items_priority: List[str], - tuning_order_lst: List[TuningOrder], - op_dtype_dict: Dict[tuple, str], - initial_op_tuning_cfg: Dict[tuple, OpTuningConfig], - ): - """Model type wise tuning sampler. - - step1. create a default tuning config for each op - step2. collect all tuning items and options, and build the model-wise traverse order - step3. yield the tuning item with option one by one, query the existence of tuning item - and specific option for one op if exist, use the default tuning config if not exist - - Args: - tuning_space: Tuning space. - tuning_items_priority: The priority to traverse the tuning items. - tuning_order_lst: The tuning orders. - op_dtype_dict: The (op name, op type) and its target data type. - initial_op_tuning_cfg: The initial tuning config. - """ - super().__init__(tuning_space, tuning_order_lst, initial_op_tuning_cfg) - - self.op_dtype_dict = op_dtype_dict - self.tuning_space = tuning_space - self.default_op_config = {} - tuning_items = defaultdict(set) # item name: options - for op_name_type, quant_mode in op_dtype_dict.items(): - full_path = self.tuning_space.get_op_default_path_by_pattern(op_name_type, quant_mode) - self.op_complete_path[op_name_type] = copy.deepcopy(full_path) - # step1, set the default config for each op - self.default_op_config[op_name_type] = tuning_space.get_default_config(op_name_type, quant_mode) - if quant_mode[0] == "precision": - continue - mode_items = copy.deepcopy(full_path) # TODO refactor the initialization method - # step2, collect all tuning items and their options - for att in mode_items: - if att not in full_path: - continue - quant_mode_item = self.tuning_space.query_quant_mode_item_by_full_path(op_name_type, full_path[att]) - for tuning_item in quant_mode_item.options: - tuning_items[tuning_item.name] = tuning_items[tuning_item.name].union(tuning_item.options) - self.tuning_items = tuning_items - - def __iter__(self): - """Yield the next tuning config. - - Yields: - The next tuning config. - """ - keys = self.tuning_items.keys() - for vals in product(*self.tuning_items.values()): - # traverse all possible combinations by model-wise level - tune_cfg = copy.deepcopy(self.initial_op_tuning_cfg) - for op_name_type, quant_mode in self.op_dtype_dict.items(): - if quant_mode[0] == "precision": - continue - all_exist_flag = True - for method_name, method_val in zip(keys, vals): - full_path = self.op_complete_path[op_name_type] - if method_name[0] not in full_path: - continue - if not self.tuning_space.query_item_option( - op_name_type, full_path[method_name[0]], method_name, method_val - ): - all_exist_flag = False - tune_cfg[op_name_type] = self.default_op_config[op_name_type] - break - if all_exist_flag: - config_args = dict(zip(keys, vals)) - self._set_dtype(op_name_type, config_args) - internal_pattern = pattern_to_internal(quant_mode) - quant_mode = quant_mode_from_pattern(internal_pattern) - tune_cfg[op_name_type] = OpTuningConfig( - op_name_type[0], op_name_type[1], quant_mode, self.tuning_space, kwargs=config_args - ) - yield tune_cfg - - -@deprecated(version="2.0") -class OpTypeWiseTuningSampler(TuningSampler): - """Not displayed in API Docs.""" - - def __init__( - self, - tuning_space: TuningSpace, - tuning_items_priority: List[str], - tuning_order_lst: List[TuningOrder], - op_dtype_dict: Dict[tuple, str], - initial_op_tuning_cfg: Dict[tuple, OpTuningConfig], - ): - """Op type wise tuning sampler. - - Args: - tuning_space: Tuning space. - tuning_items_priority: The priority to traverse the tuning items. - tuning_order_lst: The tuning orders. - op_dtype_dict: The (op name, op type) and its target data type. - initial_op_tuning_cfg: The initial tuning config. - """ - super().__init__(tuning_space, tuning_order_lst, initial_op_tuning_cfg) - tuning_items_priority = TUNING_ITEM_PRIORITY - # (op_type, quant_mode) : {tuning_item_name : [option1, option2]} - # {('activation', 'scheme'): ['sym', 'sym'], ('activation', 'algorithm'): ['minmax', 'kl', 'minmax', 'kl']} - - self.optype_quant_mode_option = {} - self.optype_quant_mode_items_name = defaultdict(list) - self.op_type_quant_mode_wise_combination = {} - self.op_dtype_dict = op_dtype_dict - self.default_op_config = {} - - for op_name_type, quant_mode in op_dtype_dict.items(): - full_path = self.tuning_space.get_op_default_path_by_pattern(op_name_type, quant_mode) - self.op_complete_path[op_name_type] = copy.deepcopy(full_path) - self.default_op_config[op_name_type] = self.tuning_space.get_default_config(op_name_type, quant_mode) - op_name, op_type = op_name_type - if quant_mode[0] == "precision": - continue - mode_items = copy.deepcopy(full_path) # TODO refactor the initialization method - op_type_quant_mode = (op_type, quant_mode) - filtered_tuning_items = [] - for item_name in tuning_items_priority: - att, method_name = item_name - if att not in mode_items: - continue - quant_mode_item = self.tuning_space.query_quant_mode_item_by_full_path(op_name_type, full_path[att]) - item = quant_mode_item.get_option_by_name(item_name) - if item: - if op_type_quant_mode not in self.optype_quant_mode_option: - self.optype_quant_mode_option[op_type_quant_mode] = defaultdict(list) - self.optype_quant_mode_option[op_type_quant_mode][item_name] += item.options - filtered_tuning_items.append(item) - self.optype_quant_mode_items_name[op_type_quant_mode] = filtered_tuning_items - - for op_type_quant_mode, val in self.optype_quant_mode_option.items(): - options_lst = [] - # remove the duplicate options - for _, item_options in val.items(): - seen = set() - filter_options = [option for option in item_options if not (option in seen or seen.add(option))] - options_lst.append(filter_options) - op_type_quant_mode_vals = product(*options_lst) - self.op_type_quant_mode_wise_combination[op_type_quant_mode] = op_type_quant_mode_vals - - def __iter__(self): - """Yield the next tuning config. - - Yields: - The next tuning config. - """ - new_tune_cfg = copy.deepcopy(self.initial_op_tuning_cfg) - for options_lst in product(*self.op_type_quant_mode_wise_combination.values()): - for index, op_type_quant_mode in enumerate(self.op_type_quant_mode_wise_combination.keys()): - for op_name_type, quant_mode in self.op_dtype_dict.items(): - if op_name_type[1] == op_type_quant_mode[0] and quant_mode == op_type_quant_mode[1]: - op_tuning_items = [item.name for item in self.optype_quant_mode_items_name[op_type_quant_mode]] - op_tuning_item_vals = options_lst[index] - all_exist_flag = True - for method_name, method_val in zip(op_tuning_items, op_tuning_item_vals): - full_path = self.op_complete_path[op_name_type] - if not self.tuning_space.query_item_option( - op_name_type, full_path[method_name[0]], method_name, method_val - ): - all_exist_flag = False - op_tuning_config = self.default_op_config[op_name_type] - break - if all_exist_flag: - config_args = dict(zip(op_tuning_items, op_tuning_item_vals)) - self._set_dtype(op_name_type, config_args) - internal_pattern = pattern_to_internal(quant_mode) - quant_mode = quant_mode_from_pattern(internal_pattern) - op_tuning_config = OpTuningConfig( - op_name_type[0], op_name_type[1], quant_mode, self.tuning_space, kwargs=config_args - ) - new_tune_cfg.update({op_name_type: op_tuning_config}) - yield new_tune_cfg - - -@deprecated(version="2.0") -class OpWiseTuningSampler(TuningSampler): - """Not displayed in API Docs.""" - - def __init__( - self, - tuning_space: TuningSpace, - tuning_items_priority: List[str], - tuning_order_lst: List[TuningOrder], - op_dtype_dict: Dict[tuple, str], - initial_op_tuning_cfg: Dict, - ): - """Op wise tuning config sampler. - - Args: - tuning_space: Tuning space. - tuning_items_priority: The priority to traverse the tuning items. - tuning_order_lst: The tuning orders. - op_dtype_dict: The (op name, op type) and its target data type. - initial_op_tuning_cfg: The initial tuning config. - """ - super().__init__(tuning_space, tuning_order_lst, initial_op_tuning_cfg) - tuning_items_priority = TUNING_ITEM_PRIORITY - # query the combination of tuning items with according to the tuning items priority - self.op_dtype_dict = op_dtype_dict - self.op_options_combination = OrderedDict() - self.op_tuning_items = {} - for op_name_type, op_quant_mode in op_dtype_dict.items(): - full_path = self.tuning_space.get_op_default_path_by_pattern(op_name_type, op_quant_mode) - self.op_complete_path[op_name_type] = copy.deepcopy(full_path) - mode_items = copy.deepcopy(full_path) - internal_pattern = pattern_to_internal(op_quant_mode) - op_quant_mode = quant_mode_from_pattern(internal_pattern) - if internal_pattern[0] == "precision": - continue - filtered_tuning_items = [] - for item_name in tuning_items_priority: - att, method_name = item_name - if att not in mode_items: - continue - quant_mode_item = self.tuning_space.query_quant_mode_item_by_full_path(op_name_type, full_path[att]) - item = quant_mode_item.get_option_by_name(item_name) - if item: - filtered_tuning_items.append(item) - self.op_tuning_items[op_name_type] = filtered_tuning_items - op_options_lst = product(*[item.options for item in filtered_tuning_items]) - self.op_options_combination[op_name_type] = op_options_lst - - def __iter__(self): - """Yield the next tuning config. - - Yields: - The next tuning config. - """ - new_tune_cfg = copy.deepcopy(self.initial_op_tuning_cfg) - for op_options_lst in product(*self.op_options_combination.values()): - for index, op_name_type in enumerate(self.op_options_combination.keys()): - op_quant_mode = self.op_dtype_dict[op_name_type] - op_tuning_items = [item.name for item in self.op_tuning_items[op_name_type]] - op_tuning_item_vals = op_options_lst[index] - config_args = dict(zip(op_tuning_items, op_tuning_item_vals)) - self._set_dtype(op_name_type, config_args) - internal_pattern = pattern_to_internal(op_quant_mode) - quant_mode = quant_mode_from_pattern(internal_pattern) - op_tuning_config = OpTuningConfig( - op_name_type[0], op_name_type[1], quant_mode, self.tuning_space, kwargs=config_args - ) - new_tune_cfg.update({op_name_type: op_tuning_config}) - yield new_tune_cfg - - def get_opwise_candidate(self): - """Collect all op-wise setting. - - Returns: - op_wise_configs: all op-wise setting. - """ - op_wise_configs = OrderedDict() - for op_name_type, op_quant_mode in self.op_dtype_dict.items(): - # For static/dynamic/fp32/bf16 - internal_pattern = pattern_to_internal(op_quant_mode) - quant_mode = quant_mode_from_pattern(internal_pattern) - full_path = self.tuning_space.get_op_default_path_by_pattern(op_name_type, op_quant_mode) - self.op_complete_path[op_name_type] = copy.deepcopy(full_path) - op_wise_configs[op_name_type] = [] - # For precision - if internal_pattern[0] == "precision": - config_args = {} - self._set_dtype(op_name_type, config_args) - op_tuning_config = OpTuningConfig( - op_name_type[0], op_name_type[1], quant_mode, self.tuning_space, kwargs=config_args - ) - op_wise_configs[op_name_type].append(op_tuning_config) - continue - # For quantization - op_tuning_items = [item.name for item in self.op_tuning_items.get(op_name_type, [])] - op_options = self.op_options_combination[op_name_type] - - for op_tuning_item_vals in op_options: - config_args = dict(zip(op_tuning_items, op_tuning_item_vals)) - self._set_dtype(op_name_type, config_args) - op_tuning_config = OpTuningConfig( - op_name_type[0], op_name_type[1], quant_mode, self.tuning_space, kwargs=config_args - ) - op_wise_configs[op_name_type].append(op_tuning_config) - return op_wise_configs - - -@deprecated(version="2.0") -class FallbackTuningSampler(TuningSampler): - """Not displayed in API Docs.""" - - def __init__( - self, - tuning_space: TuningSpace, - tuning_order_lst: List[TuningOrder], - initial_op_tuning_cfg: Dict[tuple, Any], - op_dtypes: Dict[str, str], - accumulate: bool, - skip_first: bool = True, - ): - """Sampler for generate the tuning config of fallback stage. - - Args: - tuning_space: Tuning space. - tuning_order_lst: The tuning orders. - initial_op_tuning_cfg: The initial tuning config. - op_dtypes: The (op name, op type) and its target data type. - accumulate: Fallback accumulated or not. - skip_first: Skip fallback the first op or not. Defaults to True. - """ - super().__init__(tuning_space, tuning_order_lst, initial_op_tuning_cfg) - self.op_dtypes = op_dtypes - self.accumulate = accumulate - self.skip_first = skip_first - - def __iter__(self): - """Yield the next tuning config. - - Yields: - The next tuning config. - """ - new_tune_cfg = copy.deepcopy(self.initial_op_tuning_cfg) - skip_first = self.skip_first - for op_name_type, target_dtype in self.op_dtypes.items(): - # Only support fallback to lower precision. - if not self.accumulate: - new_tune_cfg = copy.deepcopy(self.initial_op_tuning_cfg) - full_path = self.tuning_space.get_op_default_path_by_pattern(op_name_type, target_dtype) - self.op_complete_path[op_name_type] = copy.deepcopy(full_path) - config_args = {} - self._set_dtype(op_name_type, config_args) - internal_pattern = pattern_to_internal(target_dtype) - quant_mode = quant_mode_from_pattern(internal_pattern) - new_op_config = OpTuningConfig( - op_name_type[0], op_name_type[1], quant_mode, self.tuning_space, kwargs=config_args - ) - - new_tune_cfg.update({op_name_type: new_op_config}) - if self.accumulate and skip_first: # skip the first one - skip_first = False - continue - logger.debug(f"fallback {op_name_type} to {target_dtype}") - yield new_tune_cfg # need to skip the first one - - -@TuningSamplerRegistry.register("smooth_quant") -class SmoothQuantSampler(TuningSampler): - """Sampler for the hyperparameter tuning of smooth quantization.""" - - def __init__( - self, - tuning_space: TuningSpace, - tuning_order_lst: List[TuningOrder], - initial_op_tuning_cfg: Dict, - kwargs: Dict = {}, - ): - """Initialize the sampler.""" - super().__init__(tuning_space, tuning_order_lst, initial_op_tuning_cfg, kwargs) - # TODO use the alpha list specified by user - self._kwargs = kwargs - self._alpha_lst = [0.5] - if kwargs.get("smooth_quant_agrs", {}): - self._alpha_lst = kwargs["smooth_quant_agrs"].get("alpha_lst", [0.5]) - - def __iter__(self, tune_cfg=None) -> OpTuningConfig: - """Yield the next tuning config with update alpha. - - Args: - tune_cfg: tuning config. Defaults to None. - """ - for alpha in self._alpha_lst: - new_tune_cfg = copy.deepcopy(self.initial_op_tuning_cfg) if not tune_cfg else copy.deepcopy(tune_cfg) - sq_args = {"smooth_quant": True, "smooth_quant_args": {"alpha": alpha}} - if "recipe_cfgs" not in new_tune_cfg: - new_tune_cfg["recipe_cfgs"] = sq_args - else: - new_tune_cfg["recipe_cfgs"].update(sq_args) - yield new_tune_cfg diff --git a/neural_compressor/experimental/strategy/utils/tuning_space.py b/neural_compressor/experimental/strategy/utils/tuning_space.py deleted file mode 100644 index 8cb9332b4c4..00000000000 --- a/neural_compressor/experimental/strategy/utils/tuning_space.py +++ /dev/null @@ -1,733 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Tuning space.""" - -import os -import re -from collections import OrderedDict, defaultdict -from copy import deepcopy -from typing import Dict, Tuple - -from deprecated import deprecated - -from ....utils import logger -from .constant import TUNING_ITEMS_LST -from .tuning_structs import OpTuningConfig -from .utility import OrderedDefaultDict - - -@deprecated(version="2.0") -class TuningItem: - """Not displayed in API Docs.""" - - def __init__(self, name, options=[], item_type=None): - """Init the tuning item. - - Args: - name: tuning item name. - options: The options. Defaults to []. - item_type: The item type. Defaults to None. - """ - self.name = name - self._options = options - self.item_type = item_type - - @property - def options(self): - """Return all options. - - Returns: - All options. - """ - return self._options - - def get_options_name(self): - """Return the name list of the options.""" - return [o.name for o in self.options] - - def append(self, option): - """Append option. - - Args: - option: The option to add. - """ - self._options.append(option) - - def remove(self, option): - """Remove option. - - Args: - option: The option to remove. - """ - if option in self._options: - self._options.remove(option) - - def get_option_by_name(self, option_name): - """Get the option item by name. - - Args: - option_name: option name. - - Returns: - option: the queried option. - """ - for option in self.options: - if isinstance(option, TuningItem) and option.name == option_name: - return option - return None - - def get_details(self, depth=0): - """Get the tuning item and its options recursively. - - Args: - depth: recursion depth. Defaults to 0. - - Returns: - The tuning item and its options as a string. - """ - details = ["\t" * depth + f"{self.name}, {self.item_type}"] - for option in self.options: - if isinstance(option, int) or isinstance(option, str): - details.append("\t" * depth + str(option)) - else: - details.append(option.get_details(depth + 1)) - return "\n".join(details) - - -@deprecated(version="2.0") -class TuningSpace: - """Not displayed in API Docs. - - 1) capability -> internal format -> merge -> tuning space (tree) - """ - - def __init__(self, capability, conf, framework=None): - """Init the tuning space. - - Args: - capability: framework capability. - conf: user configuration - framework: framework name. Defaults to None. - """ - self.capability = capability - self.conf = conf - self.root_item = TuningItem(name="root", options=[], item_type="root") - self.quant_mode_wise_items = defaultdict(list) # quant_mode/precision_name: {(op_name, op_type),...} - self.op_type_wise_items = defaultdict(list) # op_type: {(op_name, op_type), ...} - self.framework = framework - self.ops_dtype = defaultdict(OrderedDict) - usr_cfg = conf.usr_cfg if conf else None - self.op_items = {} - # {(op_name, op_type): {(path): data type}} - self.ops_data_type = OrderedDefaultDict() - self.ops_attr = {"activation": set(), "weight": set()} - # {(op_name, op_type): {path1, path2, ...} - self.ops_path_set = defaultdict(set) - - self._create_tuning_space(capability, usr_cfg) - - def _parse_capability(self, capability: Dict) -> None: - """Parse the capability and construct the tuning space(a tree). - - Args: - capability: merged framework capability. - """ - calib = TuningItem( - name="calib_sampling_size", - options=capability["calib"]["calib_sampling_size"], - item_type="calib_sampling_size", - ) - self.root_item.append(calib) - - def _parse(cap, root, path, op_name_type): - if isinstance(cap, dict): - for key, val in cap.items(): - if isinstance(val, dict): - if len(path) > 1 and path[-2] == "precision": - self.ops_path_set[op_name_type].add(tuple(path + [key])) - tuning_item = TuningItem(name=key, options=[], item_type=key) - root.append(tuning_item) - _parse(val, tuning_item, path + [key], op_name_type) - elif isinstance(val, list): - new_key = ("activation", key) if "activation" in path else ("weight", key) - tuning_item = TuningItem(name=new_key, options=val, item_type="method") - self.ops_path_set[op_name_type].add(tuple(path)) - root.append(tuning_item) - else: - return - - for op_name_type, op_cap in capability["op"].items(): - op_name, op_type = op_name_type - op_item = TuningItem(name=op_name_type, options=[], item_type="op") - self.op_type_wise_items[op_type].append(op_item) - self.root_item.append(op_item) - self.op_items[op_name_type] = op_item - _parse(op_cap, op_item, [], op_name_type) - for q_option in op_item.options: - if q_option and q_option.name == "precision": - acc_item = q_option.get_option_by_name("activation") - if acc_item and acc_item.options: - for dtype_item in acc_item.options: - self.quant_mode_wise_items[dtype_item.name].append(op_item) - else: - self.quant_mode_wise_items[q_option.name].append(op_item) - - def _merge_op_cfg(self, cur_op_cap, op_user_cfg, fw_op_cap): - """Merge the op cfg with user cfg. - - op_user_cfg:{ - 'activation':{ - 'dtype': ['fp32'] - }, - 'weight':{ - 'dtype': ['fp32'] - } - } - - Step1. merge dtype, get the intersection between fw_op_cap and op_user_cfg. - Step2. merge method options. - - # if dtype and type intersection with precision set -> only keep the intersection precision - # and remove the quantization. - # else(no dtype, or no intersection) -> merge the method - - Args: - cur_op_cap: current capability. - op_user_cfg: The user capability. - fw_op_cap: The fwk capability(baseline). - - Returns: - Return the merged capability. - """ - from .utility import extract_data_type, reverted_data_type - - fw_op_cap = deepcopy(fw_op_cap) - new_op_cap = deepcopy(cur_op_cap) - for att in ["activation", "weight"]: - if op_user_cfg.get(att, None) is not None: - user_dtype_lst = op_user_cfg[att]["dtype"] if op_user_cfg[att]["dtype"] is not None else [] - # Merge the precision part. - fwk_att_precision_cap = fw_op_cap["precision"].get(att, {}) - fwk_precision_set = set(fwk_att_precision_cap.keys()) - # The intersection of user cfg and fwk capability. - valid_precision_set = set(fwk_precision_set).intersection(set(user_dtype_lst)) - if len(valid_precision_set) != 0: - new_op_cap = dict(filter(lambda item: item[0] == "precision", new_op_cap.items())) - new_op_cap["precision"][att] = dict( - filter(lambda item: item[0] in valid_precision_set, fw_op_cap["precision"][att].items()) - ) - else: - # Filter the valid options for tuning item - for quant_mode in fw_op_cap: - if quant_mode not in new_op_cap: - new_op_cap[quant_mode] = deepcopy(fw_op_cap[quant_mode]) - if quant_mode == "precision": - continue - for data_type in new_op_cap[quant_mode][att]: - for signed_flag in new_op_cap[quant_mode][att][data_type]: - cur_items = new_op_cap[quant_mode][att][data_type][signed_flag] - fwk_items = fw_op_cap[quant_mode][att][data_type][signed_flag] - for method_name, method_options in op_user_cfg[att].items(): - if method_name not in ["dtype", "quant_mode"] and method_options: - # filter the method options - options_intersection = set(fwk_items[method_name]).intersection( - set(method_options) - ) - # merge with fwk, if intersection -> use intersection - if len(options_intersection) > 0: - cur_items[method_name] = [ - option - for option in fwk_items[method_name] - if option in options_intersection - ] - return new_op_cap - - def _merge_optype_wise_cfg(self, cap: Dict, optype_wise_usr_cfg: Dict, fw_cap: Dict): - for op_type, op_user_cfg in optype_wise_usr_cfg.items(): - op_type_pattern = re.compile(op_type) - op_lst = [op_name_type for op_name_type in cap["op"] if op_type_pattern.fullmatch(op_name_type[1])] - for op_name_type in op_lst: - cap["op"][op_name_type] = self._merge_op_cfg( - cap["op"][op_name_type], op_user_cfg, fw_cap["op"][op_name_type] - ) - - def _merge_model_wise_cfg(self, cap: Dict, model_wise_usr_cfg: Dict, fw_cap: Dict): - for op_name_type in cap["op"].keys(): - cap["op"][op_name_type] = self._merge_op_cfg( - cap["op"][op_name_type], model_wise_usr_cfg, fw_cap["op"][op_name_type] - ) - - def _merge_op_wise_cfg(self, cap: Dict, op_wise_usr_cfg: Dict, fw_cap: Dict): - op_name_types = {key[0]: key for key in cap["op"].keys()} - for op_name_pattern, op_user_cfg in op_wise_usr_cfg.items(): - op_name_pattern = re.compile(op_name_pattern) - for op_name in op_name_types: - if op_name_pattern.fullmatch(op_name): - op_name_type = op_name_types[op_name] - cap["op"][op_name_type] = self._merge_op_cfg( - cap["op"][op_name_type], op_user_cfg, fw_cap["op"][op_name_type] - ) - - def _merge_with_user_cfg(self, capability: Dict, user_cfg: Dict): - """Merge the capability with user config. - - Merge the capability queried from the adaptor with user config in the order of - model-wise, optype-wise, and op-wise if needed. - The optype-wise user config will override the model-wise user config for their - intersection parts, the same as the op-wise and optype-wise. - - Here is an example: - capability:{ - ('op1','type1'): { - 'item1': [item1_option1, item1_option2, item1_option3], - 'item2': [item2_option1, item2_option2, item2_option3], - } - ('op2','type1'): { - 'item1': [item1_option1, item1_option2, item1_option3], - 'item2': [item2_option1, item2_option2, item2_option3], - } - ('op3','type2'): { - 'item1': [item1_option1, item1_option2], - 'item2': [item2_option1, item2_option2], - } - ('op4','type2'): { - 'item1': [item1_option1, item1_option2], - 'item2': [item2_option1, item2_option2], - } - } - - user_config{ - model-wise:{ - 'item1': [item1_option1] - } - optype-wise: { - 'type1': { - 'item1': [item1_option1, item1_option2] - }} - op-wise: { - ('op3','type2'): { - 'item2': [item2_option1] - }} - } - - # step1. merged with model-wise - capability:{ - ('op1','type1'): { - 'item1': [item1_option1], - 'item2': [item2_option1, item2_option2, item2_option3], - } - ('op2','type1'): { - 'item1': [item1_option1], - 'item2': [item2_option1, item2_option2, item2_option3], - } - ('op3','type2'): { - 'item1': [item1_option1], - 'item2': [item2_option1, item2_option2], - } - ('op4','type2'): { - 'item1': [item1_option1], - 'item2': [item2_option1, item2_option2], - } - } - - # step2. merged with optype-wise - capability:{ - ('op1','type1'): { - 'item1': [item1_option1, item1_option2], - 'item2': [item2_option1, item2_option2, item2_option3], - } - ('op2','type1'): { - 'item1': [item1_option1, item1_option2], - 'item2': [item2_option1, item2_option2, item2_option3], - } - ('op3','type2'): { - 'item1': [item1_option1], - 'item2': [item2_option1, item2_option2], - } - ('op4','type2'): { - 'item1': [item1_option1], - 'item2': [item2_option1, item2_option2], - } - } - - # step3. merged with op-wise - capability:{ - ('op1','type1'): { - 'item1': [item1_option1, item1_option2], - 'item2': [item2_option1, item2_option2, item2_option3], - } - ('op2','type1'): { - 'item1': [item1_option1, item1_option2], - 'item2': [item2_option1, item2_option2, item2_option3], - } - ('op3','type2'): { - 'item1': [item1_option1], - 'item2': [item2_option1], - } - ('op4','type2'): { - 'item1': [item1_option1], - 'item2': [item2_option1, item2_option2], - } - } - :param capability: - :param user_cfg: - :return: - """ - fw_capability = deepcopy(capability) - if user_cfg["model_wise"] is not None: - self._merge_model_wise_cfg(capability, user_cfg["model_wise"], fw_capability) - if user_cfg["optype_wise"] is not None: - self._merge_optype_wise_cfg(capability, user_cfg["optype_wise"], fw_capability) - if user_cfg["op_wise"] is not None: - self._merge_op_wise_cfg(capability, user_cfg["op_wise"], fw_capability) - - def _parse_cap_helper(self, cap): - """Convert the cpa to internal format. - - Parsed result: - (op_name, op_type): - { - 'static':{ - 'act':{ - 'int8':{ - 'signed':{ # (op_name, op_type): ('static', (('int8', 'signed'),(...))) - 'dtype': 'int8', - 'scheme': ['sym'], - 'algorithm': ['minmax', 'kl'], - 'granularity': ['per_channel','per_tensor'], - } - } - 'int4':{ - ... - } - }, - 'weight':{ - 'int8':{ - ... - } - 'int4':{ - 'signed':{ - 'dtype': 'int4' - 'scheme': ['asym'], - ... - } - } - } - }, - 'dynamic':{ - ... - } - 'precision':{ - 'act':{ - 'fp32':{} - 'bf16':{} - }, - 'weight':{ - 'fp32':{ - 'dtype': 'fp32, - }, - 'bf16':{ - 'dtype': 'fp32', - }, - } - - } - } - """ - from .utility import OrderedDefaultDict, extract_data_type - - cap = deepcopy(cap) - parsed_cap = OrderedDict() # {(op_name, op_type): parsed_op_cap} - for op_name_type, op_cap_lst in cap.items(): - parsed_op_cap = OrderedDefaultDict() # {ptq_type/precision, {}} - parsed_op_cap["precision"] = OrderedDefaultDict() - # WA for some op have extra weight dtype. - has_weight = all(["weight" in op_cap for op_cap in op_cap_lst]) - if has_weight: - self.ops_attr["weight"].add(op_name_type) - for op_cap in op_cap_lst: - if "activation" in op_cap: - self.ops_attr["activation"].add(op_name_type) - attrs_lst = ["activation", "weight"] if has_weight else ["activation"] - for att in attrs_lst: - # Parse the data info for item that has options. - if "activation" in op_cap and "quant_mode" in op_cap["activation"]: - quant_mode = op_cap["activation"]["quant_mode"] - att_dtype = op_cap[att]["dtype"][0] - signed_flag, _data_type = extract_data_type(att_dtype) - for item_name, item_options in op_cap[att].items(): - if item_name == "dtype": - # The dtype should be a string, need to align with fwk.yaml. - self.ops_data_type[op_name_type][(quant_mode, att, _data_type, signed_flag)] = ( - item_options[0] if isinstance(item_options, list) else item_options - ) - if item_name not in ["dtype", "quant_mode"]: - parsed_op_cap[quant_mode][att][_data_type][signed_flag][item_name] = item_options - else: - # Parse the data info for item with unique value. - att_dtype = op_cap[att]["dtype"] - if isinstance(att_dtype, list): - att_dtype = att_dtype[0] - parsed_op_cap["precision"][att][att_dtype] = {"dtype": att_dtype} - self.ops_data_type[op_name_type][("precision", att, att_dtype)] = att_dtype - - parsed_cap[op_name_type] = parsed_op_cap - return parsed_cap - - def _create_tuning_space(self, capability, usr_cfg): - """Create tuning space. - - steo1. convert the capability into internal format. - step2. merge the capability with usr_cfg - step3. create the tuning space - :param capability: - :param usr_cfg: - :return: - """ - capability["op"] = self._parse_cap_helper(deepcopy(capability["op"])) - if usr_cfg: - self._merge_with_user_cfg(capability, usr_cfg["quantization"]) - logger.debug("*********** After Merged with user cfg ***********") - logger.debug(capability) - self._parse_capability(capability) - - def query_item_option(self, op_name_type, path, method_name, method_val): - """Query the method value, such as scheme, algorithm. - - Args: - op_name_type: (op_name, op_type) - path: full path - method_name: method name - method_val: method value - - Returns: - Return the query result if exist. - """ - mode_item = self.get_item_by_path((op_name_type, *path)) - if not mode_item: - return None - method_item = mode_item.get_option_by_name(method_name) - return method_item is not None and method_val in method_item.options - - def get_default_config(self, op_name_type, quant_mode): - """Get the default tuning config. - - Args: - op_name_type: (op_name, op_type) - quant_mode: quantization mode. - - Returns: - op_tuning_config: the default config according to the specified quantization mode. - """ - from .tuning_structs import OpTuningConfig - - # For quant_mode static/dynamic/((static, int8), (dynamic, int4)) - # set the first option as the default if the not support the required quant mode - full_path = self.get_op_default_path_by_pattern(op_name_type, quant_mode) - config_args = {} - has_weight = op_name_type in self.ops_attr["weight"] - config_args["activation_dtype"] = self.ops_data_type[op_name_type].get(full_path["activation"]) - if has_weight: - config_args["weight_dtype"] = self.ops_data_type[op_name_type].get(full_path["weight"]) - for att in full_path: - mode_item = self.query_quant_mode_item_by_full_path(op_name_type, full_path[att]) - if mode_item: - method_args = { - method_item.name: method_item.options[0] - for method_item in mode_item.options - if method_item.name in TUNING_ITEMS_LST - } - config_args.update(method_args) - - quant_mode = quant_mode if isinstance(quant_mode, str) else quant_mode[0] - # set the first option as the default for each tuning item - op_tuning_config = OpTuningConfig(op_name_type[0], op_name_type[1], quant_mode, self, kwargs=config_args) - return op_tuning_config - - def get_item_by_path(self, path, default=None): - """Get the item according to the path.""" - item = self.root_item - for val in path: - if item is None: - logger.warning(f"Did not found the item according to the path {path}") - return default - item = item.get_option_by_name(val) - if item is None: - logger.warning(f"Did not found the item according to the path {path}") - return item - - def get_default_full_path(self, op_name_type, path): - """Complete the path. - - Args: - op_name_type: (op_name, op_path) - path: incomplete path. - - Returns: - new_path: the complete path. - """ - # For precision - if path[0] == "precision": - # If the path is ('precision', 'activation', dtype), return it directly. - if len(path) == 3: - return path - assert len(path) == 2, f"Got the path: {path}, please provide the path include activation or weight." - att_item = self.get_item_by_path((op_name_type, *path)) - if not att_item or len(att_item.options) == 0: - logger.debug(f"Could not found item for {op_name_type} with path {path}") - return None - dtype = att_item.options[0].name - return (*path, dtype) - else: - # For quantization - assert len(path) >= 2, f"Got the path: {path}, please provide the path include activation or weight." - if path[-1] is None: - path = path[:-1] - item = self.get_item_by_path((op_name_type, *path)) - new_path = path - # For path ('static', 'activation', ...) - while item: - item_options = item.options - if ( - len(item_options) > 0 - and isinstance(item_options[0], TuningItem) - and item_options[0].item_type != "method" - ): - new_path = new_path + (item_options[0].name,) - item = item_options[0] - else: - break - return new_path - - def query_quant_mode_item_by_full_path(self, op_name_type, path) -> Tuple[TuningItem, Tuple]: - """Query the mode item by full path.""" - new_path = (op_name_type, *path) - item = self.get_item_by_path(new_path) - return item - - def query_items_by_quant_mode(self, quant_mode): - """Collect all op items that support the specified mode. - - Args: - quant_mode: dynamic/static/bf16/fp32/fp16 - - Returns: - The op item set that support quant model. - """ - return self.quant_mode_wise_items.get(quant_mode, []) - - def get_op_default_path_by_pattern(self, op_name_type, pattern): - """Get the default path by quant mode. - - Args: - op_name_type: (op_name, op_type) - pattern: 'static', 'dynamic', ('static', 'int8'), ('precision', 'fp32') - - Returns: - result(Dict): The default full path of activation and weight if have. - """ - internal_pattern = pattern_to_internal(pattern) - full_path = {"activation": None, "weight": None} - full_path["activation"], full_path["weight"] = pattern_to_path(internal_pattern) - result = {} - has_weight = op_name_type in self.ops_attr["weight"] - att_lst = ["activation", "weight"] if has_weight else ["activation"] - for att in att_lst: - result[att] = self.get_default_full_path(op_name_type, full_path[att]) - return result - - -@deprecated(version="2.0") -def pattern_to_internal(pattern, default_dtype="int8"): - """Convert pattern to internal format. - - 'static' -> ('static', (('int8'),('int8'))) - 'dynamic' -> ('dynamic', (('int8'),('int8'))) - 'fp32' -> ('precision', (('fp32'), ('fp32'))) - 'bf16' -> ('precision', (('bf16'), ('bf16'))) - ('static', 'int8') -> ('static', (('int8'),('int8'))) - ('dynamic', 'int8') -> ('dynamic', (('int8'),('int8'))) - ('precision', 'fp32') -> ('precision', (('fp32'), ('fp32')))) # (('fp32'), ('fp32')) or ('fp32', 'fp32') - #TODO to add the support for mixed data type of weight and activation - """ - from .constant import PRECISION_SET_V2_0 - - pattern_bk = pattern - if isinstance(pattern, str): - pattern = ("precision", pattern) if pattern in PRECISION_SET_V2_0 else (pattern, (None)) - internal_pattern = (pattern[0], ((pattern[1],), (pattern[1],))) - return internal_pattern - - -@deprecated(version="2.0") -def pattern_to_path(pattern): - """Convert pattern to path.""" - act_path = (pattern[0], "activation", *pattern[1][0]) - weight_path = (pattern[0], "weight", *pattern[1][1]) - return act_path, weight_path - - -@deprecated(version="2.0") -def quant_mode_from_pattern(internal_pattern): - """Get quant mode from internal pattern.""" - if internal_pattern[0] == "precision": - return internal_pattern[1][0] - else: - return internal_pattern[0] - - -@deprecated(version="2.0") -def initial_tuning_cfg_with_quant_mode(op_name_type, quant_mode, tuning_space: TuningSpace) -> OpTuningConfig: - """Initialize the tuning cfg. - - Args: - op_name_type: (op name, op type) - quant_mode: dynamic/static/fp32/bf16/fp16 - tuning_space: tuning space. - - step1, convert the quant_mode into internal format. - step2, complete the path based. - step3, get the mode item. - step4, use the first option as value for method. - step5, create the op tuning config. - - Returns: - The initial tuning config. - """ - internal_pattern = pattern_to_internal(quant_mode) - full_path = {"activation": None, "weight": None} - full_path["activation"], full_path["weight"] = pattern_to_path(internal_pattern) - has_weight = op_name_type in tuning_space.ops_attr["weight"] - - config_args = {} - att_lst = ["activation", "weight"] if has_weight else ["activation"] - for att in att_lst: - att_full_path = tuning_space.get_default_full_path(op_name_type, full_path[att]) - config_args[att + "_dtype"] = tuning_space.ops_data_type[op_name_type].get(att_full_path, None) - mode_item = tuning_space.get_item_by_path((op_name_type, *att_full_path)) - if mode_item: - method_args = { - method_item.name: method_item.options[0] - for method_item in mode_item.options - if method_item.name in TUNING_ITEMS_LST - } - config_args.update(method_args) - quant_mode = internal_pattern[0] - # set the first option as the default for each tuning item - op_tuning_config = OpTuningConfig(op_name_type[0], op_name_type[1], quant_mode, tuning_space, kwargs=config_args) - return op_tuning_config diff --git a/neural_compressor/experimental/strategy/utils/tuning_structs.py b/neural_compressor/experimental/strategy/utils/tuning_structs.py deleted file mode 100644 index 382adb064b5..00000000000 --- a/neural_compressor/experimental/strategy/utils/tuning_structs.py +++ /dev/null @@ -1,108 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Tuning structure.""" -from typing import Dict - -from deprecated import deprecated - -from ....utils import logger -from .constant import PRECISION_SET, QUANT_MODE_SET, TUNING_ITEMS_LST - - -@deprecated(version="2.0") -class OpTuningConfig: - """Op tuning config.""" - - def __init__(self, op_name, op_type, op_quant_mode, tuning_space, kwargs={}): - """Create the tuning config. - - Args: - op_name: op name. - op_type: op type. - op_quant_mode: quantization mode. - tuning_space: tuning space. - kwargs: other parameters. Defaults to {}. - """ - self.op_name = op_name - self.op_type = op_type - self.op_name_type = (self.op_name, self.op_type) - self.op_quant_mode = op_quant_mode # static/dynamic/fp32/bf16/fp16 - self.kwargs = kwargs - self.act_dtype = None - self.weight_dtype = None - self.has_weight = self.op_name_type in tuning_space.ops_attr["weight"] - self._set_dtype() - - def _set_dtype(self): - """Set the date type.""" - if self.op_quant_mode in PRECISION_SET: - self.act_dtype, self.weight_dtype = self.op_quant_mode, self.op_quant_mode - else: - self.act_dtype = self.kwargs.get("activation_dtype", None) - self.weight_dtype = self.kwargs.get("weight_dtype", None) - assert self.act_dtype and isinstance(self.act_dtype, str), ( - f"Didn't assign the activation data type for {self.op_name, self.op_type}", - f"with quant_mode {self.op_quant_mode}", - ) - # if self.has_weight: - # assert self.weight_dtype, \ - # (f"Didn't assign the weight data type for {self.op_name, self.op_type}", \ - # f"with quant_mode {self.op_quant_mode}") - - def __repr__(self) -> str: - """Display the tuning config as string. - - Returns: - msg: the tuning config as string. - """ - msg = f"op name: {self.op_name}, op type : {self.op_type} \n" - msg += f"\t activation dtype: {self.act_dtype} \n" - msg += f"\t weight dtype: {self.weight_dtype} \n" if self.has_weight else "" - for key, val in self.kwargs.items(): - if key in TUNING_ITEMS_LST: - msg += f"\t {key[0]} {key[1]}: {val}\n" - return msg - - def get_state(self): - """Return the op tuning configuration. - - Returns: - Dict: The op tuning state. - """ - result = {} - if self.has_weight: - result["weight"] = { - "dtype": self.weight_dtype, - } - result["activation"] = { - "dtype": self.act_dtype, - "quant_mode": self.op_quant_mode, - } - for key, val in self.kwargs.items(): - if key in TUNING_ITEMS_LST: - result[key[0]][key[1]] = val - return result - - @classmethod - def from_state(cls, config: Dict): - """Create the tuning config from dict. - - Args: - config: A dict includes the tuning config. - """ - cls(**config) diff --git a/neural_compressor/experimental/strategy/utils/utility.py b/neural_compressor/experimental/strategy/utils/utility.py deleted file mode 100644 index 1ca98552924..00000000000 --- a/neural_compressor/experimental/strategy/utils/utility.py +++ /dev/null @@ -1,66 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Tuning utility.""" - - -from collections import OrderedDict - -from deprecated import deprecated - - -@deprecated(version="2.0") -class OrderedDefaultDict(OrderedDict): - """Ordered default dict.""" - - def __missing__(self, key): - """Initialize value for the missing key.""" - self[key] = value = OrderedDefaultDict() - return value - - -@deprecated(version="2.0") -def extract_data_type(data_type: str) -> str: - """Extract data type and signed from data type. - - Args: - data_type: The original data type such as uint8, int8. - - Returns: - (signed or unsigned, data type without signed) - """ - return ("signed", data_type) if data_type[0] != "u" else ("unsigned", data_type[1:]) - - -@deprecated(version="2.0") -def reverted_data_type(signed_flag: str, data_type: str) -> str: - """Revert the data type.""" - return data_type if signed_flag == "signed" else "u" + data_type - - -@deprecated(version="2.0") -def get_adaptor_name(adaptor): - """Get adaptor name. - - Args: - adaptor: adaptor instance. - """ - adaptor_name = type(adaptor).__name__.lower() - adaptor_name_lst = ["onnx", "tensorflow", "pytorch"] - for name in adaptor_name_lst: - if adaptor_name.startswith(name): - return name - return "" diff --git a/neural_compressor/strategy/strategy.py b/neural_compressor/strategy/strategy.py index 60101104e3c..06c5b0d0783 100644 --- a/neural_compressor/strategy/strategy.py +++ b/neural_compressor/strategy/strategy.py @@ -485,7 +485,6 @@ def traverse(self): return self.distributed_traverse() self._setup_pre_tuning_algo_scheduler() self._prepare_tuning() - # import pdb;pdb.set_trace() traverse_start_time = time() for op_tuning_cfg in self.next_tune_cfg(): tuning_start_time = time() diff --git a/neural_compressor/torch/algorithms/habana_fp8/fp8_quant.py b/neural_compressor/torch/algorithms/habana_fp8/fp8_quant.py index 0330bd475ad..c80cc443531 100644 --- a/neural_compressor/torch/algorithms/habana_fp8/fp8_quant.py +++ b/neural_compressor/torch/algorithms/habana_fp8/fp8_quant.py @@ -131,7 +131,6 @@ def input_observer_forward_pre_hook(self, input): ### Insert input observer into model, only for fp8_e4m3 static quantization ### observer_cls = observer_mapping[act_observer] - # import pdb;pdb.set_trace() if isinstance(module, white_list): observer_obj = observer_cls(dtype=dtype_mapping[qconfig.act_dtype]) diff --git a/neural_compressor/torch/algorithms/weight_only/gptq.py b/neural_compressor/torch/algorithms/weight_only/gptq.py index 4cd9918d93d..4c2df596282 100644 --- a/neural_compressor/torch/algorithms/weight_only/gptq.py +++ b/neural_compressor/torch/algorithms/weight_only/gptq.py @@ -27,7 +27,13 @@ import torch.nn as nn from tqdm import tqdm -from neural_compressor.torch.utils import get_accelerator, is_transformers_imported, logger, set_module +from neural_compressor.torch.utils import ( + get_accelerator, + get_model_device, + is_transformers_imported, + logger, + set_module, +) from neural_compressor.torch.utils.auto_accelerator import auto_detect_accelerator from .modules import WeightOnlyLinear @@ -995,6 +1001,7 @@ def prepare( if use_layer_wise: # pragma: no cover assert model_path is not None, "model_path should not be None when use layer wise mode" + self.model_device = get_model_device(model) # return model on the same device self.gptq_quantizer = RAWGPTQuantizer( model, weight_config=self.quant_config, @@ -1013,6 +1020,7 @@ def convert(self, model, *args, **kwargs): self.gptq_quantizer.model = model self.gptq_quantizer.remove_prepare_for_calibration() q_model, gptq_config = self.gptq_quantizer.execute_quantization() + q_model = q_model.to(self.model_device) q_model.gptq_config = gptq_config logger.info("GPTQ quantizing done.") return q_model diff --git a/neural_compressor/torch/algorithms/weight_only/modules.py b/neural_compressor/torch/algorithms/weight_only/modules.py index 18cf6e46e55..dcb2ff421f4 100644 --- a/neural_compressor/torch/algorithms/weight_only/modules.py +++ b/neural_compressor/torch/algorithms/weight_only/modules.py @@ -270,8 +270,8 @@ def recover(self): def pack_tensor_with_torch(self, raw_tensor): target_len = math.ceil(raw_tensor.shape[1] / self.n_pack) - packed_tensor = torch.zeros(raw_tensor.shape[0], target_len, dtype=self.compression_dtype).to(self.device) - mask = torch.tensor(2**self.bits - 1, dtype=self.compression_dtype).to(self.device) + packed_tensor = torch.zeros(raw_tensor.shape[0], target_len, dtype=self.compression_dtype).to(raw_tensor.device) + mask = torch.tensor(2**self.bits - 1, dtype=self.compression_dtype).to(raw_tensor.device) for j in range(packed_tensor.shape[1]): start = self.n_pack * j end = self.n_pack * (j + 1) @@ -286,8 +286,8 @@ def pack_tensor_with_torch(self, raw_tensor): def unpack_tensor_with_torch(self, packed_tensor): target_dtype = torch.int8 if not hasattr(self, "qzeros") or "int" not in self.dtype else torch.uint8 target_len = packed_tensor.shape[1] * self.n_pack - unpacked_tensor = torch.zeros(packed_tensor.shape[0], target_len, dtype=target_dtype).to(self.device) - mask = torch.tensor(2**self.bits - 1, dtype=self.compression_dtype).to(self.device) + unpacked_tensor = torch.zeros(packed_tensor.shape[0], target_len, dtype=target_dtype).to(packed_tensor.device) + mask = torch.tensor(2**self.bits - 1, dtype=self.compression_dtype).to(packed_tensor.device) for j in range(packed_tensor.shape[1]): for e in range(self.n_pack): index = j * self.n_pack + e @@ -338,13 +338,13 @@ def unpack_tensor_with_numpy(self, packed_tensor): return unpacked_tensor def pack_tensor(self, raw_tensor): - if "cuda" in self.device: + if "cuda" in raw_tensor.device.type: return self.pack_tensor_with_torch(raw_tensor) else: return self.pack_tensor_with_numpy(raw_tensor) def unpack_tensor(self, packed_tensor): - if "cuda" in self.device: + if "cuda" in packed_tensor.device.type: return self.unpack_tensor_with_torch(packed_tensor) else: return self.unpack_tensor_with_numpy(packed_tensor) diff --git a/neural_compressor/torch/algorithms/weight_only/rtn.py b/neural_compressor/torch/algorithms/weight_only/rtn.py index ca6f3e499e5..6a95bec4550 100644 --- a/neural_compressor/torch/algorithms/weight_only/rtn.py +++ b/neural_compressor/torch/algorithms/weight_only/rtn.py @@ -28,6 +28,7 @@ from neural_compressor.torch.utils import ( get_accelerator, get_attr, + get_model_device, is_transformers_imported, logger, set_attr, @@ -99,10 +100,7 @@ def convert( """ weight_config = self.quant_config device = get_accelerator(kwargs.pop("device", "auto")).current_device_name() - - # Put model on device explicitly - # TODO: refine it later, Put module on device one by one instead of the whole model - model.to(device) + model_device = get_model_device(model) # return model on the same device # for transformers model. If lm_head is tied from embedding, we deepcopy it. if quant_lm_head and getattr(getattr(model, "config", None), "tie_word_embeddings", False): @@ -132,6 +130,8 @@ def convert( dtype = weight_config[name].get("dtype", "int") if dtype == "fp32": continue + # Move modules to the accelerator device layer-by-layer + m.to(device) ### FP8 cast part if dtype in ["fp8_e5m2", "fp8_e5m2fnuz", "fp8_e4m3fn", "fp8_e4m3fnuz"]: logger.debug("Cast module {} to FP8 using qdq mode, no scaling".format(name)) @@ -223,4 +223,8 @@ def convert( return new_module else: set_module(model, name, new_module) + # Move modules back to the model device layer-by-layer + m.to(model_device) + new_module.to(model_device) + model.to(model_device) return model diff --git a/neural_compressor/torch/algorithms/weight_only/teq.py b/neural_compressor/torch/algorithms/weight_only/teq.py index 9f2e8fc8dce..9783d913070 100644 --- a/neural_compressor/torch/algorithms/weight_only/teq.py +++ b/neural_compressor/torch/algorithms/weight_only/teq.py @@ -16,8 +16,7 @@ # limitations under the License. # -import copy -from typing import Any +from typing import Any, List import torch @@ -36,10 +35,10 @@ class TrainableEquivalentTransformation: """Weight-only quantization, Trainable Equivalent Transformation (TEQ).""" - _PREPARE_ATTRS: list[str] = ["weight_config", "trained_alphas"] + _PREPARE_ATTRS: List[str] = ["weight_config", "trained_alphas"] _PREPARE_ATTRS_PREFIX = "_prepare_" - def __init__(self, model, weight_config={}, absorb_to_layer={}, folding=True, example_inputs=None): + def __init__(self, model, weight_config={}, absorb_to_layer=None, folding=True, example_inputs=None): """ :param model: the model for quantization :param weight_config (dict, optional): contains all info required by RTN. Defaults to {}. @@ -54,6 +53,24 @@ def __init__(self, model, weight_config={}, absorb_to_layer={}, folding=True, ex self.absorb_to_layer = absorb_to_layer self._post_initialized = False + def _detect_absorb_to_layer(self, model, folding, example_inputs): + # If user not provide the layers to absorb the quantization, detect layers automatically + supported_layers = ["Linear"] + detected_absorb_layers = {} + # Detect the layers that can be absorbed automatically + if folding: + from neural_compressor.torch.algorithms.weight_only.utility import GraphTrace + + tg = GraphTrace() + detected_absorb_layers, _ = tg.get_absorb_to_layer(model, example_inputs, supported_layers) + else: # pragma: no cover + for name, module in model.named_modules(): + if module.__class__.__name__ in supported_layers: + detected_absorb_layers[name] = [name] + logger.info("Detected **absorb layer**: **absorbed layers**") + logger.info(detected_absorb_layers) + return detected_absorb_layers + def _post_init(self): self.dtype = self._get_dtype() self.model.to(self.device) @@ -75,6 +92,8 @@ def add_tuning_scale(self, sqrt_w_init=False): to the paper for more details :param sqrt_w_init: use sqrt weight to init.""" + if not self.absorb_to_layer: + self.absorb_to_layer = self._detect_absorb_to_layer(self.model, self.folding, self.example_inputs) if not self._post_initialized: self._post_init() # freeze model. @@ -104,7 +123,7 @@ def add_tuning_scale(self, sqrt_w_init=False): self.trained_alphas[layer_norm] = alpha for layer_name in self.absorb_to_layer[layer_norm]: - if self.weight_config.get(layer_name) is None: # pragma: no cover + if not self.weight_config.get(layer_name): # pragma: no cover logger.info(f"layer {layer_name} not in weight config, skip.") continue num_bits = self.weight_config[layer_name]["bits"] @@ -117,10 +136,10 @@ def add_tuning_scale(self, sqrt_w_init=False): ) set_module(self.model, layer_name, wrapper_module) - for n, m in self.model.named_modules(): + for layer_name, m in self.model.named_modules(): if isinstance(m, torch.nn.Linear) and "orig_layer" not in n: - if self.weight_config.get(n) is None: # pragma: no cover - logger.info(f"out of absorbed layer {n} not in weight config, skip.") + if not self.weight_config.get(layer_name): # pragma: no cover + logger.info(f"out of absorbed layer {layer_name} not in weight config, skip.") continue num_bits = self.weight_config[layer_name]["bits"] group_size = self.weight_config[layer_name]["group_size"] @@ -131,7 +150,7 @@ def add_tuning_scale(self, sqrt_w_init=False): wrapper_module = TEQLinearFakeQuant( orig_layer=m, alpha=alpha, num_bits=num_bits, group_size=group_size, scheme=scheme ) - set_module(self.model, n, wrapper_module) + set_module(self.model, layer_name, wrapper_module) # Attach the weight config captured at prepare stage to the model self.model._weight_config = self.weight_config self.model._trained_alphas = self.trained_alphas @@ -190,7 +209,9 @@ def _absorb_scales(self, layer, scale, layer_name=""): scale = scale.view(scale.shape[0], 1) layer.weight *= scale - elif layer.__class__.__name__ == "LlamaRMSNorm" or layer.__class__.__name__ == "T5LayerNorm": ##quite tricky + elif ( + layer.__class__.__name__ == "LlamaRMSNorm" or layer.__class__.__name__ == "T5LayerNorm" + ): # pragma: no cover layer.weight *= scale else: # pragma: no cover @@ -222,7 +243,7 @@ def _scale_layer_weight(self, layer, scale): ##input channel @torch.no_grad() def transform(self): """Apply alpha/scale.""" - if not self._post_initialized: + if not self._post_initialized: # pragma: no cover self._post_init() for ln_name, layer_names in self.absorb_to_layer.items(): module = get_module(self.model, ln_name) @@ -272,7 +293,7 @@ def save(self, save_scale_file="", save_state_dict_file=""): class TEQuantizer(Quantizer): - def __init__(self, quant_config, folding, absorb_to_layer, example_inputs): + def __init__(self, quant_config, folding, example_inputs, absorb_to_layer=None): super().__init__(quant_config=quant_config) self.folding = folding self.absorb_to_layer = absorb_to_layer diff --git a/neural_compressor/torch/utils/utility.py b/neural_compressor/torch/utils/utility.py index 5d9a03d106b..bf1bb2a77b1 100644 --- a/neural_compressor/torch/utils/utility.py +++ b/neural_compressor/torch/utils/utility.py @@ -222,3 +222,16 @@ def dump_model_op_stats(mode, tune_cfg): output_data.append(field_results) Statistics(output_data, header="Mixed Precision Statistics", field_names=field_names).print_stat() + + +def get_model_device(model: torch.nn.Module): + """Get the device. + + Args: + model (torch.nn.Module): the input model. + + Returns: + device (str): a string. + """ + for n, p in model.named_parameters(): + return p.data.device.type # p.data.device == device(type='cpu') diff --git a/neural_compressor/utils/load_huggingface.py b/neural_compressor/utils/load_huggingface.py index 6ce30eec99a..6671640722d 100644 --- a/neural_compressor/utils/load_huggingface.py +++ b/neural_compressor/utils/load_huggingface.py @@ -18,6 +18,7 @@ import copy import os +import sys import torch import transformers diff --git a/neural_compressor/utils/options.py b/neural_compressor/utils/options.py index 12f959e8e4e..0746f7650a8 100644 --- a/neural_compressor/utils/options.py +++ b/neural_compressor/utils/options.py @@ -16,7 +16,7 @@ # limitations under the License. """ONNX options.""" -from ..conf.dotdict import DotDict +from .utility import DotDict class onnxrt: diff --git a/neural_compressor/utils/pytorch.py b/neural_compressor/utils/pytorch.py index bd053c20388..83c260c5a13 100644 --- a/neural_compressor/utils/pytorch.py +++ b/neural_compressor/utils/pytorch.py @@ -18,6 +18,7 @@ import json import os +import sys import torch import torch.quantization as tq @@ -25,6 +26,8 @@ from packaging.version import Version from torch.quantization import convert +import neural_compressor.utils as inc_utils + from ..adaptor.pytorch import ( PyTorch_FXAdaptor, _cfg_to_qconfig, @@ -35,6 +38,13 @@ from ..adaptor.torch_utils import util from . import logger +# In 1.x, the `best_configure` is an instance of `neural_compressor.conf.dotdict.DotDict`, +# which was removed since 2.x. This configuration was saved directly in the model's state_dict. +# During loading, the pickle requires `neural_compressor.conf.dotdict.DotDict` to deserialize the `best_configure`. +# In 2.x, the `DotDict` was moved to `neural_compressor.utils.utility`. Create an alias to make the pickle import work. +# https://stackoverflow.com/a/13398680/23445462 +sys.modules["neural_compressor.conf.dotdict"] = inc_utils.utility + yaml.SafeLoader.add_constructor( "tag:yaml.org,2002:python/tuple", lambda loader, node: tuple(loader.construct_sequence(node)) ) diff --git a/neural_compressor/utils/utility.py b/neural_compressor/utils/utility.py index ca915c36fce..34d15105fbd 100644 --- a/neural_compressor/utils/utility.py +++ b/neural_compressor/utils/utility.py @@ -659,6 +659,43 @@ def dump_class_attrs(obj, result={}): result[obj_name][attr] = value +from functools import reduce + + +def deep_get(dictionary, keys, default=None): + """Get the dot key's item in nested dict. + + For example, person = {'person':{'name':{'first':'John'}}} + deep_get(person, "person.name.first") will output 'John'. + + Args: + dictionary (dict): The dict object to get keys + keys (dict): The deep keys + default (object): The return item if key not exists + Returns: + item: the item of the deep dot keys + """ + return reduce(lambda d, key: d.get(key, default) if isinstance(d, dict) else default, keys.split("."), dictionary) + + +def deep_set(dictionary, keys, value): + """Set the dot key's item in nested dict. + + For example, person = {'person':{'name':{'first':'John'}}} + deep_set(person, "person.sex", 'male') will output + {'person': {'name': {'first': 'John'}, 'sex': 'male'}} + + Args: + dictionary (dict): The dict object to get keys + keys (dict): The deep keys + value (object): The value of the setting key + """ + keys = keys.split(".") + for key in keys[:-1]: + dictionary = dictionary.setdefault(key, DotDict()) + dictionary[keys[-1]] = value + + class DotDict(dict): """Access yaml using attributes instead of using the dictionary notation. diff --git a/test/3x/torch/algorithms/weight_only/test_teq_quantizer.py b/test/3x/torch/algorithms/weight_only/test_teq_quantizer.py index a27ce5ec0f2..4e06cedb284 100644 --- a/test/3x/torch/algorithms/weight_only/test_teq_quantizer.py +++ b/test/3x/torch/algorithms/weight_only/test_teq_quantizer.py @@ -82,8 +82,21 @@ def setUpClass(self): ) self.gptj.seqlen = 512 - def train_func(self): - pass + def test_teq_detect_absorb_layers(self): + example_inputs = torch.ones([1, 512], dtype=torch.long) + test_input = torch.ones([1, 512], dtype=torch.long) + model = copy.deepcopy(self.gptj) + out0 = model(test_input) + + weight_config = { + # 'op_name': (bit, group_size, scheme) + "transformer.h.0.mlp.fc_in": {"bits": 8, "group_size": -1, "scheme": "sym"}, + "transformer.h.0.mlp.fc_out": {"bits": 4, "group_size": 32, "scheme": "asym"}, + } + quantizer = TEQuantizer(quant_config=weight_config, folding=True, example_inputs=example_inputs) + model = quantizer.quantize(copy.deepcopy(self.gptj), run_fn=train) + out1 = model(test_input) + self.assertTrue(torch.allclose(out1[0], out0[0], atol=0.03)) def test_teq(self): example_inputs = torch.ones([1, 512], dtype=torch.long) diff --git a/test/3x/torch/quantization/weight_only/test_gptq.py b/test/3x/torch/quantization/weight_only/test_gptq.py index 7fbe0ad7737..dfbc39c25e7 100644 --- a/test/3x/torch/quantization/weight_only/test_gptq.py +++ b/test/3x/torch/quantization/weight_only/test_gptq.py @@ -36,6 +36,20 @@ def setup_class(self): def teardown_class(self): shutil.rmtree("saved_results", ignore_errors=True) + @pytest.mark.skipif(device == "cpu", reason="no available accelerator") + def test_auto_host2device(self): + # if model is on CPU, we move it to device layer-by-layer for acceleration, + # and then move it back to CPU after quantization. + model = copy.deepcopy(self.tiny_gptj).to("cpu") + example_inputs = copy.deepcopy(self.example_inputs).to("cpu") + quant_config = get_default_gptq_config() + model = prepare(model, quant_config) + run_fn(model) + model = convert(model) + gptq_label = model(example_inputs)[0] + gptq_atol = (gptq_label - self.label.to("cpu")).amax() + assert gptq_atol < 0.06, "GPTQ should have low atol." + def test_accuracy_improvement(self): # test_default_rtn_config model = copy.deepcopy(self.tiny_gptj) @@ -215,9 +229,9 @@ def test_conv1d(self): from transformers import GPT2Model, GPT2Tokenizer tokenizer = GPT2Tokenizer.from_pretrained("sshleifer/tiny-gpt2") - model = GPT2Model.from_pretrained("sshleifer/tiny-gpt2") + model = GPT2Model.from_pretrained("sshleifer/tiny-gpt2").to(device) text = "Replace me by any text you'd like." - encoded_input = tokenizer(text, return_tensors="pt") + encoded_input = tokenizer(text, return_tensors="pt").to(device) def run_fn_conv1d(model): model(**encoded_input) diff --git a/test/3x/torch/quantization/weight_only/test_rtn.py b/test/3x/torch/quantization/weight_only/test_rtn.py index 206bd20aa10..04f6c444485 100644 --- a/test/3x/torch/quantization/weight_only/test_rtn.py +++ b/test/3x/torch/quantization/weight_only/test_rtn.py @@ -352,3 +352,16 @@ def mock_is_transformers_imported(): model = convert(model) out = model(self.example_inputs)[0] assert torch.allclose(out, self.label, atol=1e-1), "Accuracy gap atol > 0.1 is unexpected." + + @pytest.mark.skipif(device == "cpu", reason="no available accelerator") + def test_auto_host2device(self): + # if model is on CPU, we move it to device layer-by-layer for acceleration, + # and then move it back to CPU after quantization. + model = copy.deepcopy(self.tiny_gptj).to("cpu") + example_inputs = copy.deepcopy(self.example_inputs).to("cpu") + quant_config = get_default_rtn_config() + model = prepare(model, quant_config) + model = convert(model) + rtn_label = model(example_inputs)[0] + rtn_atol = (rtn_label - self.label.to("cpu")).amax() + assert rtn_atol < 0.08, "RTN should have low atol." diff --git a/test/adaptor/mxnet_adaptor/test_adaptor_mxnet.py b/test/adaptor/mxnet_adaptor/test_adaptor_mxnet.py deleted file mode 100644 index b576a1f6eb0..00000000000 --- a/test/adaptor/mxnet_adaptor/test_adaptor_mxnet.py +++ /dev/null @@ -1,299 +0,0 @@ -import json -import os -import platform -import shutil -import sys -import unittest -from pathlib import Path -from tempfile import TemporaryDirectory - -import mxnet as mx -import mxnet.gluon.nn as nn -import numpy as np -import yaml - -from neural_compressor.adaptor.mxnet_utils.util import check_mx_version -from neural_compressor.experimental import Quantization, common -from neural_compressor.experimental.metric.metric import MXNetMetrics -from neural_compressor.utils.utility import recover - -WORKSPACE_DIR = Path("./saved") - -MX_NAMESPACE = mx.np if check_mx_version("2.0.0") else mx.nd - - -def build_mxnet(): - fake_yaml = """ - model: - name: imagenet - framework: mxnet - - evaluation: - accuracy: - metric: - topk: 1 - - tuning: - accuracy_criterion: - relative: 0.01 - exit_policy: - timeout: 0 - random_seed: 9527 - workspace: - path: {} - """.format( - str(WORKSPACE_DIR) - ) - configs = yaml.load(fake_yaml, Loader=yaml.SafeLoader) - with open("mxnet.yaml", "w", encoding="utf-8") as f: - yaml.dump(configs, f) - f.close() - - -def build_mxnet_kl(): - fake_yaml = """ - model: - name: imagenet - framework: mxnet - - quantization: - model_wise: - activation: - algorithm: kl - - tuning: - accuracy_criterion: - relative: 0.01 - exit_policy: - timeout: 0 - random_seed: 9527 - workspace: - path: {} - """.format( - str(WORKSPACE_DIR) - ) - configs = yaml.load(fake_yaml, Loader=yaml.SafeLoader) - with open("mxnet_kl.yaml", "w", encoding="utf-8") as f: - yaml.dump(configs, f) - f.close() - - -def are_models_equal(tester, model_a, model_b): - symnet_a, args_a, auxs_a = model_a - symnet_b, args_b, auxs_b = model_b - - nodes_a = [(node["op"], node["inputs"]) for node in json.loads(symnet_a.tojson())["nodes"]] - nodes_b = [(node["op"], node["inputs"]) for node in json.loads(symnet_b.tojson())["nodes"]] - tester.assertEqual(nodes_a, nodes_b) - - args_a = dict(sorted(args_a.items(), key=lambda x: x[0])) - args_b = dict(sorted(args_b.items(), key=lambda x: x[0])) - auxs_a = dict(sorted(auxs_a.items(), key=lambda x: x[0])) - auxs_b = dict(sorted(auxs_b.items(), key=lambda x: x[0])) - - assert len(args_a) == len(args_b) - for val_a, val_b in zip(args_a.values(), args_b.values()): - tester.assertTrue(np.all((val_a == val_b).asnumpy())) - - assert len(auxs_a) == len(auxs_b) - for val_a, val_b in zip(auxs_a.values(), auxs_b.values()): - tester.assertTrue(np.all((val_a == val_b).asnumpy())) - - -class TestAdaptorMXNet(unittest.TestCase): - """Test MXNet adaptor functions.""" - - @classmethod - def setUpClass(self): - if platform.system().lower() == "windows": - self.skipTest(self, "not support mxnet on windows yet") - build_mxnet() - build_mxnet_kl() - - self.data_low = -1000 - self.data_high = 1000 - - @classmethod - def tearDownClass(self): - os.remove("mxnet.yaml") - os.remove("mxnet_kl.yaml") - shutil.rmtree(WORKSPACE_DIR, ignore_errors=True) - shutil.rmtree("runs", ignore_errors=True) - - def test_utils(self): - import neural_compressor.adaptor.mxnet_utils.util as utils - - self.assertTrue(utils.isiterable([1, 2, 3])) - self.assertFalse(utils.isiterable(123)) - - def test_mlp_model_quantization(self): - """Use MLP model to test minmax calibration and built-in evaluate function.""" - mlp_input = mx.symbol.Variable("data") - mlp_model = mx.symbol.FullyConnected(data=mlp_input, name="fc1", num_hidden=32) - mlp_model = mx.symbol.Activation(data=mlp_model, act_type="relu") - mlp_model = mx.symbol.FullyConnected(data=mlp_model, name="fc2", num_hidden=16) - mlp_model = mx.symbol.Softmax(mlp_model, name="softmax") - - for shape in [ - (32, 64), - ]: - data = MX_NAMESPACE.random.uniform(self.data_low, self.data_high, shape).astype("float32") - labels = MX_NAMESPACE.ones((shape[0],)) - calib_data = mx.io.NDArrayIter(data=data, label=labels, batch_size=shape[0]) - - with TemporaryDirectory() as tmpdirname: - prefix = str(Path(tmpdirname) / "tmp") - sym_block = mx.gluon.SymbolBlock(mlp_model, [mlp_input]) - sym_block.initialize() - sym_block.forward(data) - sym_block.export(prefix, epoch=0) - fp32_model = mx.model.load_checkpoint(prefix, 0) - - quantizer = Quantization("./mxnet.yaml") - quantizer.model = fp32_model - quantizer.calib_dataloader = calib_data - quantizer.eval_dataloader = calib_data - qmodel = quantizer.fit() - self.assertIsInstance(qmodel.model[0], mx.symbol.Symbol) - - # test inspect_tensor - inspect_tensor = quantizer.strategy.adaptor.inspect_tensor - quantizer.model = fp32_model - - fc_op_name = "sg_{}_fully_connected".format("onednn" if check_mx_version("2.0.0") else "mkldnn") - fc_node_name1 = fc_op_name + "_eltwise_0" - fc_node_name2 = fc_op_name + "_1" - - insp = inspect_tensor( - quantizer.model, - quantizer.calib_dataloader, - op_list=[fc_node_name1, fc_node_name2], - iteration_list=[1, 3], - ) - qinsp = inspect_tensor( - qmodel, quantizer.calib_dataloader, op_list=[fc_node_name1, fc_node_name2], iteration_list=[1, 3] - ) - - self.assertNotEqual(len(insp["activation"]), 0) - self.assertEqual(len(insp["activation"]), len(qinsp["activation"])) - - for tensors, qtensors in zip(insp["activation"], qinsp["activation"]): - for k in set(tensors.keys()) & set(qtensors.keys()): - tensor, qtensor = tensors[k][k], qtensors[k][k] - self.assertEqual(tensor.shape, qtensor.shape) - - # test inspect with an empty iteration_list - inspect_tensor(qmodel, quantizer.calib_dataloader, op_list=[fc_node_name1], iteration_list=[]) - - # test recovery for symbolic model - qmodel_r = recover(fp32_model, WORKSPACE_DIR / "history.snapshot", -1) - are_models_equal(self, qmodel.model, qmodel_r.model) - - # test symbolic model saving - qmodel_r.save(WORKSPACE_DIR / "save_test") - - def test_conv_model_quantization(self): - """Use Conv model to test KL calibration and user specific evaluate function.""" - conv_net = nn.HybridSequential() - conv_net.add(nn.Conv2D(channels=3, kernel_size=(1, 1))) - conv_net.add(nn.BatchNorm()) - conv_net.add(nn.Activation("relu")) - conv_net.add(nn.AvgPool2D(pool_size=(4, 4))) - conv_net.add(nn.Dense(1, activation="sigmoid")) - conv_net.initialize() - - for shape in [ - (32, 3, 224, 224), - ]: - dataShape = (shape[0] * 5, *shape[1:]) - data = MX_NAMESPACE.random.uniform(self.data_low, self.data_high, dataShape, dtype="float32") - label = MX_NAMESPACE.random.randint(0, 2, (dataShape[0], 1)).astype("float32") - dataset = mx.gluon.data.ArrayDataset(data, label) - - def eval(model): - eval_dataloader = mx.gluon.data.DataLoader(dataset, batch_size=8) - metric = MXNetMetrics().metrics["Accuracy"]() - for batch in eval_dataloader: - data, labels = batch - preds = model.forward(data) - metric.update(labels.asnumpy(), preds.asnumpy()) - return metric.result() - - calib_dataloader = mx.gluon.data.DataLoader(dataset, batch_size=8) - calib_dataloader.batch_size = 8 - quantizer = Quantization("./mxnet_kl.yaml") - quantizer.model = conv_net - quantizer.calib_dataloader = calib_dataloader - quantizer.eval_func = eval - qnet = quantizer.fit().model - self.assertIsInstance(qnet, mx.gluon.HybridBlock) - - def test_gluon_model(self): - """Use gluon model to test gluon related functions in mxnet adaptor.""" - - # create gluon model - def create_model(params=None): - net = nn.HybridSequential() - net.add(nn.Conv2D(1, (1, 1), activation="relu")) - net.add(nn.Flatten()) - net.add(nn.Dense(64, activation="relu")) - net.add(nn.Dense(10)) - if params is not None: - if check_mx_version("2.0.0"): - net.load_dict({k: v.data() for k, v in params.items()}) - else: - param_keys = sorted(net.collect_params().keys()) - param_values = sorted(params.items(), key=lambda x: x[0]) - params = {k: v.data() for k, (old_k, v) in zip(param_keys, param_values)} - net.collect_params().load_dict(params) - else: - net.initialize() - return net - - class CalibDataset: - def __init__(self, dataset): - self.dataset = dataset - - def __getitem__(self, idx): - if check_mx_version("2.0.0"): - mx_namespace = mx.np - else: - mx_namespace = mx.nd - data, label = self.dataset[idx] - data = mx_namespace.reshape(data, (data.shape[-1], *data.shape[:-1])).astype("float32") - return data, label - - def __len__(self): - return len(self.dataset) - - net = create_model() - dataset = CalibDataset(mx.gluon.data.vision.datasets.FashionMNIST(train=False)) - dataloader = common.DataLoader(dataset, batch_size=8) - quantizer = Quantization("./mxnet.yaml") - quantizer.model = net - quantizer.calib_dataloader = dataloader - quantizer.eval_dataloader = dataloader - qnet = quantizer.fit() - self.assertIsInstance(qnet.model, mx.gluon.HybridBlock) - - # test recovery for gluon model - net = create_model(net.collect_params()) - qnet_r = recover(net, WORKSPACE_DIR / "history.snapshot", -1) - - from neural_compressor.adaptor.mxnet_utils.util import prepare_dataloader, prepare_model - - dataloader = prepare_dataloader(qnet, mx.cpu(), quantizer.calib_dataloader) - - # test calling prepare_dataloader for already prepared dataloader - self.assertIs(dataloader, prepare_dataloader(qnet, mx.cpu(), dataloader)) - - model_a = prepare_model(qnet, mx.cpu(), dataloader.input_desc) - model_b = prepare_model(qnet_r, mx.cpu(), dataloader.input_desc) - are_models_equal(self, model_a, model_b) - - # test gluon model saving - qnet_r.save(WORKSPACE_DIR / "save_test") - - -if __name__ == "__main__": - unittest.main() diff --git a/test/adaptor/onnxrt_adaptor/test_adaptor_onnxrt.py b/test/adaptor/onnxrt_adaptor/test_adaptor_onnxrt.py index 8f4b9582433..b85ad84531b 100644 --- a/test/adaptor/onnxrt_adaptor/test_adaptor_onnxrt.py +++ b/test/adaptor/onnxrt_adaptor/test_adaptor_onnxrt.py @@ -17,329 +17,11 @@ from neural_compressor import PostTrainingQuantConfig, quantization, set_workspace from neural_compressor.adaptor import FRAMEWORKS from neural_compressor.adaptor.pytorch import get_torch_version -from neural_compressor.conf.config import conf from neural_compressor.data import DATALOADERS, DataLoader, Datasets -from neural_compressor.experimental import Benchmark, Quantization, common from neural_compressor.model import Model from neural_compressor.utils.utility import recover -def build_static_yaml(): - fake_yaml = """ - model: - name: imagenet - framework: onnxrt_qlinearops - - quantization: - approach: post_training_static_quant - calibration: - sampling_size: 50 - op_wise: { - 'Gather_*': { - 'activation': {'dtype': ['fp32'], 'scheme':['sym']}, - 'weight': {'dtype': ['fp32'], 'scheme':['sym']} - } - } - - evaluation: - accuracy: - metric: - MSE: - compare_label: False - - tuning: - accuracy_criterion: - relative: 0.01 - exit_policy: - timeout: 0 - random_seed: 9527 - workspace: - path: ./nc_workspace/recover/ - """ - with open("qlinear.yaml", "w", encoding="utf-8") as f: - f.write(fake_yaml) - - fake_yaml = """ - model: - name: imagenet - framework: onnxrt_qdq - - quantization: - approach: post_training_static_quant - calibration: - sampling_size: 50 - op_wise: { - 'Gather_*': { - 'activation': {'dtype': ['fp32'], 'scheme':['sym']}, - 'weight': {'dtype': ['fp32'], 'scheme':['sym']} - } - } - - evaluation: - accuracy: - metric: - MSE: - compare_label: False - - tuning: - accuracy_criterion: - relative: 0.01 - exit_policy: - timeout: 0 - random_seed: 9527 - workspace: - path: ./nc_workspace/recover/ - """ - with open("qdq.yaml", "w", encoding="utf-8") as f: - f.write(fake_yaml) - - -def build_benchmark_yaml(): - fake_yaml = """ - model: - name: imagenet - framework: onnxrt_qlinearops - - evaluation: - performance: - warmup: 1 - iteration: 10 - configs: - num_of_instance: 1 - dataloader: - batch_size: 1 - dataset: - ImageFolder: - root: /path/to/evaluation/dataset/ - accuracy: - metric: - topk: 1 - - tuning: - accuracy_criterion: - relative: 0.01 - exit_policy: - timeout: 0 - random_seed: 9527 - """ - with open("benchmark.yaml", "w", encoding="utf-8") as f: - f.write(fake_yaml) - - -def build_dynamic_yaml(): - fake_yaml = """ - model: - name: imagenet - framework: onnxrt_integerops - - quantization: - approach: post_training_dynamic_quant - calibration: - sampling_size: 50 - - evaluation: - accuracy: - metric: - MSE: - compare_label: False - - tuning: - accuracy_criterion: - relative: 0.01 - exit_policy: - timeout: 0 - random_seed: 9527 - workspace: - path: ./nc_workspace/recover/ - - """ - with open("dynamic.yaml", "w", encoding="utf-8") as f: - f.write(fake_yaml) - - -def build_recipe_yaml(): - fake_yaml = """ - model: - name: imagenet - framework: onnxrt_qlinearops - - quantization: - approach: post_training_static_quant - recipes: - first_conv_or_matmul_quantization: False - last_conv_or_matmul_quantization: False - calibration: - sampling_size: 1 - dataloader: - dataset: - dummy_v2: - input_shape: [100, 4] - - evaluation: - accuracy: - metric: - MSE: - compare_label: False - dataloader: - dataset: - dummy_v2: - input_shape: [100, 4] - - tuning: - accuracy_criterion: - relative: -0.01 - exit_policy: - timeout: 0 - random_seed: 9527 - """ - with open("recipe.yaml", "w", encoding="utf-8") as f: - f.write(fake_yaml) - - -def build_recipe2_yaml(): - fake_yaml = """ - model: - name: imagenet - framework: onnxrt_qlinearops - - quantization: - approach: post_training_static_quant - recipes: - last_conv_or_matmul_quantization: False - pre_post_process_quantization: False - calibration: - sampling_size: 1 - dataloader: - dataset: - dummy_v2: - input_shape: [100, 4] - - evaluation: - accuracy: - metric: - MSE: - compare_label: False - dataloader: - dataset: - dummy_v2: - input_shape: [100, 4] - - tuning: - accuracy_criterion: - relative: -0.01 - exit_policy: - timeout: 0 - random_seed: 9527 - """ - with open("recipe2.yaml", "w", encoding="utf-8") as f: - f.write(fake_yaml) - - -def build_gather_yaml(): - fake_yaml = """ - model: - name: imagenet - framework: onnxrt_qlinearops - - quantization: - approach: post_training_static_quant - calibration: - sampling_size: 1 - dataloader: - batch_size: 1 - dataset: - dummy_v2: - input_shape: [100, 4] - - evaluation: - accuracy: - metric: - MSE: - compare_label: False - dataloader: - batch_size: 1 - dataset: - dummy_v2: - input_shape: [100, 4] - - tuning: - accuracy_criterion: - relative: -0.01 - exit_policy: - timeout: 0 - random_seed: 9527 - """ - with open("gather.yaml", "w", encoding="utf-8") as f: - f.write(fake_yaml) - - -def build_rename_yaml(): - fake_yaml = """ - model: - name: test - framework: onnxrt_integerops - - quantization: - approach: post_training_dynamic_quant - calibration: - sampling_size: 1 - - evaluation: - accuracy: - metric: - Accuracy: {} - - tuning: - accuracy_criterion: - relative: 0.01 - exit_policy: - timeout: 0 - random_seed: 9527 - """ - with open("rename.yaml", "w", encoding="utf-8") as f: - f.write(fake_yaml) - - -def build_non_MSE_yaml(): - fake_yaml = """ - model: - name: imagenet - framework: onnxrt_qlinearops - - quantization: - approach: post_training_static_quant - calibration: - sampling_size: 50 - op_wise: { - 'Gather_*': { - 'activation': {'dtype': ['fp32'], 'scheme':['sym']}, - 'weight': {'dtype': ['fp32'], 'scheme':['sym']} - } - } - - evaluation: - accuracy: - metric: - MSE: - compare_label: False - performance: - warmup: 5 - iteration: 10 - - tuning: - accuracy_criterion: - relative: 0.1 - exit_policy: - timeout: 0 - random_seed: 9527 - workspace: - path: ./nc_workspace/recover/ - - """ - with open("non_MSE.yaml", "w", encoding="utf-8") as f: - f.write(fake_yaml) - - def eval_func(model): return 1.0 @@ -696,50 +378,6 @@ def build_model_share_init(): return model -def build_benchmark(): - seq = """ -from neural_compressor.experimental import Benchmark -from neural_compressor.data import Datasets, DATALOADERS -from neural_compressor import conf -from onnx import onnx_pb as onnx_proto -from onnx import helper, TensorProto, numpy_helper -from onnxruntime_extensions import onnx_op -import numpy as np - -@onnx_op(op_type="PyReverseMatrix") -def reverse_matrix(x): - # The user custom op implementation here. - return np.flip(x, axis=0).astype(np.float32) - -nodes = [] -nodes[0:] = [helper.make_node('Identity', ['input_1'], ['identity1'])] -nodes[1:] = [helper.make_node('PyReverseMatrix', - ['identity1'], ['reversed'], - domain='ai.onnx.contrib')] - -input0 = helper.make_tensor_value_info( - 'input_1', onnx_proto.TensorProto.FLOAT, [None, 2]) -output0 = helper.make_tensor_value_info( - 'reversed', onnx_proto.TensorProto.FLOAT, [None, 2]) - -graph = helper.make_graph(nodes, 'test0', [input0], [output0]) -model = helper.make_model(graph, **{'opset_imports': [helper.make_opsetid('', 13)]}) - -datasets = Datasets('onnxrt_qlinearops') -ext_dataset = datasets['dummy'](shape=(10, 2), low=0., high=1., label=True) -ext_dataloader = DATALOADERS['onnxrt_qlinearops'](ext_dataset) - -conf.model.framework = 'onnxrt_qlinearops' -conf.evaluation.accuracy.metric = {'Accuracy': {}} -evaluator = Benchmark(conf) -evaluator.b_dataloader = ext_dataloader -evaluator.model = model -evaluator('performance') - """ - with open("benchmark.py", "w", encoding="utf-8") as f: - f.writelines(seq) - - class MatmulDataset: def __init__(self): self.data = [] @@ -869,14 +507,6 @@ class TestAdaptorONNXRT(unittest.TestCase): @classmethod def setUpClass(self): - build_rename_yaml() - build_static_yaml() - build_dynamic_yaml() - build_gather_yaml() - build_non_MSE_yaml() - build_benchmark_yaml() - build_recipe_yaml() - build_recipe2_yaml() export_onnx_cv_model(self.mb_v2_model, self.mb_v2_export_path, 13) self.mb_v2_model = onnx.load(self.mb_v2_export_path) export_onnx_cv_model(self.rn50_model, self.rn50_export_path, 12) @@ -898,21 +528,10 @@ def setUpClass(self): self.distilbert_model = onnx.load(self.distilbert_export_path) self.albert_model = onnx.load(self.albert_export_path) self.gather_matmul_model = build_matmul_gather_model() - build_benchmark() set_workspace("nc_workspace") @classmethod def tearDownClass(self): - os.remove("qlinear.yaml") - os.remove("qdq.yaml") - os.remove("recipe.yaml") - os.remove("recipe2.yaml") - os.remove("dynamic.yaml") - os.remove("non_MSE.yaml") - os.remove("benchmark.yaml") - os.remove("gather.yaml") - os.remove("rename.yaml") - os.remove("rename_model.onnx") os.remove("rn50_9.onnx") os.remove(self.mb_v2_export_path) os.remove(self.rn50_export_path) @@ -942,128 +561,6 @@ def evaluate(self): with self.assertRaises(ValueError): test() - @unittest.skipIf( - Version(ort.__version__) == Version("1.13.1"), - "This function does not work with ONNX Runtime 1.13.1 for QDQ format quantization of ONNX models.", - ) - def test_inspect_tensor(self): - framework_specific_info = { - "device": "cpu", - "approach": "post_training_static_quant", - "random_seed": 1234, - "q_dataloader": None, - "backend": "default", - "format": "default", - "domain": "auto", - "recipes": {}, - "workspace_path": "./nc_workspace/{}/{}/".format("onnxrt", "imagenet"), - } - framework = "onnxrt_qlinearops" - adaptor = FRAMEWORKS[framework](framework_specific_info) - op_list = [i.name for i in self.rn50_model.graph.node if i.op_type == "Conv"] - - data = adaptor.inspect_tensor(self.rn50_model, self.cv_dataloader, inspect_type="activation", op_list=op_list) - self.assertNotEqual(len(data["activation"][0]), 0) - self.assertTrue("weight" not in data) - - adaptor.inspect_tensor(self.rn50_model, self.cv_dataloader, inspect_type="activation", save_to_disk=True) - self.assertTrue(os.path.isfile(framework_specific_info["workspace_path"] + "inspect_result.pkl")) - - data = adaptor.inspect_tensor(self.rn50_model, self.cv_dataloader, inspect_type="weight", op_list=op_list) - self.assertNotEqual(len(data["weight"]), 0) - self.assertTrue("activation" not in data) - - data = adaptor.inspect_tensor(self.rn50_model, self.cv_dataloader, inspect_type="all", op_list=op_list) - self.assertTrue("activation" in data) - self.assertTrue("weight" in data) - self.assertNotEqual(len(data["activation"][0]), 0) - self.assertNotEqual(len(data["weight"]), 0) - - data = adaptor.inspect_tensor(self.rn50_model, self.cv_dataloader, [op_list[0]], inspect_type="activation") - self.assertEqual(len(data["activation"][0]), 1) - - op = OrderedDict() - op[(op_list[0], "Conv")] = None - data = adaptor.inspect_tensor(self.rn50_model, self.cv_dataloader, op.keys(), inspect_type="activation") - self.assertEqual(len(data["activation"][0]), 1) - - for fake_yaml in ["qlinear.yaml", "qdq.yaml"]: - quantizer = Quantization(fake_yaml) - quantizer.calib_dataloader = self.cv_dataloader - quantizer.eval_dataloader = self.cv_dataloader - quantizer.model = self.rn50_model - q_model = quantizer.fit() - self.assertNotEqual(q_model, None) - - adaptor._pre_optimize(common.Model(self.rn50_model)) - opt_model = quantizer.strategy.adaptor.pre_optimized_model - - op_list, _ = quantizer.strategy.adaptor.diagnosis_helper( - opt_model, q_model, None, "./nc_workspace/recover/" - ) - - fp32_tensor = quantizer.strategy.adaptor.inspect_tensor(opt_model.model, self.cv_dataloader, op_list) - int8_tensor = quantizer.strategy.adaptor.inspect_tensor(q_model.model, self.cv_dataloader, op_list) - - self.assertTrue(len(fp32_tensor["activation"]) == len(int8_tensor["activation"])) - self.assertTrue(sorted(fp32_tensor["activation"][0].keys()) == sorted(int8_tensor["activation"][0].keys())) - for op in op_list: - for x, y in zip(fp32_tensor["activation"][0][op].values(), int8_tensor["activation"][0][op].values()): - self.assertTrue(x.shape == y.shape) - - if fake_yaml == "qlinear.yaml": - fp32_tensor = quantizer.strategy.adaptor.inspect_tensor( - opt_model.model, self.cv_dataloader, op_list, inspect_type="weight" - ) - int8_tensor = quantizer.strategy.adaptor.inspect_tensor( - q_model.model, self.cv_dataloader, op_list, inspect_type="weight" - ) - self.assertTrue(len(fp32_tensor["weight"]) == len(int8_tensor["weight"])) - self.assertTrue(sorted(fp32_tensor["weight"].keys()) == sorted(int8_tensor["weight"].keys())) - ai_onnx_domain = [ - opset for opset in q_model.model.opset_import if not opset.domain or opset.domain == "ai.onnx" - ] - if ai_onnx_domain[0].version > 12 or Version(ort.__version__) < Version("1.12.0"): - for op in fp32_tensor["weight"].keys(): - self.assertTrue( - sorted(fp32_tensor["weight"][op].keys()) == sorted(int8_tensor["weight"][op].keys()) - ) - fp32_tensor = quantizer.strategy.adaptor.inspect_tensor( - opt_model.model, self.cv_dataloader, op_list, inspect_type="all" - ) - int8_tensor = quantizer.strategy.adaptor.inspect_tensor( - q_model.model, self.cv_dataloader, op_list, inspect_type="all" - ) - self.assertTrue(len(fp32_tensor["weight"]) == len(int8_tensor["weight"])) - self.assertTrue(len(fp32_tensor["activation"]) == len(int8_tensor["activation"])) - self.assertTrue(sorted(fp32_tensor["weight"].keys()) == sorted(int8_tensor["weight"].keys())) - if ai_onnx_domain[0].version > 12 or Version(ort.__version__) < Version("1.12.0"): - for op in fp32_tensor["weight"].keys(): - self.assertTrue( - sorted(fp32_tensor["weight"][op].keys()) == sorted(int8_tensor["weight"][op].keys()) - ) - self.assertTrue( - sorted(fp32_tensor["activation"][0].keys()) == sorted(int8_tensor["activation"][0].keys()) - ) - if ai_onnx_domain[0].version > 12 or Version(ort.__version__) < Version("1.12.0"): - for op in op_list: - self.assertTrue( - sorted(fp32_tensor["activation"][0][op].keys()) - == sorted(int8_tensor["activation"][0][op].keys()) - ) - - config = PostTrainingQuantConfig(approach="static", recipes={"gemm_to_matmul": False}) - q_model = quantization.fit(self.gemm_model, config, calib_dataloader=self.ir3_dataloader) - - fp32_tensor = quantizer.strategy.adaptor.inspect_tensor( - self.gemm_model, self.ir3_dataloader, ["gemm"], inspect_type="weight" - ) - int8_tensor = quantizer.strategy.adaptor.inspect_tensor( - q_model.model, self.ir3_dataloader, ["gemm"], inspect_type="weight" - ) - self.assertTrue(len(fp32_tensor["weight"]) == len(int8_tensor["weight"])) - self.assertTrue(sorted(fp32_tensor["weight"].keys()) == sorted(int8_tensor["weight"].keys())) - def test_set_tensor(self): from neural_compressor.adaptor.ox_utils.util import get_node_original_name, quantize_data_with_scale_zero @@ -1156,31 +653,6 @@ def test_set_tensor(self): (new_tensor == numpy_helper.to_array(q_model.get_initializer(tensor_name + "_quantized"))).all() ) - def test_auto_quant(self): - conf.model.framework = "onnxrt_qlinearops" - conf.quantization.approach = "post_training_auto_quant" - conf.quantization.optype_wise = { - "Add|MatMul|Conv": {"weight": {"algorithm": ["minmax"]}, "activation": {"algorithm": ["minmax"]}} - } - conf.quantization.calibration.sampling_size = 1 - conf.tuning.exit_policy.timeout = 1000000 - conf.tuning.exit_policy.max_trials = 8 - conf.evaluation.accuracy.metric = {"MSE": {"compare_label": False}} - quantizer = Quantization(conf) - quantizer.calib_dataloader = self.cv_dataloader - quantizer.eval_dataloader = self.cv_dataloader - quantizer.model = self.rn50_model - q_model = quantizer.fit() - self.assertNotEqual(q_model, None) - - conf.model.framework = "onnxrt_qdq" - quantizer = Quantization(conf) - quantizer.calib_dataloader = self.cv_dataloader - quantizer.eval_dataloader = self.cv_dataloader - quantizer.model = self.rn50_model - q_model = quantizer.fit() - self.assertNotEqual(q_model, None) - def test_auto_quant_v2(self): from neural_compressor.config import AccuracyCriterion, PostTrainingQuantConfig, TuningCriterion from neural_compressor.quantization import fit @@ -1210,191 +682,6 @@ def test_quantize_data_per_channel(self): new_tensor_value = quantize_data_per_channel(tensor_value, 1, 254, qType, "sym") self.assertEqual(tensor_value.all(), new_tensor_value[-1].all()) - def test_adaptor(self): - from neural_compressor.utils.constant import FP32, INT8_SYM_MINMAX_PERTENSOR, UINT8_ASYM_MINMAX_PERTENSOR - - # check op_wise has higher priority than optype_wise - conf.model.framework = "onnxrt_qlinearops" - conf.quantization.approach = "post_training_static_quant" - conf.quantization.calibration.sampling_size = 1 - conf.quantization.optype_wise = {"Add": FP32} - conf.quantization.op_wise = { - "add": {"weight": INT8_SYM_MINMAX_PERTENSOR, "activation": UINT8_ASYM_MINMAX_PERTENSOR} - } - conf.evaluation.accuracy.metric = {"MSE": {"compare_label": False}} - quantizer = Quantization(conf) - quantizer.calib_dataloader = self.matmul_dataloader - quantizer.eval_dataloader = self.matmul_dataloader - quantizer.model = self.matmul_model - q_model = quantizer.fit() - self.assertTrue("add2" in [i.name for i in q_model.nodes()]) - self.assertTrue("add_quant" in [i.name for i in q_model.nodes()]) - - # check optype_wise has higher priority than model_wise - conf.quantization.pop("op_wise") - conf.quantization.model_wise = {"weight": INT8_SYM_MINMAX_PERTENSOR} - conf.quantization.optype_wise = {"MatMul": {"weight": {"granularity": ["per_channel"]}}} - quantizer = Quantization(conf) - quantizer.calib_dataloader = self.matmul_dataloader - quantizer.eval_dataloader = self.matmul_dataloader - quantizer.model = self.matmul_model - q_model = quantizer.fit() - self.assertEqual(len([i for i in q_model.initializer() if i.name == "B_scale"][0].float_data), 2) - - conf.quantization.pop("optype_wise") - conf.quantization.pop("model_wise") - - # check rename renamed nodes function - conf.model.framework = "onnxrt_integerops" - conf.quantization.approach = "post_training_dynamic_quant" - conf.quantization.calibration.sampling_size = 1 - conf.evaluation.accuracy.metric = {"MSE": {"compare_label": False}} - quantizer = Quantization(conf) - quantizer.calib_dataloader = self.rename_dataloader - quantizer.eval_dataloader = self.rename_dataloader - quantizer.model = self.rename_model - q_model = quantizer.fit() - self.assertNotEqual(q_model, None) - self.assertEqual(len([i.name for i in q_model.nodes()]), len(set([i.name for i in q_model.nodes()]))) - - # check large model quantization - conf.model.framework = "onnxrt_integerops" - conf.quantization.approach = "post_training_dynamic_quant" - conf.quantization.calibration.sampling_size = 1 - conf.evaluation.accuracy.metric = {"MSE": {"compare_label": False}} - quantizer = Quantization(conf) - quantizer.calib_dataloader = self.rename_dataloader - quantizer.eval_dataloader = self.rename_dataloader - onnx.save(self.rename_model, "rename_model.onnx") - quantizer.model = "rename_model.onnx" - # force set the model to large model - quantizer.model._is_large_model = True - q_model = quantizer.fit() - self.assertNotEqual(q_model, None) - - quantizer = Quantization("dynamic.yaml") - quantizer.calib_dataloader = self.cv_dataloader - quantizer.eval_dataloader = self.cv_dataloader - quantizer.model = self.rn50_model - q_model = quantizer.fit() - self.assertNotEqual(q_model, None) - - import copy - - # check opset version - tmp_model = copy.deepcopy(self.rn50_model) - tmp_model.opset_import[0].version = 10 - quantizer.model = tmp_model - q_model = quantizer.fit() - self.assertNotEqual(q_model, None) - tmp_model.opset_import.extend([onnx.helper.make_opsetid("", 11)]) - quantizer.model = tmp_model - q_model = quantizer.fit() - self.assertEqual(q_model, None) - model = onnx.load("rn50_9.onnx") - quantizer.model = model - q_model = quantizer.fit() - self.assertNotEqual(q_model, None) - - # check query quantizable_ops function - framework_specific_info = { - "device": "cpu", - "approach": "post_training_static_quant", - "random_seed": 1234, - "q_dataloader": None, - "backend": "default", - "format": "default", - "domain": "auto", - "recipes": {}, - "workspace_path": "./nc_workspace/{}/{}/".format("onnxrt", "imagenet"), - } - framework = "onnxrt_qlinearops" - adaptor = FRAMEWORKS[framework](framework_specific_info) - tune_cfg = { - "calib_iteration": 1, - "op": { - ("gather", "Gather"): { - "activation": {"dtype": ["uint8"], "quant_mode": "static"}, - "weight": {"dtype": ["uint8"]}, - }, - ("add", "Add"): { - "activation": {"dtype": ["uint8"], "quant_mode": "static"}, - "weight": {"dtype": ["int8"]}, - }, - ("squeeze", "Squeeze"): { - "activation": {"dtype": ["uint8"], "quant_mode": "static"}, - "weight": {"dtype": ["int8"]}, - }, - }, - } - adaptor.quantize(tune_cfg, common.Model(self.gather_model), self.gather_dataloader) - self.assertTrue(len(adaptor.quantizable_ops), 2) - - # check int8 + fp16 function - framework_specific_info["device"] = "gpu" - framework_specific_info["backend"] = "onnxrt_cuda_ep" - - tune_cfg = { - "calib_iteration": 1, - "op": { - ("Matmul", "MatMul"): { - "activation": {"dtype": ["uint8"], "quant_mode": "static"}, - "weight": {"dtype": ["int8"]}, - }, - ("add", "Add"): {"activation": {"dtype": "fp16", "quant_mode": "static"}, "weight": {"dtype": "fp16"}}, - ("add2", "Add"): {"activation": {"dtype": "fp16", "quant_mode": "static"}, "weight": {"dtype": "fp16"}}, - }, - } - adaptor = FRAMEWORKS[framework](framework_specific_info) - model = adaptor.quantize(tune_cfg, common.Model(self.matmul_model), self.matmul_dataloader) - self.assertEqual(len([i for i in model.model.graph.node if i.op_type == "Cast"]), 2) - - for fake_yaml in ["gather.yaml"]: - quantizer = Quantization(fake_yaml) - quantizer.model = self.gather_model - q_model = quantizer.fit() - self.assertNotEqual(q_model, None) - - quantizer.model = self.matmul_model2 - q_model = quantizer.fit() # error input shape test - self.assertEqual(q_model, None) - - quantizer.eval_dataloader = self.matmul_dataloader - q_model = quantizer.fit() # error input shape test - self.assertEqual(q_model, None) - - quantizer.calib_dataloader = self.matmul_dataloader - quantizer.eval_dataloader = self.matmul_dataloader - quantizer.model = self.matmul_model - q_model = quantizer.fit() - self.assertNotEqual(q_model, None) - - quantizer = Quantization("recipe.yaml") - quantizer.model = self.matmul_model - quantizer.calib_dataloader = self.matmul_dataloader - quantizer.eval_dataloader = self.matmul_dataloader - q_model = quantizer.fit() - self.assertTrue("Matmul" in [i.name for i in q_model.nodes()]) - - quantizer = Quantization("recipe2.yaml") - quantizer.model = self.conv_model2 - quantizer.calib_dataloader = self.conv_dataloader - quantizer.eval_dataloader = self.conv_dataloader - q_model = quantizer.fit() - self.assertNotEqual(q_model, None) - - for fake_yaml in ["non_MSE.yaml"]: - quantizer = Quantization(fake_yaml) - quantizer.calib_dataloader = self.cv_dataloader - quantizer.eval_dataloader = self.cv_dataloader - quantizer.model = self.mb_v2_model - q_model = quantizer.fit() - self.assertNotEqual(q_model, None) - - # check recover model function - model = recover(self.mb_v2_model, "./nc_workspace/recover/history.snapshot", 0) - self.assertTrue(model.model == q_model.model) - def test_qdq_settings(self): config = PostTrainingQuantConfig( approach="static", quant_format="QDQ", recipes={"add_qdq_pair_to_weight": True} @@ -1426,43 +713,6 @@ def test_model_name_checking(self): q_model = quantization.fit(self.matmul_model3, config, calib_dataloader=self.matmul_dataloader) self.assertTrue("MatMulInteger" in [i.op_type for i in q_model.nodes()]) - def test_lower_is_better_case(self): - import time - - conf.model.framework = "onnxrt_qlinearops" - conf.quantization.approach = "post_training_static_quant" - conf.quantization.model_wise = { - "weight": {"granularity": ["per_tensor"]}, - "activation": {"granularity": ["per_tensor"]}, - } - conf.tuning.exit_policy.max_trials = 5 - conf.tuning.accuracy_criterion.relative = 0.01 - conf.tuning.accuracy_criterion.higher_is_better = False - conf.tuning.exit_policy.timeout = 100 - - result = [0.0, 0.1, 0.1005, 0.102, 0.1002, 0.102, 0.102] - - def sub_eval(model, result): - time.sleep(0.001 * len(result)) - del result[0] - return result[0] - - def eval(model): - return sub_eval(model, result) - - from neural_compressor.experimental import Quantization - - quantizer = Quantization(conf) - quantizer.model = self.matmul_model - quantizer.calib_dataloader = self.matmul_dataloader - quantizer.eval_func = eval - q_model = quantizer.fit() - node_names = [i.name for i in q_model.nodes()] - # This assert it depends on the number of trials, disables it first. - # self.assertTrue('Matmul_quant' in node_names) - # self.assertTrue('add' in node_names) - # self.assertTrue('add2' in node_names) - def test_new_API(self): import time @@ -1564,107 +814,6 @@ def test_smooth_quant_args(self): adaptor.smooth_quant(self.conv_model, self.cv_dataloader, 1, scales_per_op=False) self.assertEqual(len([i for i in adaptor.pre_optimized_model.nodes() if i.op_type == "Mul"]), 1) - def test_multi_metrics(self): - conf.model.framework = "onnxrt_qlinearops" - conf.quantization.approach = "post_training_static_quant" - conf.evaluation.accuracy.multi_metrics = {"Accuracy": {}, "MSE": {"compare_label": False}} - conf.evaluation.accuracy.pop("metric", None) - from neural_compressor.experimental import Quantization - - quantizer = Quantization(conf) - quantizer.eval_dataloader = self.cv_dataloader - quantizer.calib_dataloader = self.cv_dataloader - quantizer.model = self.rn50_model - q_model = quantizer.fit() - self.assertNotEqual(q_model, None) - - conf.evaluation.accuracy.multi_metrics = { - "Accuracy": {}, - "MSE": {"compare_label": False}, - "higher_is_better": [False, False], - } - conf.tuning.exit_policy.max_trials = 1 - from neural_compressor.experimental import Quantization - - quantizer = Quantization(conf) - quantizer.eval_dataloader = self.cv_dataloader - quantizer.calib_dataloader = self.cv_dataloader - quantizer.model = self.rn50_model - q_model = quantizer.fit() - self.assertEqual(q_model, None) - - conf.tuning.accuracy_criterion.relative = 0.01 - conf.tuning.accuracy_criterion.higher_is_better = True - conf.evaluation.accuracy.multi_metrics = {"Accuracy": {}, "MSE": {"compare_label": False}, "weight": [0.5, 0.5]} - from neural_compressor.experimental import Quantization - - quantizer = Quantization(conf) - quantizer.eval_dataloader = self.cv_dataloader - quantizer.calib_dataloader = self.cv_dataloader - quantizer.model = self.rn50_model - q_model = quantizer.fit() - self.assertNotEqual(q_model, None) - - conf.evaluation.accuracy.multi_metrics = { - "Accuracy": {}, - "MSE": {"compare_label": False}, - "weight": [0.5, 0.5], - "higher_is_better": [False, False], - } - from neural_compressor.experimental import Quantization - - quantizer = Quantization(conf) - quantizer.eval_dataloader = self.cv_dataloader - quantizer.calib_dataloader = self.cv_dataloader - quantizer.model = self.rn50_model - q_model = quantizer.fit() - self.assertNotEqual(q_model, None) - - conf.evaluation.accuracy.multi_metrics = { - "Accuracy": {}, - "MSE": {"compare_label": False}, - "weight": [0.5, 0.5], - "higher_is_better": [False, False], - } - conf.tuning.accuracy_criterion.higher_is_better = False - conf.tuning.exit_policy.max_trials = 2 - from neural_compressor.experimental import Quantization - - quantizer = Quantization(conf) - quantizer.eval_dataloader = self.cv_dataloader - quantizer.calib_dataloader = self.cv_dataloader - quantizer.model = self.rn50_model - q_model = quantizer.fit() - self.assertEqual(q_model, None) - - import time - - result = [[0.0, 0.0], [0.0, 0.0], [0.0, 122.0]] - - def sub_eval(model, result): - time.sleep(0.001 * len(result)) - del result[0] - return result[0] - - def eval(model): - return sub_eval(model, result) - - conf.evaluation.accuracy.multi_metrics = { - "Accuracy": {}, - "MSE": {"compare_label": False}, - "higher_is_better": [False, False], - } - conf.tuning.exit_policy.max_trials = 1 - conf.tuning.accuracy_criterion = {"absolute": 0.01, "higher_is_better": False} - from neural_compressor.experimental import Quantization - - quantizer = Quantization(conf) - quantizer.eval_func = eval - quantizer.calib_dataloader = self.cv_dataloader - quantizer.model = self.rn50_model - q_model = quantizer.fit() - self.assertEqual(q_model, None) - def test_calibrator(self): from neural_compressor.adaptor.ox_utils.calibrator import CALIBRATOR @@ -1749,40 +898,6 @@ def test_query_block_info(self): q_capability = adaptor.query_fw_capability(Model(self.albert_model)) self.assertEqual(len(q_capability["block_wise"]), 12) - def test_dataloader_input(self): - cv_dataloader = DataLoader(framework="onnxruntime", dataset=DummyCVDataset_list(shape=(3, 224, 224))) - - quantizer = Quantization("qlinear.yaml") - quantizer.calib_dataloader = cv_dataloader - quantizer.eval_dataloader = cv_dataloader - quantizer.model = self.rn50_model - q_model = quantizer.fit() - self.assertNotEqual(q_model, None) - - cv_dataloader = DataLoader(framework="pytorch", dataset=DummyCVDataset_dict(shape=(3, 224, 224))) - quantizer = Quantization("qlinear.yaml") - quantizer.calib_dataloader = cv_dataloader - quantizer.eval_dataloader = cv_dataloader - quantizer.model = self.rn50_model - q_model = quantizer.fit() - self.assertNotEqual(q_model, None) - - nlp_dataloader = DummyNLPDataloader_list("distilbert-base-uncased-finetuned-sst-2-english") - quantizer = Quantization("qlinear.yaml") - quantizer.calib_dataloader = nlp_dataloader - quantizer.eval_dataloader = nlp_dataloader - quantizer.model = self.distilbert_model - q_model = quantizer.fit() - self.assertNotEqual(q_model, None) - - nlp_dataloader = DummyNLPDataloader_dict("distilbert-base-uncased-finetuned-sst-2-english") - quantizer = Quantization("qlinear.yaml") - quantizer.calib_dataloader = nlp_dataloader - quantizer.eval_dataloader = nlp_dataloader - quantizer.model = self.distilbert_model - q_model = quantizer.fit() - self.assertNotEqual(q_model, None) - @patch("logging.Logger.warning") def test_backend(self, mock_warning): framework_specific_info = { diff --git a/test/adaptor/onnxrt_adaptor/test_onnxrt_augment.py b/test/adaptor/onnxrt_adaptor/test_onnxrt_augment.py index 382efeaab6c..7137add6d07 100644 --- a/test/adaptor/onnxrt_adaptor/test_onnxrt_augment.py +++ b/test/adaptor/onnxrt_adaptor/test_onnxrt_augment.py @@ -10,7 +10,7 @@ sys.path.append("..") from neural_compressor.adaptor.ox_utils.calibration import ONNXRTAugment from neural_compressor.data import DATALOADERS, Datasets -from neural_compressor.experimental.data.datasets.dataset import Dataset +from neural_compressor.data.datasets.dataset import Dataset from neural_compressor.model.onnx_model import ONNXModel diff --git a/test/adaptor/pytorch_adaptor/test_adaptor_pytorch_1x.py b/test/adaptor/pytorch_adaptor/test_adaptor_pytorch_1x.py deleted file mode 100644 index b13c6ff5a76..00000000000 --- a/test/adaptor/pytorch_adaptor/test_adaptor_pytorch_1x.py +++ /dev/null @@ -1,1209 +0,0 @@ -import copy -import os -import pickle -import shutil -import unittest - -import numpy as np -import torch -import torch.nn as nn -import torch.nn.quantized as nnq -from packaging.version import Version -from torch.quantization import DeQuantStub, QuantStub - -import neural_compressor.adaptor.pytorch as nc_torch -from neural_compressor.adaptor import FRAMEWORKS -from neural_compressor.conf.config import QuantConf -from neural_compressor.experimental import Quantization, common -from neural_compressor.model import MODELS -from neural_compressor.utils.pytorch import load -from neural_compressor.utils.utility import LazyImport, recover - -try: - import intel_extension_for_pytorch as ipex - - IPEX = True -except: - IPEX = False - -# improve lazy import UT coverage -resnet18 = LazyImport("torchvision.models.resnet18") -q_resnet18 = LazyImport("torchvision.models.quantization.resnet18") - -PT_VERSION = nc_torch.get_torch_version().release -if PT_VERSION >= Version("1.8.0").release: - FX_MODE = True -else: - FX_MODE = False - - -fake_dyn_yaml = """ - model: - name: imagenet - framework: pytorch - - quantization: - approach: post_training_dynamic_quant - op_wise: { - 'decoder': { - 'activation': {'dtype': ['fp32']}, - 'weight': {'dtype': ['fp32']} - } - } - evaluation: - accuracy: - metric: - topk: 1 - performance: - warmup: 5 - iteration: 10 - - tuning: - accuracy_criterion: - relative: 0.01 - exit_policy: - timeout: 0 - random_seed: 9527 - workspace: - path: saved - """ - - -fake_ptq_yaml = """ - model: - name: imagenet - framework: pytorch - - quantization: - op_wise: { - - 'layer1.0.conv1': { - 'activation': {'dtype': ['fp32']}, - 'weight': {'dtype': ['fp32']} - }, - 'layer1.0.conv2': { - 'activation': {'dtype': ['fp32']}, - 'weight': {'dtype': ['fp32']} - }, - 'layer2.0.conv1': { - 'activation': {'dtype': ['uint8'], 'algorithm': ['minmax'], 'granularity': ['per_tensor'], 'scheme':['sym']}, - 'weight': {'dtype': ['int8'], 'algorithm': ['minmax'], 'granularity': ['per_channel'], 'scheme':['sym']} - }, - 'layer3.0.conv1': { - 'activation': {'dtype': ['uint8'], 'algorithm': ['kl'], 'granularity': ['per_tensor'], 'scheme':['sym']}, - 'weight': {'dtype': ['int8'], 'algorithm': ['minmax'], 'granularity': ['per_channel'], 'scheme':['sym']} - }, - 'layer1.0.add_relu': { - 'activation': {'dtype': ['fp32']}, - 'weight': {'dtype': ['fp32']} - }, - } - evaluation: - accuracy: - metric: - topk: 1 - performance: - warmup: 1 - iteration: 10 - - tuning: - accuracy_criterion: - relative: 0.01 - exit_policy: - timeout: 0 - random_seed: 9527 - workspace: - path: saved - """ - -fake_auto_yaml = """ - model: - name: imagenet - framework: pytorch_fx - - quantization: - approach: post_training_auto_quant - evaluation: - accuracy: - metric: - topk: 1 - performance: - warmup: 1 - iteration: 10 - - tuning: - accuracy_criterion: - relative: 0.01 - exit_policy: - timeout: 1000 - max_trials: 3 - random_seed: 9527 - workspace: - path: saved - """ - - -fake_ptq_yaml_for_fx = """ - model: - name: imagenet - framework: pytorch_fx - - quantization: - approach: post_training_auto_quant - op_wise: { - 'layer1.0.conv1': { - 'activation': {'dtype': ['fp32']}, - 'weight': {'dtype': ['fp32']} - }, - 'layer1.0.conv2': { - 'activation': {'dtype': ['fp32']}, - 'weight': {'dtype': ['fp32']} - }, - 'layer2.0.conv1': { - 'activation': {'dtype': ['uint8'], 'algorithm': ['minmax'], 'granularity': ['per_tensor'], 'scheme':['sym']}, - 'weight': {'dtype': ['int8'], 'algorithm': ['minmax'], 'granularity': ['per_channel'], 'scheme':['sym']} - }, - 'layer3.0.conv1': { - 'activation': {'dtype': ['uint8'], 'algorithm': ['kl'], 'granularity': ['per_tensor'], 'scheme':['sym']}, - 'weight': {'dtype': ['int8'], 'algorithm': ['minmax'], 'granularity': ['per_channel'], 'scheme':['sym']} - }, - 'layer1.0.add_relu': { - 'activation': {'dtype': ['fp32']}, - 'weight': {'dtype': ['fp32']} - }, - 'conv.module': { - 'weight': {'dtype': ['fp32']}, - 'activation': {'dtype': ['fp32']} - }, - 'default_qconfig': { - 'activation': {'dtype': ['fp32']}, - 'weight': {'dtype': ['fp32']} - } - } - evaluation: - accuracy: - metric: - topk: 1 - performance: - warmup: 5 - iteration: 10 - - tuning: - accuracy_criterion: - relative: 0.01 - exit_policy: - timeout: 0 - random_seed: 9527 - workspace: - path: saved - """ - - -fake_qat_yaml = """ - model: - name: imagenet - framework: pytorch - - quantization: - approach: quant_aware_training - train: - end_epoch: 1 - iteration: 1 - optimizer: - SGD: - learning_rate: 0.0001 - criterion: - CrossEntropyLoss: - reduction: mean - op_wise: { - 'layer1.0.conv1': { - 'activation': {'dtype': ['fp32']}, - 'weight': {'dtype': ['fp32']} - }, - 'layer1.0.conv2': { - 'activation': {'dtype': ['fp32']}, - 'weight': {'dtype': ['fp32']} - }, - 'layer2.0.conv1': { - 'activation': {'dtype': ['uint8'], 'algorithm': ['minmax'], 'granularity': ['per_tensor'], 'scheme':['sym']}, - 'weight': {'dtype': ['int8'], 'algorithm': ['minmax'], 'granularity': ['per_channel'], 'scheme':['sym']} - }, - 'layer3.0.conv1': { - 'activation': {'dtype': ['uint8'], 'algorithm': ['kl'], 'granularity': ['per_tensor'], 'scheme':['sym']}, - 'weight': {'dtype': ['int8'], 'algorithm': ['minmax'], 'granularity': ['per_channel'], 'scheme':['sym']} - }, - 'layer1.0.add_relu': { - 'activation': {'dtype': ['fp32']}, - 'weight': {'dtype': ['fp32']} - } - } - evaluation: - accuracy: - metric: - topk: 1 - - tuning: - accuracy_criterion: - relative: 0.01 - exit_policy: - timeout: 0 - random_seed: 9527 - workspace: - path: saved - """ - - -def build_pytorch_yaml(): - with open("ptq_yaml.yaml", "w", encoding="utf-8") as f: - f.write(fake_ptq_yaml) - - with open("dynamic_yaml.yaml", "w", encoding="utf-8") as f: - f.write(fake_dyn_yaml) - - with open("qat_yaml.yaml", "w", encoding="utf-8") as f: - f.write(fake_qat_yaml) - - with open("auto_yaml.yaml", "w", encoding="utf-8") as f: - f.write(fake_auto_yaml) - - -def build_pytorch_fx_yaml(): - if PT_VERSION >= Version("1.9.0").release: - fake_fx_ptq_yaml = fake_ptq_yaml_for_fx - else: - fake_fx_ptq_yaml = fake_ptq_yaml.replace("pytorch", "pytorch_fx") - with open("fx_ptq_yaml.yaml", "w", encoding="utf-8") as f: - f.write(fake_fx_ptq_yaml) - - fake_fx_dyn_yaml = fake_dyn_yaml.replace("pytorch", "pytorch_fx") - with open("fx_dynamic_yaml.yaml", "w", encoding="utf-8") as f: - f.write(fake_fx_dyn_yaml) - - fake_fx_qat_yaml = fake_qat_yaml.replace("pytorch", "pytorch_fx") - with open("fx_qat_yaml.yaml", "w", encoding="utf-8") as f: - f.write(fake_fx_qat_yaml) - - -def build_dump_tensors_yaml(): - fake_yaml = """ - model: - name: imagenet - framework: pytorch - - evaluation: - accuracy: - metric: - topk: 1 - - tuning: - accuracy_criterion: - relative: 0.01 - exit_policy: - timeout: 0 - random_seed: 9527 - workspace: - path: saved - tensorboard: true - """ - with open("dump_yaml.yaml", "w", encoding="utf-8") as f: - f.write(fake_yaml) - - -class M(torch.nn.Module): - def __init__(self): - super().__init__() - self.quant = QuantStub() - self.conv = nn.Conv2d(3, 1, 1) - self.linear = nn.Linear(224 * 224, 5) - self.dequant = DeQuantStub() - - def forward(self, x): - x = self.quant(x) - x = self.conv(x) - x = x.view(1, -1) - x = self.linear(x) - x = self.dequant(x) - return x - - -class FP32Model(torch.nn.Module): - def __init__(self): - super().__init__() - - def forward(self, x): - times = x.size(1) - if times == 1: - return torch.ones(x.shape) - return torch.ones(x.shape) + 1 - - -class DynamicModel(torch.nn.Module): - def __init__(self): - super().__init__() - self.conv = nn.Conv2d(1, 1, 1) - - def forward(self, x): - if x is not None: - x = self.conv(x) - return x - - -class SubModel(torch.nn.Module): - def __init__(self, bypass=True): - super().__init__() - self.quant = QuantStub() - self.conv = nn.Conv2d(1, 1, 1) - self.conv1 = nn.Conv2d(1, 1, 1) - self.bn = nn.BatchNorm2d(1) - self.relu = nn.ReLU() - self.fp32 = FP32Model() - self.norm = nn.LayerNorm([1, 224, 224]) - self.dequant = DeQuantStub() - self.bypass = bypass - - def forward(self, x): - x = self.conv(x) - x = self.bn(x) - x = self.quant(x) - x = self.relu(x) - x = self.conv1(x) - x = self.dequant(x) - if not self.bypass: - x = self.fp32(x) - x = self.norm(x) - return x - - -class PartialQuantModel(torch.nn.Module): - def __init__(self): - super().__init__() - self.quant = QuantStub() - self.conv = nn.Conv2d(3, 1, 1) - self.bn = nn.BatchNorm2d(1) - self.conv1 = nn.Conv2d(1, 1, 1) - self.bn1 = nn.BatchNorm2d(1) - self.conv2 = nn.Conv2d(1, 1, 1) - self.linear = nn.Linear(224 * 224, 1) - self.dequant = DeQuantStub() - self.sub = SubModel(bypass=False) - - def forward(self, x): - x = self.conv(x) - x = self.bn(x) - x = self.conv1(x) - x = self.bn1(x) - x = self.sub(x) - x = self.quant(x) - x = self.conv2(x) - x = x.view(1, -1) - x = self.linear(x) - x = self.dequant(x) - return x - - -class DynamicControlModel(torch.nn.Module): - def __init__(self): - super().__init__() - self.conv = nn.Conv2d(3, 1, 1) - self.bn = nn.BatchNorm2d(1) - self.linear = nn.Linear(224 * 224, 1) - self.sub = SubModel() - self.fp32 = FP32Model() - self.dyn = DynamicModel() - - def forward(self, x): - x = self.conv(x) - x = self.dyn(x) - x = self.bn(x) - x = self.sub(x) - x = self.fp32(x) - x = x.view(1, -1) - x = self.linear(x) - return x - - -class LSTMModel(nn.Module): - """Container module with an encoder, a recurrent module, and a decoder.""" - - def __init__(self, ntoken=10, ninp=512, nhid=256, nlayers=5, dropout=0.5): - super(LSTMModel, self).__init__() - self.drop = nn.Dropout(dropout) - self.encoder = nn.Embedding(ntoken, ninp) - self.rnn = nn.LSTM(ninp, nhid, nlayers, dropout=dropout) - self.decoder = nn.Linear(nhid, ntoken) - self.init_weights() - self.nhid = nhid - self.nlayers = nlayers - - def init_weights(self): - initrange = 0.1 - self.encoder.weight.data.uniform_(-initrange, initrange) - self.decoder.bias.data.zero_() - self.decoder.weight.data.uniform_(-initrange, initrange) - - def forward(self, input): - input = torch.ones((3, 10), dtype=torch.int32) - h0 = torch.randn(2, 10, 256) - c0 = torch.randn(2, 10, 256) - hidden = (h0, c0) - emb = self.encoder(input) - output, hidden = self.rnn(emb, hidden) - output = self.drop(output) - decoded = self.decoder(output) - return decoded, hidden - - -def eval_func(model): - # switch to evaluate mode - model.eval() - with torch.no_grad(): - input = torch.randn(1, 3, 224, 224) - # compute output - output = model(input) - return 0.0 - - -def q_func(model): - optimizer = torch.optim.SGD(model.parameters(), lr=0.0001) - # switch to evaluate mode - model.train() - input = torch.randn(1, 3, 224, 224) - # compute output - output = model(input) - loss = output.mean() - optimizer.zero_grad() - loss.backward() - optimizer.step() - return model - - -class TestPytorchAdaptor(unittest.TestCase): - framework_specific_info = { - "device": "cpu", - "approach": "post_training_static_quant", - "random_seed": 1234, - "q_dataloader": None, - "workspace_path": "./", - } - framework = "pytorch" - adaptor = FRAMEWORKS[framework](framework_specific_info) - model = q_resnet18() - nc_model = MODELS["pytorch"](model) - - @classmethod - def setUpClass(self): - build_pytorch_yaml() - build_dump_tensors_yaml() - - @classmethod - def tearDownClass(self): - os.remove("ptq_yaml.yaml") - os.remove("dynamic_yaml.yaml") - os.remove("qat_yaml.yaml") - os.remove("dump_yaml.yaml") - os.remove("auto_yaml.yaml") - shutil.rmtree("./saved", ignore_errors=True) - shutil.rmtree("runs", ignore_errors=True) - - def test_get_all_weight_name(self): - assert len(list(self.nc_model.get_all_weight_names())) == 62 - - def test_get_weight(self): - for name, param in self.model.named_parameters(): - if name == "layer4.1.conv2.weight": - param.data.fill_(0.0) - if name == "fc.bias": - param.data.fill_(0.1) - assert int(torch.sum(self.nc_model.get_weight("layer4.1.conv2.weight"))) == 0 - assert torch.allclose(torch.sum(self.nc_model.get_weight("fc.bias")), torch.tensor(100.0)) - - def test_get_input(self): - model = MODELS["pytorch"](q_resnet18()) - model.model.eval().fuse_model() - model.register_forward_pre_hook() - rand_input = torch.rand(100, 3, 224, 224).float() - model.model(rand_input) - assert torch.equal(model.get_inputs("x"), rand_input) - model.remove_hooks() - - def test_update_weights(self): - self.nc_model.update_weights("fc.bias", torch.zeros([1000])) - assert int(torch.sum(self.nc_model.get_weight("fc.bias"))) == 0 - - def test_get_gradient(self): - with self.assertRaises(AssertionError): - self.nc_model.get_gradient("fc.bias") - - for name, tensor in self.nc_model._model.named_parameters(): - if name == "fc.bias": - tensor.grad = torch.zeros_like(tensor) - break - assert torch.equal(torch.Tensor(self.nc_model.get_gradient("fc.bias")), torch.zeros_like(tensor)) - - rand_input = torch.rand(100, 3, 224, 224).float() - rand_input.grad = torch.ones_like(rand_input) - assert torch.equal(torch.Tensor(self.nc_model.get_gradient(rand_input)), torch.ones_like(rand_input)) - - def test_report_sparsity(self): - df, total_sparsity = self.nc_model.report_sparsity() - self.assertTrue(total_sparsity > 0) - self.assertTrue(len(df) == 22) - - def test_quantization_saved(self): - for fake_yaml in ["dynamic_yaml.yaml", "qat_yaml.yaml", "ptq_yaml.yaml"]: - model = M() - quantizer = Quantization(fake_yaml) - quantizer.conf.usr_cfg.tuning.exit_policy["performance_only"] = True - dataset = quantizer.dataset("dummy", (100, 3, 224, 224), label=True) - quantizer.model = model - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.eval_dataloader = common.DataLoader(dataset) - q_model = quantizer.fit() - eval_func(q_model) - q_model.save("./saved") - # Load configure and weights by neural_compressor.utils - saved_model = load("./saved", model) - eval_func(saved_model) - # recover int8 model from history - history_file = "./saved/history.snapshot" - model_recover = recover(model, history_file, 0) - eval_func(model_recover) - self.assertEqual(type(saved_model.conv), type(model_recover.conv)) - shutil.rmtree("./saved", ignore_errors=True) - from neural_compressor.experimental import Benchmark - - evaluator = Benchmark("ptq_yaml.yaml") - # Load configure and weights by neural_compressor.model - evaluator.model = model - evaluator.b_dataloader = common.DataLoader(dataset) - evaluator.fit("accuracy") - - for fake_yaml in ["qat_yaml.yaml", "ptq_yaml.yaml"]: - model = copy.deepcopy(self.model) - if fake_yaml == "ptq_yaml.yaml": - model.eval().fuse_model() - conf = QuantConf(fake_yaml) - quantizer = Quantization(conf) - dataset = quantizer.dataset("dummy", (100, 3, 224, 224)) - quantizer.model = model - if fake_yaml == "qat_yaml.yaml": - quantizer.q_func = q_func - else: - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.eval_func = eval_func - q_model = quantizer.fit() - q_model.save("./saved") - # Load configure and weights by neural_compressor.utils - saved_model = load("./saved", model) - eval_func(saved_model) - shutil.rmtree("./saved", ignore_errors=True) - - def test_quantization_new_saved(self): - for fake_yaml in ["dynamic_yaml.yaml", "qat_yaml.yaml", "ptq_yaml.yaml"]: - model = M() - quantizer = Quantization(fake_yaml) - quantizer.conf.usr_cfg.tuning.exit_policy["performance_only"] = True - dataset = quantizer.dataset("dummy", (100, 3, 224, 224), label=True) - quantizer.model = model - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.eval_dataloader = common.DataLoader(dataset) - q_model = quantizer.fit() - eval_func(q_model) - torch.save(q_model.quantized_state_dict(), "./saved/model.pt") - # Load configure and weights by neural_compressor.utils - from neural_compressor.experimental.common import Model - - common_model = Model(model) - common_model.load_quantized_state_dict(torch.load("./saved/model.pt")) - eval_func(common_model) - self.assertEqual(type(q_model._model.linear), type(common_model._model.linear)) - shutil.rmtree("./saved", ignore_errors=True) - - @unittest.skipIf(IPEX, "this function is affected by IPEX, Fixing now.") - def test_non_quant_module(self): - for fake_yaml in ["qat_yaml.yaml", "ptq_yaml.yaml"]: - model = PartialQuantModel() - conf = QuantConf(fake_yaml) - quantizer = Quantization(conf) - dataset = quantizer.dataset("dummy", (1, 3, 224, 224)) - non_quant_dict = { - "non_quant_module_name": ["conv", "conv1", "sub.conv"], - "non_quant_module_class": ["BatchNorm2d", "FP32Model"], - } - quantizer.model = common.Model(model, **non_quant_dict) - if fake_yaml == "qat_yaml.yaml": - quantizer.q_func = q_func - else: - quantizer.calib_func = eval_func - quantizer.eval_func = eval_func - q_model = quantizer.fit() - self.assertTrue(isinstance(q_model.model.conv, torch.nn.Conv2d)) - self.assertTrue("quantize" in str(q_model.model.conv2.__class__)) - q_model.save("./saved") - saved_model = load("./saved", model, **non_quant_dict) - eval_func(saved_model) - shutil.rmtree("./saved", ignore_errors=True) - - def test_auto_quant(self): - def eval_func(model): - return 1 - - model_origin = LSTMModel( - ntoken=10, - ninp=512, - nhid=256, - nlayers=2, - ) - # run fx_quant in neural_compressor and save the quantized GraphModule - quantizer = Quantization("auto_yaml.yaml") - dataset = quantizer.dataset("dummy", (3, 10), label=True) - quantizer.eval_func = eval_func - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = common.Model(model_origin) - q_model = quantizer.fit() - self.assertNotEqual(q_model, None) - - def test_workspace_path(self): - model = M() - quantizer = Quantization("ptq_yaml.yaml") - quantizer.conf.usr_cfg.tuning.exit_policy["performance_only"] = True - dataset = quantizer.dataset("dummy", (100, 3, 224, 224), label=True) - quantizer.model = model - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.eval_dataloader = common.DataLoader(dataset) - q_model = quantizer.fit() - eval_func(q_model) - torch.save(q_model.quantized_state_dict(), "./saved/best_model.pt") - # Load configure and weights by workspace_path - from neural_compressor.experimental.common import Model - - common_model = Model(model) - common_model.workspace_path = "./saved" - eval_func(common_model) - self.assertEqual(type(q_model._model.linear), type(common_model._model.linear)) - shutil.rmtree("./saved", ignore_errors=True) - - def test_get_graph_info(self): - from neural_compressor.model.torch_model import PyTorchModel - - model = PyTorchModel(self.model) - op_map = model.graph_info - self.assertTrue(op_map["conv1"] == "Conv2d") - - def test_tensorboard(self): - model = copy.deepcopy(self.nc_model) - model.model.eval().fuse_model() - quantizer = Quantization("dump_yaml.yaml") - dataset = quantizer.dataset("dummy", (100, 3, 224, 224), label=True) - quantizer.model = model.model - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.eval_func = eval_func - quantizer.fit() - self.assertTrue(True if os.path.exists("runs/eval/baseline_acc0.0") else False) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.eval_func = None - quantizer.fit() - self.assertTrue(True if os.path.exists("runs/eval/baseline_acc0.0") else False) - - def test_tensor_dump_and_set(self): - model = copy.deepcopy(self.nc_model) - model.model.eval().fuse_model() - quantizer = Quantization("ptq_yaml.yaml") - dataset = quantizer.dataset("dummy", (100, 3, 224, 224), label=True) - dataloader = common.DataLoader(dataset) - dataloader = common._generate_common_dataloader(dataloader, "pytorch") - quantizer.eval_dataloader = dataloader - quantizer.calib_dataloader = dataloader - quantizer.model = model.model - q_model = quantizer.fit() - quantizer.strategy.adaptor.inspect_tensor( - model, - dataloader, - op_list=["conv1.0", "layer1.0.conv1.0"], - iteration_list=[1, 2], - inspect_type="all", - save_to_disk=True, - ) - with open("saved/inspect_result.pkl", "rb") as fp: - tensor_dict = pickle.load(fp) - a = tensor_dict["activation"][0] - w = tensor_dict["weight"] - if PT_VERSION >= Version("1.8.0").release: - self.assertTrue(w["conv1.0"]["conv1.0.weight"].shape[0] == a["conv1.0"]["conv1.0.output0"].shape[1]) - else: - self.assertTrue(w["conv1.0"]["conv1.0.weight"].shape[0] == a["conv1.0"]["conv1.1.output0"].shape[1]) - data = np.random.random(w["conv1.0"]["conv1.0.weight"].shape).astype(np.float32) - quantizer.strategy.adaptor.set_tensor(q_model, {"conv1.0.weight": data}) - changed_tensor = q_model.get_weight("conv1.weight") - scales = changed_tensor.q_per_channel_scales() - changed_tensor_fp32 = torch.dequantize(changed_tensor) - self.assertTrue(np.allclose(data, changed_tensor_fp32.numpy(), atol=2 / np.min(scales.numpy()))) - quantizer.strategy.adaptor.inspect_tensor( - q_model, - dataloader, - op_list=["conv1.0", "layer1.0.conv1.0"], - iteration_list=[1, 2], - inspect_type="all", - save_to_disk=False, - ) - - def test_forward_wrapper(self): - vision_model = resnet18() - - class dummymodel(torch.nn.Module): - def __init__(self, model): - super(dummymodel, self).__init__() - self._model = model - - def forward(self, input=None): - return self._model(input) - - data = [ - [{"input": torch.rand(3, 224, 224)}, torch.ones(1, 1)], - ] - # dataloader.batch_size=100 - dataloader = common.DataLoader(data, batch_size=1) - quantizer = Quantization("dynamic_yaml.yaml") - model = dummymodel(vision_model) - quantizer.model = model - quantizer.calib_dataloader = dataloader - quantizer.eval_dataloader = dataloader - model = quantizer.fit() - self.assertTrue(isinstance(model, torch.nn.Module)) - - def test_floatfunctions_fallback(self): - class ModelWithFunctionals(torch.nn.Module): - def __init__(self): - super(ModelWithFunctionals, self).__init__() - self.mycat = nnq.FloatFunctional() - self.myadd = nnq.FloatFunctional() - self.myadd_relu = nnq.FloatFunctional() - # Tracing doesn't work yet for c10 ops with scalar inputs - # https://github.com/pytorch/pytorch/issues/27097 - self.my_scalar_add = nnq.FloatFunctional() - self.mymul = nnq.FloatFunctional() - self.my_scalar_mul = nnq.FloatFunctional() - self.quant = QuantStub() - self.dequant = DeQuantStub() - - def forward(self, x): - x = self.quant(x) - y = self.mycat.cat([x, x, x]) - z = self.myadd.add(y, y) - w = self.myadd_relu.add_relu(z, z) - # Tracing doesn't work yet for c10 ops with scalar inputs - # https://github.com/pytorch/pytorch/issues/27097 - w = self.my_scalar_add.add_scalar(w, -0.5) - w = self.mymul.mul(w, w) - w = self.my_scalar_mul.mul_scalar(w, 0.5) - w = self.dequant(w) - return w - - model = ModelWithFunctionals() - model = MODELS["pytorch"](model) - x = torch.rand(10, 1, dtype=torch.float) - y = model.model(x) - fallback_ops = [] - q_capability = self.adaptor.query_fw_capability(model) - for k, v in q_capability["opwise"].items(): - if k[0] != "quant" and k[0] != "dequant": - fallback_ops.append(k[0]) - model.model.qconfig = torch.quantization.default_qconfig - model.model.quant.qconfig = torch.quantization.default_qconfig - if PT_VERSION >= Version("1.8.0").release: - model.model.dequant.qconfig = torch.quantization.default_qconfig - nc_torch._fallback_quantizable_ops_recursively(model.model, "", fallback_ops, op_qcfgs={}) - if PT_VERSION >= Version("2.0.0").release: - from torch.quantization.quantize import _add_observer_ as add_observer_ - else: - from torch.quantization.quantize import add_observer_ - add_observer_(model.model) - model.model(x) - torch.quantization.convert(model.model, self.adaptor.q_mapping, inplace=True) - qy = model.model(x) - tol = {"atol": 1e-01, "rtol": 1e-03} - self.assertTrue(np.allclose(y, qy, **tol)) - - -@unittest.skipIf(not FX_MODE, "Unsupported Fx Mode with PyTorch Version Below 1.8") -class TestPytorchFXAdaptor(unittest.TestCase): - framework_specific_info = { - "device": "cpu", - "approach": "post_training_static_quant", - "random_seed": 1234, - "q_dataloader": None, - "workspace_path": "./", - } - framework = "pytorch_fx" - adaptor = FRAMEWORKS[framework](framework_specific_info) - - @classmethod - def setUpClass(self): - build_pytorch_fx_yaml() - - @classmethod - def tearDownClass(self): - os.remove("fx_ptq_yaml.yaml") - os.remove("fx_dynamic_yaml.yaml") - shutil.rmtree("./saved", ignore_errors=True) - shutil.rmtree("runs", ignore_errors=True) - - def test_fx_quant(self): - for fake_yaml in ["fx_qat_yaml.yaml", "fx_ptq_yaml.yaml"]: - model_origin = resnet18() - # run fx_quant in neural_compressor and save the quantized GraphModule - quantizer = Quantization(fake_yaml) - dataset = quantizer.dataset("dummy", (10, 3, 224, 224), label=True) - quantizer.eval_func = eval_func - if fake_yaml == "fx_qat_yaml.yaml": - quantizer.q_func = q_func - else: - quantizer.calib_func = eval_func - dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = dataloader - quantizer.model = common.Model( - model_origin, - **{ - "prepare_custom_config_dict": {"non_traceable_module_name": ["a"]}, - "convert_custom_config_dict": {"preserved_attributes": []}, - } - ) - q_model = quantizer.fit() - q_model.save("./saved") - # Load configure and weights with neural_compressor.utils - model_fx = load( - "./saved", - model_origin, - **{ - "prepare_custom_config_dict": {"non_traceable_module_name": ["a"]}, - "convert_custom_config_dict": {"preserved_attributes": []}, - } - ) - self.assertTrue(isinstance(model_fx, torch.fx.graph_module.GraphModule)) - - # recover int8 model with only tune_cfg - history_file = "./saved/history.snapshot" - model_fx_recover = recover( - model_origin, - history_file, - 0, - **{ - "prepare_custom_config_dict": {"non_traceable_module_name": ["a"]}, - "convert_custom_config_dict": {"preserved_attributes": []}, - } - ) - self.assertEqual(model_fx.code, model_fx_recover.code) - shutil.rmtree("./saved", ignore_errors=True) - - for fake_yaml in ["fx_qat_yaml.yaml", "fx_ptq_yaml.yaml"]: - model_origin = M() - # run fx_quant in neural_compressor and save the quantized GraphModule - quantizer = Quantization(fake_yaml) - quantizer.conf.usr_cfg.tuning.exit_policy["performance_only"] = True - dataset = quantizer.dataset("dummy", (10, 3, 224, 224), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.model = common.Model( - model_origin, - **{ - "prepare_custom_config_dict": {"non_traceable_module_name": ["a"]}, - "convert_custom_config_dict": {"preserved_attributes": []}, - } - ) - q_model = quantizer.fit() - q_model.save("./saved") - # Load configure and weights with neural_compressor.utils - model_fx = load( - "./saved", - model_origin, - **{ - "prepare_custom_config_dict": {"non_traceable_module_name": ["a"]}, - "convert_custom_config_dict": {"preserved_attributes": []}, - "dataloader": quantizer.calib_dataloader, - } - ) - self.assertTrue(isinstance(model_fx, torch.fx.graph_module.GraphModule)) - shutil.rmtree("./saved", ignore_errors=True) - - @unittest.skipIf( - PT_VERSION < Version("1.9.0").release, - "Please use PyTroch 1.9 or higher version for dynamic quantization with pytorch_fx backend", - ) - def test_fx_dynamic_quant(self): - model = LSTMModel( - ntoken=10, - ninp=512, - nhid=256, - nlayers=5, - ) - # run fx_quant in neural_compressor and save the quantized GraphModule - model.eval() - quantizer = Quantization("fx_dynamic_yaml.yaml") - quantizer.model = common.Model( - copy.deepcopy(model), - **{ - "prepare_custom_config_dict": {"non_traceable_module_name": ["a"]}, - "convert_custom_config_dict": {"preserved_attributes": []}, - } - ) - q_model = quantizer.fit() - q_model.save("./saved") - - # Load configure and weights by neural_compressor.utils - model_fx = load( - "./saved", - copy.deepcopy(model), - **{ - "prepare_custom_config_dict": {"non_traceable_module_name": ["a"]}, - "convert_custom_config_dict": {"preserved_attributes": []}, - } - ) - self.assertTrue(isinstance(model_fx, torch.fx.graph_module.GraphModule)) - - # Test the functionality of older model saving type - state_dict = torch.load("./saved/best_model.pt") - tune_cfg = state_dict.pop("best_configure") - import yaml - - with open("./saved/best_configure.yaml", "w") as f: - yaml.dump(tune_cfg, f, default_flow_style=False) - torch.save(state_dict, "./saved/best_model_weights.pt") - os.remove("./saved/best_model.pt") - model_fx = load( - "./saved", - copy.deepcopy(model), - **{ - "prepare_custom_config_dict": {"non_traceable_module_name": ["a"]}, - "convert_custom_config_dict": {"preserved_attributes": []}, - } - ) - self.assertTrue(isinstance(model_fx, torch.fx.graph_module.GraphModule)) - - # recover int8 model with only tune_cfg - history_file = "./saved/history.snapshot" - model_fx_recover = recover( - model, - history_file, - 0, - **{ - "prepare_custom_config_dict": {"non_traceable_module_name": ["a"]}, - "convert_custom_config_dict": {"preserved_attributes": []}, - } - ) - self.assertEqual(model_fx.code, model_fx_recover.code) - shutil.rmtree("./saved", ignore_errors=True) - - def test_default_dynamic_quant(self): - def eval_func(model): - return 1 - - def q_func(model): - return model - - # Model Definition - for fake_yaml in ["fx_qat_yaml.yaml", "fx_ptq_yaml.yaml"]: - model_origin = LSTMModel( - ntoken=10, - ninp=512, - nhid=256, - nlayers=2, - ) - # run fx_quant in neural_compressor and save the quantized GraphModule - quantizer = Quantization(fake_yaml) - dataset = quantizer.dataset("dummy", (3, 10), label=True) - quantizer.eval_func = eval_func - if fake_yaml == "fx_qat_yaml.yaml": - quantizer.q_func = q_func - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = common.Model(model_origin) - q_model = quantizer.fit() - self.assertTrue("quantize" in str(type(q_model.model.encoder))) - self.assertTrue("quantize" in str(type(q_model.model.rnn))) - - def test_fx_sub_module_quant(self): - for fake_yaml in ["fx_qat_yaml.yaml", "fx_dynamic_yaml.yaml", "fx_ptq_yaml.yaml"]: - model_origin = DynamicControlModel() - # run fx_quant in neural_compressor and save the quantized GraphModule - quantizer = Quantization(fake_yaml) - dataset = quantizer.dataset("dummy", (1, 3, 224, 224), label=True) - quantizer.eval_func = eval_func - if fake_yaml == "fx_qat_yaml.yaml": - quantizer.q_func = q_func - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = common.Model( - model_origin, - **{ - "prepare_custom_config_dict": {"non_traceable_module_name": ["a"]}, - "convert_custom_config_dict": {"preserved_attributes": []}, - } - ) - q_model = quantizer.fit() - q_model.save("./saved") - # Load configure and weights with neural_compressor.utils - model_fx = load( - "./saved/best_model.pt", - model_origin, - **{ - "prepare_custom_config_dict": {"non_traceable_module_name": ["a"]}, - "convert_custom_config_dict": {"preserved_attributes": []}, - } - ) - self.assertTrue(isinstance(model_fx.sub, torch.fx.graph_module.GraphModule)) - - # recover int8 model with only tune_cfg - history_file = "./saved/history.snapshot" - model_fx_recover = recover( - model_origin, - history_file, - 0, - **{ - "prepare_custom_config_dict": {"non_traceable_module_name": ["a"]}, - "convert_custom_config_dict": {"preserved_attributes": []}, - } - ) - self.assertEqual(model_fx.sub.code, model_fx_recover.sub.code) - shutil.rmtree("./saved", ignore_errors=True) - - def test_deepcopy_failure(self): - def eval_func(model): - return 1 - - # To build an object t2, which will fail on deepcopy. - class T1: - def __init__(self, t1) -> None: - self.t1 = t1 - self.j = 1 - - # required for usage with set in T1 - def __hash__(self): - return hash(self.j) - - t1 = set() - t2 = T1([t1]) - t1.add(t2) - - for fake_yaml in ["fx_ptq_yaml.yaml"]: - model_origin = M() - model_origin.tmp = t2 - # run fx_quant in neural_compressor and save the quantized GraphModule - quantizer = Quantization(fake_yaml) - dataset = quantizer.dataset("dummy", (1, 3, 224, 224), label=True) - quantizer.eval_func = eval_func - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = common.Model(model_origin) - q_model = quantizer.fit() - self.assertTrue(isinstance(q_model.model, torch.fx.graph_module.GraphModule)) - - @unittest.skipIf( - PT_VERSION < Version("1.11.0").release, - "Please use PyTroch 1.11 or higher version for mixed precision with pytorch_fx or pytorch backend", - ) - def test_bf16_capability(self): - model_origin = DynamicControlModel() - os.environ["FORCE_BF16"] = "1" - q_capability = self.adaptor._get_quantizable_ops(model_origin) - del os.environ["FORCE_BF16"] - - self.assertEqual([elem["weight"]["dtype"] for elem in q_capability["optypewise"]["Conv2d"]], [["int8"], "fp32"]) - self.assertEqual( - [elem["activation"]["dtype"] for elem in q_capability["optypewise"]["Conv2d"]], [["uint8"], "fp32"] - ) - self.assertEqual( - [elem["weight"]["dtype"] for elem in q_capability["opwise"][("conv", "Conv2d")]], [["int8"], "fp32"] - ) - self.assertEqual( - [elem["activation"]["dtype"] for elem in q_capability["opwise"][("conv", "Conv2d")]], [["uint8"], "fp32"] - ) - self.assertEqual( - [elem["weight"]["dtype"] for elem in q_capability["opwise"][("linear", "Linear")]], - [["int8"], "fp32", "bf16"], - ) - self.assertEqual( - [elem["activation"]["dtype"] for elem in q_capability["opwise"][("linear", "Linear")]], - [["uint8"], "fp32", "bf16"], - ) - - @unittest.skipIf( - PT_VERSION < Version("1.11.0").release, - "Please use PyTroch 1.11 or higher version for mixed precision with pytorch_fx or pytorch backend", - ) - def test_mix_precision(self): - fake_yaml = "fx_ptq_yaml.yaml" - model_origin = DynamicControlModel() - # run fx_quant in neural_compressor and save the quantized GraphModule - quantizer = Quantization(fake_yaml) - dataset = quantizer.dataset("dummy", (1, 3, 224, 224), label=True) - quantizer.eval_func = eval_func - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = common.Model( - model_origin, - **{ - "prepare_custom_config_dict": {"non_traceable_module_name": ["a"]}, - "convert_custom_config_dict": {"preserved_attributes": []}, - } - ) - q_model = quantizer.fit() - tune_cfg = q_model.q_config - tune_cfg["op"][("conv.module", "Conv2d")].clear() - tune_cfg["op"][("conv.module", "Conv2d")] = {"weight": {"dtype": "bf16"}, "activation": {"dtype": "bf16"}} - tune_cfg["bf16_ops_list"].append(("conv.module", "Conv2d")) - from neural_compressor.adaptor.torch_utils.bf16_convert import Convert - - q_model._model = Convert(q_model._model, tune_cfg) - - self.assertEqual(q_model._model.conv.module.module.weight.dtype, torch.bfloat16) - self.assertEqual(q_model._model.conv.module.module.bias.dtype, torch.bfloat16) - - def test_symbolic_trace(self): - from neural_compressor.adaptor.torch_utils.symbolic_trace import symbolic_trace - - model_origin = DynamicControlModel() - traced_model = symbolic_trace(model_origin, is_qat=False) - if PT_VERSION >= Version("1.11.0").release: - self.assertTrue(isinstance(traced_model.sub, torch.nn.Module)) - self.assertTrue(isinstance(traced_model.conv, torch.fx.graph_module.GraphModule)) - else: - self.assertTrue(isinstance(traced_model.sub, torch.fx.graph_module.GraphModule)) - traced_model_qat = symbolic_trace(model_origin, is_qat=True) - self.assertTrue(isinstance(traced_model_qat.sub, torch.fx.graph_module.GraphModule)) - - def test_tensor_dump(self): - model = resnet18() - model = MODELS["pytorch"](model) - quantizer = Quantization("fx_ptq_yaml.yaml") - dataset = quantizer.dataset("dummy", (100, 3, 224, 224), label=True) - dataloader = common.DataLoader(dataset) - dataloader = common._generate_common_dataloader(dataloader, "pytorch") - quantizer.eval_dataloader = dataloader - quantizer.calib_dataloader = dataloader - quantizer.model = model.model - q_model = quantizer.fit() - op_list, _ = quantizer.strategy.adaptor.diagnosis_helper(model, q_model, None) - quantizer.strategy.adaptor.inspect_tensor( - model, dataloader, op_list=op_list, iteration_list=[1], inspect_type="all", save_to_disk=True - ) - with open("saved/inspect_result.pkl", "rb") as fp: - tensor_dict = pickle.load(fp) - a = tensor_dict["activation"][0] - w = tensor_dict["weight"] - self.assertTrue(w["conv1"]["conv1.weight"].shape[0] == a["conv1"]["conv1.output0"].shape[1]) - quantizer.strategy.adaptor.inspect_tensor( - q_model, - dataloader, - op_list=["conv1", "layer2.0.downsample.0"], - iteration_list=[1, 2], - inspect_type="all", - save_to_disk=True, - ) - with open("saved/inspect_result.pkl", "rb") as fp: - tensor_dict = pickle.load(fp) - a = tensor_dict["activation"][0] - w = tensor_dict["weight"] - self.assertTrue( - w["layer2.0.downsample.0"]["layer2.0.downsample.0.weight"].shape[0] - == a["layer2.0.downsample.0"]["layer2.0.downsample.0.output0"].shape[1] - ) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/adaptor/tensorflow_adaptor/test_bf16_convert.py b/test/adaptor/tensorflow_adaptor/test_bf16_convert.py index 4467f709aba..4a3801f7b32 100644 --- a/test/adaptor/tensorflow_adaptor/test_bf16_convert.py +++ b/test/adaptor/tensorflow_adaptor/test_bf16_convert.py @@ -12,113 +12,6 @@ from neural_compressor.adaptor.tf_utils.graph_rewriter.bf16.bf16_convert import BF16Convert -def build_fake_yaml(): - fake_yaml = """ - model: - name: fake_yaml - framework: tensorflow - inputs: input - outputs: final - device: cpu - use_bf16: True - evaluation: - accuracy: - metric: - topk: 1 - tuning: - strategy: - name: basic - exit_policy: - max_trials: 2 - accuracy_criterion: - relative: 0.01 - workspace: - path: saved - """ - with open("fake_yaml.yaml", "w", encoding="utf-8") as f: - f.write(fake_yaml) - f.close() - - -def build_newapi_fake_yaml(): - fake_yaml = """ - model: - name: fake_yaml - framework: tensorflow - inputs: input - outputs: final - device: cpu - use_bf16: True - evaluation: - accuracy: - metric: - topk: 1 - tuning: - strategy: - name: basic - exit_policy: - max_trials: 2 - accuracy_criterion: - relative: 0.01 - workspace: - path: saved - """ - with open("newapi_fake_yaml.yaml", "w", encoding="utf-8") as f: - f.write(fake_yaml) - f.close() - - -def build_fake_bf16_rnn_yaml(): - fake_yaml = """ - model: - name: fake_yaml - framework: tensorflow - inputs: input_1 - outputs: dense/BiasAdd - device: cpu - use_bf16: True - quantization: - op_wise: { - \"lstm/while/MatMul\": { - \"activation\": {\"dtype\": [\"bf16\"]}, - }, - \"lstm/while/MatMul_1\": { - \"activation\": {\"dtype\": [\"bf16\"]}, - }, - \"lstm/while/MatMul_2\": { - \"activation\": {\"dtype\": [\"bf16\"]}, - }, - \"lstm/while/MatMul_3\": { - \"activation\": {\"dtype\": [\"bf16\"]}, - }, - \"lstm_1/while/MatMul\": { - \"activation\": {\"dtype\": [\"bf16\"]}, - }, - \"lstm_1/while/MatMul_1\": { - \"activation\": {\"dtype\": [\"bf16\"]}, - }, - \"lstm_1/while/MatMul_2\": { - \"activation\": {\"dtype\": [\"bf16\"]}, - }, - \"lstm_1/while/MatMul_3\": { - \"activation\": {\"dtype\": [\"bf16\"]}, - }, - } - evaluation: - accuracy: - metric: - topk: 1 - tuning: - accuracy_criterion: - relative: 0.05 - exit_policy: - performance_only: True - """ - with open("fake_bf16_rnn.yaml", "w", encoding="utf-8") as f: - f.write(fake_yaml) - f.close() - - def create_test_graph(bf16_graph=True): input_node = node_def_pb2.NodeDef() input_node.name = "input" @@ -360,49 +253,11 @@ def setUpClass(self): self.input_graph.ParseFromString(f.read()) self.test_graph = create_test_graph() self.test_fp32_graph = create_test_graph(False) - build_fake_yaml() - build_newapi_fake_yaml() - build_fake_bf16_rnn_yaml() @classmethod def tearDownClass(self): - os.remove("fake_yaml.yaml") - os.remove("newapi_fake_yaml.yaml") - os.remove("fake_bf16_rnn.yaml") shutil.rmtree("saved", ignore_errors=True) - def test_bf16_transpose_b_matmul(self): - from tensorflow.core.framework import attr_value_pb2 - - os.environ["FORCE_BF16"] = "1" - DT_BFLOAT16 = attr_value_pb2.AttrValue(type=dtypes.bfloat16.as_datatype_enum) - g = tf.Graph() - with g.as_default(): - x_data = np.array([[0.1, 0.2], [0.2, 0.3]]) - y_data = np.array([[1, 2], [3, 4]], dtype=float) - x = tf.compat.v1.placeholder(tf.float32, shape=[2, 2], name="x") - y = tf.constant(y_data, dtype=tf.float32, shape=[2, 2]) - z = tf.matmul(x, y, name="no_quant_matmul", transpose_b=True) - z = tf.nn.relu6(z, name="op_to_store") - is_bf16 = False - with tf.compat.v1.Session() as sess: - sess.run(z, feed_dict={x: x_data, y: y_data}) - float_graph_def = sess.graph.as_graph_def() - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(2, 2), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.eval_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.model = float_graph_def - output_graph = quantizer.fit() - for i in output_graph.graph_def.node: - if i.op == "MatMul" and i.attr["T"] == DT_BFLOAT16: - is_bf16 = True - break - self.assertEqual(is_bf16, True) - @unittest.skipIf(tf.__version__ < "2.0", "currently bf16 convert does not support 1.15up3") def test_rn50_convert(self): bf16_nodes = [node.name for node in self.input_graph.node if node.op in ["Conv2D", "AvgPool", "MatMul"]] @@ -426,75 +281,6 @@ def test_do_transform(self): self.assertEqual(new_relu2.attr["T"].type, dtypes.bfloat16) self.assertEqual(new_conv3.attr["T"].type, dtypes.float32) - def test_bf16_fallback(self): - os.environ["FORCE_BF16"] = "1" - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("newapi_fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(1, 224, 224, 3), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = self.test_fp32_graph - output_graph = quantizer.fit() - cast_op_count = 0 - for node in output_graph.graph_def.node: - if node.op == "Cast": - cast_op_count += 1 - self.assertTrue(cast_op_count == 0) - - @unittest.skipIf(tf.version.VERSION.find("up") == -1, "Only supports tf 1.x") - def test_bf16_rnn(self): - os.environ["FORCE_BF16"] = "1" - try: - inp = tf.keras.layers.Input(shape=(None, 4)) - lstm_1 = tf.keras.layers.LSTM(units=10, return_sequences=True)(inp) - dropout_1 = tf.keras.layers.Dropout(0.2)(lstm_1) - lstm_2 = tf.keras.layers.LSTM(units=10, return_sequences=False)(dropout_1) - dropout_2 = tf.keras.layers.Dropout(0.2)(lstm_2) - out = tf.keras.layers.Dense(1)(dropout_2) - model = tf.keras.models.Model(inputs=inp, outputs=out) - - model.compile(loss="mse", optimizer=tf.keras.optimizers.RMSprop()) - - # input_names = [t.name.split(":")[0] for t in model.inputs] - output_names = [t.name.split(":")[0] for t in model.outputs] - - q_data = np.random.randn(64, 10, 4) - label = np.random.randn(64, 1) - model.predict(q_data) - - sess = tf.keras.backend.get_session() - - graph = sess.graph - - from tensorflow.compat.v1 import graph_util - - graph_def = graph_util.convert_variables_to_constants( - sess, - graph.as_graph_def(), - output_names, - ) - quant_data = (q_data, label) - evl_data = (q_data, label) - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_bf16_rnn.yaml") - quantizer.calib_dataloader = common.DataLoader(dataset=list(zip(quant_data[0], quant_data[1]))) - quantizer.eval_dataloader = common.DataLoader(dataset=list(zip(evl_data[0], evl_data[1]))) - quantizer.model = graph_def - quantized_model = quantizer.fit() - - convert_to_bf16_flag = False - for i in quantized_model.graph_def.node: - if i.name == "lstm/while/MatMul_3" and i.attr["T"].type == dtypes.bfloat16.as_datatype_enum: - convert_to_bf16_flag = True - - self.assertEqual(convert_to_bf16_flag, True) - except NotImplementedError: - # Kernel bug, happens when the version of python is 3.7 and the version of numpy is >= 1.20.0 - pass - if __name__ == "__main__": unittest.main() diff --git a/test/adaptor/tensorflow_adaptor/test_tensorboard.py b/test/adaptor/tensorflow_adaptor/test_tensorboard.py deleted file mode 100644 index 69b4b1070a8..00000000000 --- a/test/adaptor/tensorflow_adaptor/test_tensorboard.py +++ /dev/null @@ -1,241 +0,0 @@ -"""Tests for quantization.""" - -import os -import shutil -import unittest - -import numpy as np -import tensorflow as tf -import yaml -from tensorflow.core.framework import attr_value_pb2, graph_pb2, node_def_pb2 -from tensorflow.python.framework import dtypes, tensor_util - -from neural_compressor.adaptor.tf_utils.util import version1_gt_version2 - -tf.compat.v1.disable_eager_execution() - - -def build_fake_yaml(): - fake_yaml = """ - model: - name: fake_yaml - framework: tensorflow - inputs: input - outputs: final - device: cpu - evaluation: - accuracy: - metric: - topk: 1 - tuning: - tensorboard: true - strategy: - name: basic - exit_policy: - timeout: 200 - accuracy_criterion: - relative: 0.01 - workspace: - path: saved - """ - y = yaml.load(fake_yaml, Loader=yaml.SafeLoader) - with open("fake_yaml.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - f.close() - - -def build_fake_model(): - input_node = node_def_pb2.NodeDef() - input_node.name = "input" - input_node.op = "Placeholder" - input_node.attr["dtype"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - - conv1_weight_node = node_def_pb2.NodeDef() - conv1_weight_node.name = "conv1_weights" - conv1_weight_node.op = "Const" - conv1_weight_value = np.float32(np.abs(np.random.randn(3, 3, 3, 32))) - conv1_weight_node.attr["dtype"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - conv1_weight_node.attr["value"].CopyFrom( - attr_value_pb2.AttrValue( - tensor=tensor_util.make_tensor_proto( - conv1_weight_value, conv1_weight_value.dtype.type, conv1_weight_value.shape - ) - ) - ) - - conv1_node = node_def_pb2.NodeDef() - conv1_node.name = "conv1" - conv1_node.op = "Conv2D" - conv1_node.attr["T"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - conv1_node.input.extend([input_node.name, conv1_weight_node.name]) - conv1_node.attr["strides"].CopyFrom( - attr_value_pb2.AttrValue(list=attr_value_pb2.AttrValue.ListValue(i=[1, 1, 1, 1])) - ) - conv1_node.attr["dilations"].CopyFrom( - attr_value_pb2.AttrValue(list=attr_value_pb2.AttrValue.ListValue(i=[1, 1, 1, 1])) - ) - conv1_node.attr["padding"].CopyFrom(attr_value_pb2.AttrValue(s=b"SAME")) - conv1_node.attr["data_format"].CopyFrom(attr_value_pb2.AttrValue(s=b"NHWC")) - - bias_node = node_def_pb2.NodeDef() - bias_node.name = "conv1_bias" - bias_node.op = "Const" - bias_value = np.float32(np.abs(np.random.randn(32))) - bias_node.attr["dtype"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - bias_node.attr["value"].CopyFrom( - attr_value_pb2.AttrValue( - tensor=tensor_util.make_tensor_proto(bias_value, bias_value.dtype.type, bias_value.shape) - ) - ) - - bias_add_node = node_def_pb2.NodeDef() - bias_add_node.name = "conv1_bias_add" - bias_add_node.op = "BiasAdd" - bias_add_node.attr["T"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - bias_add_node.input.extend([conv1_node.name, bias_node.name]) - bias_add_node.attr["data_format"].CopyFrom(attr_value_pb2.AttrValue(s=b"NHWC")) - relu_node = node_def_pb2.NodeDef() - relu_node.op = "Relu" - relu_node.name = "relu" - relu_node.attr["T"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - relu_node.input.extend([bias_add_node.name]) - - conv2_weight_node = node_def_pb2.NodeDef() - conv2_weight_node.name = "conv2_weights" - conv2_weight_node.op = "Const" - conv2_weight_value = np.float32(np.abs(np.random.randn(3, 3, 32, 32))) - conv2_weight_node.attr["dtype"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - conv2_weight_node.attr["value"].CopyFrom( - attr_value_pb2.AttrValue( - tensor=tensor_util.make_tensor_proto( - conv2_weight_value, conv2_weight_value.dtype.type, conv2_weight_value.shape - ) - ) - ) - - conv2_node = node_def_pb2.NodeDef() - conv2_node.name = "conv2" - conv2_node.op = "Conv2D" - conv2_node.attr["T"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - conv2_node.input.extend([relu_node.name, conv2_weight_node.name]) - conv2_node.attr["strides"].CopyFrom( - attr_value_pb2.AttrValue(list=attr_value_pb2.AttrValue.ListValue(i=[1, 1, 1, 1])) - ) - conv2_node.attr["dilations"].CopyFrom( - attr_value_pb2.AttrValue(list=attr_value_pb2.AttrValue.ListValue(i=[1, 1, 1, 1])) - ) - conv2_node.attr["padding"].CopyFrom(attr_value_pb2.AttrValue(s=b"SAME")) - conv2_node.attr["data_format"].CopyFrom(attr_value_pb2.AttrValue(s=b"NHWC")) - - bias_node2 = node_def_pb2.NodeDef() - bias_node2.name = "conv2_bias" - bias_node2.op = "Const" - bias_value2 = np.float32(np.abs(np.random.randn(32))) - bias_node2.attr["dtype"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - bias_node2.attr["value"].CopyFrom( - attr_value_pb2.AttrValue( - tensor=tensor_util.make_tensor_proto(bias_value2, bias_value2.dtype.type, bias_value2.shape) - ) - ) - - bias_add_node2 = node_def_pb2.NodeDef() - bias_add_node2.name = "conv2_bias_add" - bias_add_node2.op = "BiasAdd" - bias_add_node2.attr["T"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - bias_add_node2.input.extend([conv2_node.name, bias_node2.name]) - bias_add_node2.attr["data_format"].CopyFrom(attr_value_pb2.AttrValue(s=b"NHWC")) - - relu_node2 = node_def_pb2.NodeDef() - relu_node2.op = "Relu" - relu_node2.name = "relu2" - relu_node2.attr["T"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - relu_node2.input.extend([bias_add_node2.name]) - - conv3_weight_node = node_def_pb2.NodeDef() - conv3_weight_node.name = "conv3_weights" - conv3_weight_node.op = "Const" - conv3_weight_value = np.float32(np.abs(np.random.randn(3, 3, 32, 32))) - conv3_weight_node.attr["dtype"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - conv3_weight_node.attr["value"].CopyFrom( - attr_value_pb2.AttrValue( - tensor=tensor_util.make_tensor_proto( - conv3_weight_value, conv3_weight_value.dtype.type, conv3_weight_value.shape - ) - ) - ) - - conv3_node = node_def_pb2.NodeDef() - conv3_node.name = "conv3" - conv3_node.op = "Conv2D" - conv3_node.attr["T"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - conv3_node.input.extend([relu_node2.name, conv3_weight_node.name]) - conv3_node.attr["strides"].CopyFrom( - attr_value_pb2.AttrValue(list=attr_value_pb2.AttrValue.ListValue(i=[1, 1, 1, 1])) - ) - conv3_node.attr["dilations"].CopyFrom( - attr_value_pb2.AttrValue(list=attr_value_pb2.AttrValue.ListValue(i=[1, 1, 1, 1])) - ) - conv3_node.attr["padding"].CopyFrom(attr_value_pb2.AttrValue(s=b"SAME")) - conv3_node.attr["data_format"].CopyFrom(attr_value_pb2.AttrValue(s=b"NHWC")) - - identity_node = node_def_pb2.NodeDef() - identity_node.name = "final" - identity_node.op = "Identity" - identity_node.attr["T"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - identity_node.input.extend([conv3_node.name]) - - graph = graph_pb2.GraphDef() - - graph.node.extend( - [ - input_node, - conv1_weight_node, - conv1_node, - bias_node, - bias_add_node, - relu_node, - conv2_weight_node, - conv2_node, - bias_node2, - bias_add_node2, - relu_node2, - conv3_weight_node, - conv3_node, - identity_node, - ] - ) - - return graph - - -class TestTensorboard(unittest.TestCase): - @classmethod - def setUpClass(self): - self.constant_graph = build_fake_model() - build_fake_yaml() - - @classmethod - def tearDownClass(self): - os.remove("fake_yaml.yaml") - - shutil.rmtree("saved", ignore_errors=True) - shutil.rmtree("runs/", ignore_errors=True) - - @unittest.skipIf( - version1_gt_version2(tf.version.VERSION, "2.5.0"), "Skip test_bf16_fallback case for tf 2.6.0 and above." - ) - def test_run_basic_one_trial(self): - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", (1, 224, 224, 3), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.model = self.constant_graph - quantizer.fit() - - self.assertTrue(True if len(os.listdir("./runs/eval")) > 2 else False) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/adaptor/tensorflow_adaptor/test_tensorflow_calculate_op_sensitivity.py b/test/adaptor/tensorflow_adaptor/test_tensorflow_calculate_op_sensitivity.py deleted file mode 100644 index 076fb27c666..00000000000 --- a/test/adaptor/tensorflow_adaptor/test_tensorflow_calculate_op_sensitivity.py +++ /dev/null @@ -1,150 +0,0 @@ -import os -import shutil -import unittest - -import numpy as np -import tensorflow as tf - - -def build_msev2_yaml(): - mse_yaml = """ - model: - name: fake_yaml - framework: tensorflow - inputs: x - outputs: op2_to_store - device: cpu - evaluation: - accuracy: - metric: - topk: 1 - tuning: - strategy: - name: mse_v2 - accuracy_criterion: - relative: 0.01 - exit_policy: - max_trials: 10 - timeout: 3600 - """ - with open("mse_yaml.yaml", "w", encoding="utf-8") as f: - f.write(mse_yaml) - - -def build_fake_model(): - try: - graph = tf.Graph() - graph_def = tf.compat.v1.GraphDef() - with tf.compat.v1.Session() as sess: - x = tf.compat.v1.placeholder(tf.float32, shape=(1, 3, 3, 1), name="x") - y = tf.constant(np.random.random((2, 2, 1, 1)).astype(np.float32), name="y") - z = tf.constant(np.random.random((1, 1, 1, 1)).astype(np.float32), name="z") - op = tf.nn.conv2d(input=x, filters=y, strides=[1, 1, 1, 1], padding="VALID", name="op_to_store") - op2 = tf.nn.conv2d( - input=op, - filters=z, - strides=[1, 1, 1, 1], - padding="VALID", - ) - last_identity = tf.identity(op2, name="op2_to_store") - sess.run(tf.compat.v1.global_variables_initializer()) - constant_graph = tf.compat.v1.graph_util.convert_variables_to_constants( - sess, sess.graph_def, ["op2_to_store"] - ) - - graph_def.ParseFromString(constant_graph.SerializeToString()) - with graph.as_default(): - tf.import_graph_def(graph_def, name="") - except: - graph = tf.Graph() - graph_def = tf.compat.v1.GraphDef() - with tf.compat.v1.Session() as sess: - x = tf.compat.v1.placeholder(tf.float32, shape=(1, 3, 3, 1), name="x") - y = tf.constant(np.random.random((2, 2, 1, 1)).astype(np.float32), name="y") - z = tf.constant(np.random.random((1, 1, 1, 1)).astype(np.float32), name="z") - op = tf.nn.conv2d(input=x, filters=y, strides=[1, 1, 1, 1], padding="VALID", name="op_to_store") - op2 = tf.nn.conv2d(input=op, filters=z, strides=[1, 1, 1, 1], padding="VALID") - last_identity = tf.identity(op2, name="op2_to_store") - - sess.run(tf.compat.v1.global_variables_initializer()) - constant_graph = tf.compat.v1.graph_util.convert_variables_to_constants( - sess, sess.graph_def, ["op2_to_store"] - ) - - graph_def.ParseFromString(constant_graph.SerializeToString()) - with graph.as_default(): - tf.import_graph_def(graph_def, name="") - return graph - - -class TestGetOutputTensor(unittest.TestCase): - @classmethod - def setUpClass(self): - build_msev2_yaml() - self.model = build_fake_model() - - @classmethod - def tearDownClass(self): - os.remove("mse_yaml.yaml") - shutil.rmtree("./saved", ignore_errors=True) - shutil.rmtree("runs", ignore_errors=True) - - def test_get_output_op_names(self): - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("mse_yaml.yaml") - dataset = quantizer.dataset("dummy", (100, 3, 3, 1), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.model = self.model - qmodel = quantizer.fit() - - self.assertEqual(quantizer.strategy.adaptor.get_output_op_names(qmodel), ["Conv2D_dummy_biasadd"]) - - def test_calculate_op_sensitivity(self): - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("mse_yaml.yaml") - quantizer.model = self.model - dataset = quantizer.dataset("dummy", (100, 3, 3, 1), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.pre_process() - - dataloader = quantizer._calib_dataloader - strategy = quantizer.strategy - adaptor = strategy.adaptor - tune_cfg_generator = strategy.next_tune_cfg() - tune_cfg = strategy._tune_cfg_converter(next(tune_cfg_generator)) - output_op_names = ["Conv2D_dummy_biasadd"] - - op_sensitivity = adaptor.calculate_op_sensitivity( - model=quantizer.model, - dataloader=dataloader, - tune_cfg=tune_cfg, - output_op_names=output_op_names, - confidence_batches=1, - fallback=True, - ) - self.assertIn(("op_to_store", "conv2d"), op_sensitivity) - self.assertIn(("Conv2D", "conv2d"), op_sensitivity) - - tune_cfg["op"][("op_to_store", "conv2d")] = { - "activation": {"dtype": "fp32", "quant_mode": "fp32"}, - "weight": {"dtype": "fp32"}, - } - - op_sensitivity = adaptor.calculate_op_sensitivity( - model=quantizer.model, - dataloader=dataloader, - tune_cfg=tune_cfg, - output_op_names=output_op_names, - confidence_batches=1, - fallback=True, - ) - self.assertNotIn(("op_to_store", "conv2d"), op_sensitivity) - self.assertIn(("Conv2D", "conv2d"), op_sensitivity) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/adaptor/tensorflow_adaptor/test_tensorflow_graph_concat.py b/test/adaptor/tensorflow_adaptor/test_tensorflow_graph_concat.py deleted file mode 100644 index da161bd2737..00000000000 --- a/test/adaptor/tensorflow_adaptor/test_tensorflow_graph_concat.py +++ /dev/null @@ -1,153 +0,0 @@ -# -# -# -*- coding: utf-8 -*- -import os -import platform -import unittest - -import tensorflow as tf -import yaml -from tensorflow.compat.v1 import graph_util - -import neural_compressor -from neural_compressor.adaptor.tensorflow import TensorflowQuery -from neural_compressor.adaptor.tf_utils.quantize_graph.quantize_graph_for_intel_cpu import QuantizeGraphForIntel -from neural_compressor.adaptor.tf_utils.util import disable_random, read_graph - - -def build_fake_yaml(): - fake_yaml = """ - model: - name: fake_yaml - framework: tensorflow - inputs: input - outputs: predict - device: cpu - quantization: - model_wise: - weight: - granularity: per_tensor - scheme: sym - dtype: int8 - algorithm: minmax - evaluation: - accuracy: - metric: - topk: 1 - tuning: - strategy: - name: mse - accuracy_criterion: - relative: 0.01 - exit_policy: - performance_only: True - workspace: - path: saved - """ - y = yaml.load(fake_yaml, Loader=yaml.SafeLoader) - with open("fake_yaml.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - f.close() - - -class TestTensorflowConcat(unittest.TestCase): - mb_model_url = ( - "https://storage.googleapis.com/intel-optimized-tensorflow/models/v1_8/inceptionv3_fp32_pretrained_model.pb" - ) - pb_path = "/tmp/.neural_compressor/inceptionv3_fp32.pb" - platform = platform.system().lower() - if platform == "windows": - pb_path = "C:\\tmp\\.neural_compressor\\inceptionv3_fp32.pb" - - @classmethod - def setUpClass(self): - if not os.path.exists(self.pb_path) and self.platform == "linux": - os.system("mkdir -p /tmp/.neural_compressor && wget {} -O {} ".format(self.mb_model_url, self.pb_path)) - self.op_wise_sequences = TensorflowQuery( - local_config_file=os.path.join(os.path.dirname(neural_compressor.__file__), "adaptor/tensorflow.yaml") - ).get_eightbit_patterns() - build_fake_yaml() - - @classmethod - def tearDownClass(self): - os.remove("fake_yaml.yaml") - - @unittest.skipIf(tf.__version__ < "2.0", "does not support on 1.15up3") - def test_tensorflow_concat_quantization(self): - output_graph_def = read_graph(self.pb_path) - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 299, 299, 3), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - found_quantized_concat_node = False - - target_concat_node_name = "v0/cg/incept_v3_a0/concat_eightbit_quantized_concatv2" - from neural_compressor.adaptor.tf_utils.graph_util import GraphAnalyzer - - cur_graph = GraphAnalyzer() - cur_graph.graph = output_graph.graph_def - graph_info = cur_graph.parse_graph() - found_quantized_concat_node = target_concat_node_name in graph_info - - self.assertEqual(found_quantized_concat_node, True) - min_out, max_out = [], [] - for input_conv_name in graph_info[target_concat_node_name].node.input[:4]: - # print (input_conv_name, graph_info[input_conv_name].node.input) - min_freezed_out_name = graph_info[input_conv_name].node.input[-2] - max_freezed_out_name = graph_info[input_conv_name].node.input[-1] - min_freezed_out_value = (graph_info[min_freezed_out_name].node.attr["value"].tensor.float_val)[0] - max_freezed_out_value = (graph_info[max_freezed_out_name].node.attr["value"].tensor.float_val)[0] - min_out.append(min_freezed_out_value) - max_out.append(max_freezed_out_value) - - self.assertEqual(len(set(min_out)), 1) - self.assertEqual(len(set(max_out)), 1) - - @disable_random() - def test_concat_with_different_input_type(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 128, 128, 16], name="input") - conv_weights = tf.compat.v1.get_variable( - "weight", [2, 2, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv_bias = tf.compat.v1.get_variable("bias", [16], initializer=tf.compat.v1.random_normal_initializer()) - - x = tf.nn.relu(x) - sqrt = tf.math.sqrt(x) - relu_sqrt = tf.nn.relu(sqrt) - conv = tf.nn.conv2d(relu_sqrt, conv_weights, strides=[1, 2, 2, 1], padding="SAME", name="last") - normed = tf.compat.v1.layers.batch_normalization(conv) - - relu = tf.nn.relu(normed) - conv1 = tf.nn.conv2d(x, conv_weights, strides=[1, 2, 2, 1], padding="SAME", name="last") - conv_bias = tf.nn.bias_add(conv1, conv_bias) - concat = tf.concat([relu, conv_bias], 1) - final_node = tf.nn.relu(concat, name="op_to_store") - out_name = final_node.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 128, 128, 16), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - - quantized_concat = False - for i in output_graph.graph_def.node: - if i.op == "QuantizedConcatV2": - quantized_concat = True - self.assertEqual(quantized_concat, False) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/adaptor/tensorflow_adaptor/test_tensorflow_graph_conv_as_output.py b/test/adaptor/tensorflow_adaptor/test_tensorflow_graph_conv_as_output.py deleted file mode 100644 index 1cd9fd0f5f6..00000000000 --- a/test/adaptor/tensorflow_adaptor/test_tensorflow_graph_conv_as_output.py +++ /dev/null @@ -1,231 +0,0 @@ -import os -import shutil -import unittest - -import numpy as np -import tensorflow as tf -from tensorflow.core.framework import attr_value_pb2, graph_pb2, node_def_pb2 -from tensorflow.python.framework import dtypes, tensor_util - -from neural_compressor.experimental import Quantization, common - - -def build_fake_yaml(): - fake_yaml = """ - model: - name: fake_yaml - framework: tensorflow - inputs: input - outputs: conv3 - device: cpu - evaluation: - accuracy: - metric: - topk: 1 - tuning: - strategy: - name: basic - exit_policy: - max_trials: 2 - accuracy_criterion: - relative: 0.01 - workspace: - path: saved - """ - with open("fake_yaml.yaml", "w", encoding="utf-8") as f: - f.write(fake_yaml) - f.close() - - -def create_test_graph(): - input_node = node_def_pb2.NodeDef() - input_node.name = "input" - input_node.op = "Placeholder" - input_node.attr["dtype"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - - conv1_weight_node = node_def_pb2.NodeDef() - conv1_weight_node.name = "conv1_weights" - conv1_weight_node.op = "Const" - conv1_weight_value = np.float32(np.abs(np.random.randn(3, 3, 3, 32))) - conv1_weight_node.attr["dtype"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - conv1_weight_node.attr["value"].CopyFrom( - attr_value_pb2.AttrValue( - tensor=tensor_util.make_tensor_proto( - conv1_weight_value, conv1_weight_value.dtype.type, conv1_weight_value.shape - ) - ) - ) - - conv1_node = node_def_pb2.NodeDef() - conv1_node.name = "conv1" - conv1_node.op = "Conv2D" - conv1_node.attr["T"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - conv1_node.input.extend([input_node.name, conv1_weight_node.name]) - conv1_node.attr["strides"].CopyFrom( - attr_value_pb2.AttrValue(list=attr_value_pb2.AttrValue.ListValue(i=[1, 1, 1, 1])) - ) - conv1_node.attr["dilations"].CopyFrom( - attr_value_pb2.AttrValue(list=attr_value_pb2.AttrValue.ListValue(i=[1, 1, 1, 1])) - ) - conv1_node.attr["padding"].CopyFrom(attr_value_pb2.AttrValue(s=b"SAME")) - conv1_node.attr["data_format"].CopyFrom(attr_value_pb2.AttrValue(s=b"NHWC")) - - bias_node = node_def_pb2.NodeDef() - bias_node.name = "conv1_bias" - bias_node.op = "Const" - bias_value = np.float32(np.abs(np.random.randn(32))) - bias_node.attr["dtype"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - bias_node.attr["value"].CopyFrom( - attr_value_pb2.AttrValue( - tensor=tensor_util.make_tensor_proto(bias_value, bias_value.dtype.type, bias_value.shape) - ) - ) - - bias_add_node = node_def_pb2.NodeDef() - bias_add_node.name = "conv1_bias_add" - bias_add_node.op = "BiasAdd" - bias_add_node.attr["T"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - bias_add_node.input.extend([conv1_node.name, bias_node.name]) - bias_add_node.attr["data_format"].CopyFrom(attr_value_pb2.AttrValue(s=b"NHWC")) - - relu_node = node_def_pb2.NodeDef() - relu_node.op = "Relu" - relu_node.name = "relu" - relu_node.attr["T"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - relu_node.input.extend([bias_add_node.name]) - - conv2_weight_node = node_def_pb2.NodeDef() - conv2_weight_node.name = "conv2_weights" - conv2_weight_node.op = "Const" - conv2_weight_value = np.float32(np.abs(np.random.randn(3, 3, 32, 32))) - conv2_weight_node.attr["dtype"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - conv2_weight_node.attr["value"].CopyFrom( - attr_value_pb2.AttrValue( - tensor=tensor_util.make_tensor_proto( - conv2_weight_value, conv2_weight_value.dtype.type, conv2_weight_value.shape - ) - ) - ) - - conv2_node = node_def_pb2.NodeDef() - conv2_node.name = "conv2" - conv2_node.op = "Conv2D" - conv2_node.attr["T"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - conv2_node.input.extend([relu_node.name, conv2_weight_node.name]) - conv2_node.attr["strides"].CopyFrom( - attr_value_pb2.AttrValue(list=attr_value_pb2.AttrValue.ListValue(i=[1, 1, 1, 1])) - ) - conv2_node.attr["dilations"].CopyFrom( - attr_value_pb2.AttrValue(list=attr_value_pb2.AttrValue.ListValue(i=[1, 1, 1, 1])) - ) - conv2_node.attr["padding"].CopyFrom(attr_value_pb2.AttrValue(s=b"SAME")) - conv2_node.attr["data_format"].CopyFrom(attr_value_pb2.AttrValue(s=b"NHWC")) - - bias_node2 = node_def_pb2.NodeDef() - bias_node2.name = "conv2_bias" - bias_node2.op = "Const" - bias_value2 = np.float32(np.abs(np.random.randn(32))) - bias_node2.attr["dtype"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - bias_node2.attr["value"].CopyFrom( - attr_value_pb2.AttrValue( - tensor=tensor_util.make_tensor_proto(bias_value2, bias_value2.dtype.type, bias_value2.shape) - ) - ) - - bias_add_node2 = node_def_pb2.NodeDef() - bias_add_node2.name = "conv2_bias_add" - bias_add_node2.op = "BiasAdd" - bias_add_node2.attr["T"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - bias_add_node2.input.extend([conv2_node.name, bias_node2.name]) - bias_add_node2.attr["data_format"].CopyFrom(attr_value_pb2.AttrValue(s=b"NHWC")) - - relu_node2 = node_def_pb2.NodeDef() - relu_node2.op = "Relu" - relu_node2.name = "relu2" - relu_node2.attr["T"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - relu_node2.input.extend([bias_add_node2.name]) - - log_node = node_def_pb2.NodeDef() - log_node.name = "log1" - log_node.op = "Log" - log_node.attr["T"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - log_node.input.extend([relu_node2.name]) - - conv3_weight_node = node_def_pb2.NodeDef() - conv3_weight_node.name = "conv3_weights" - conv3_weight_node.op = "Const" - conv3_weight_value = np.float32(np.abs(np.random.randn(3, 3, 32, 32))) - conv3_weight_node.attr["dtype"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - conv3_weight_node.attr["value"].CopyFrom( - attr_value_pb2.AttrValue( - tensor=tensor_util.make_tensor_proto( - conv3_weight_value, conv3_weight_value.dtype.type, conv3_weight_value.shape - ) - ) - ) - - conv3_node = node_def_pb2.NodeDef() - conv3_node.name = "conv3" - conv3_node.op = "Conv2D" - conv3_node.attr["T"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - conv3_node.input.extend([log_node.name, conv3_weight_node.name]) - conv3_node.attr["strides"].CopyFrom( - attr_value_pb2.AttrValue(list=attr_value_pb2.AttrValue.ListValue(i=[1, 1, 1, 1])) - ) - conv3_node.attr["dilations"].CopyFrom( - attr_value_pb2.AttrValue(list=attr_value_pb2.AttrValue.ListValue(i=[1, 1, 1, 1])) - ) - conv3_node.attr["padding"].CopyFrom(attr_value_pb2.AttrValue(s=b"SAME")) - conv3_node.attr["data_format"].CopyFrom(attr_value_pb2.AttrValue(s=b"NHWC")) - - test_graph = graph_pb2.GraphDef() - - test_graph.node.extend( - [ - input_node, - conv1_weight_node, - conv1_node, - bias_node, - bias_add_node, - relu_node, - conv2_weight_node, - conv2_node, - bias_node2, - bias_add_node2, - log_node, - relu_node2, - conv3_weight_node, - conv3_node, - ] - ) - return test_graph - - -@unittest.skipIf(tf.__version__ < "2.8.0", "only support spr-base TF") -class TestConvAsOutput(unittest.TestCase): - @classmethod - def setUpClass(self): - self.test_graph = create_test_graph() - build_fake_yaml() - - @classmethod - def tearDownClass(self): - os.remove("fake_yaml.yaml") - shutil.rmtree("saved", ignore_errors=True) - - def test_do_transform(self): - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(1, 224, 224, 3), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = create_test_graph() - output_graph = quantizer.fit() - f = tf.io.gfile.GFile("ut.pb", "wb") - f.write(output_graph.graph_def.SerializeToString()) - for node in output_graph.graph_def.node: - if node.name == "conv3_eightbit_requantize": - self.assertTrue("Quantized" in node.op) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/adaptor/tensorflow_adaptor/test_tensorflow_graph_conv_fusion.py b/test/adaptor/tensorflow_adaptor/test_tensorflow_graph_conv_fusion.py deleted file mode 100644 index 1d10d5287d8..00000000000 --- a/test/adaptor/tensorflow_adaptor/test_tensorflow_graph_conv_fusion.py +++ /dev/null @@ -1,596 +0,0 @@ -# -# -*- coding: utf-8 -*- -# -import os -import platform -import unittest - -import numpy as np -import tensorflow as tf -import yaml -from tensorflow.compat.v1 import graph_util - -import neural_compressor -from neural_compressor.adaptor.tensorflow import TensorflowQuery -from neural_compressor.adaptor.tf_utils.graph_rewriter.generic.fold_batch_norm import FoldBatchNormNodesOptimizer -from neural_compressor.adaptor.tf_utils.graph_rewriter.generic.strip_unused_nodes import StripUnusedNodesOptimizer -from neural_compressor.adaptor.tf_utils.quantize_graph.quantize_graph_for_intel_cpu import QuantizeGraphForIntel -from neural_compressor.adaptor.tf_utils.util import disable_random - - -def build_fake_yaml(): - fake_yaml = """ - model: - name: fake_yaml - framework: tensorflow - inputs: input - device: cpu - quantization: - model_wise: - weight: - granularity: per_tensor - scheme: sym - dtype: int8 - algorithm: minmax - evaluation: - accuracy: - metric: - topk: 1 - tuning: - strategy: - name: basic - accuracy_criterion: - relative: 0.1 - exit_policy: - performance_only: True - workspace: - path: saved - """ - y = yaml.load(fake_yaml, Loader=yaml.SafeLoader) - with open("fake_yaml.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - f.close() - - -class TestConvBiasAddAddReluFusion(unittest.TestCase): - @classmethod - def setUpClass(self): - build_fake_yaml() - - @classmethod - def tearDownClass(self): - os.remove("fake_yaml.yaml") - - @disable_random() - def test_conv_relu_fusion(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - top_relu = tf.nn.relu(x) - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(top_relu, paddings, "CONSTANT") - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv2d(x_pad, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - relu = tf.nn.relu(conv) - - relu6 = tf.nn.relu6(relu, name="op_to_store") - - out_name = relu6.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - found_conv_fusion = True - - for i in output_graph.graph_def.node: - if i.op == "Relu": - found_conv_fusion = False - break - - self.assertEqual(found_conv_fusion, False) - - @disable_random() - @unittest.skipIf(tf.__version__ < "2.0", "does not support on 1.15up3") - def test_depthwiseconv_biasadd_fusion(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - top_relu = tf.nn.relu(x) - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(top_relu, paddings, "CONSTANT") - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.depthwise_conv2d(x_pad, conv_weights, strides=[1, 1, 1, 1], padding="VALID") - - normed = tf.compat.v1.layers.batch_normalization(conv, name="op_to_store") - out_name = normed.name.split(":")[0] - - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - found_conv_fusion = False - - for i in output_graph.graph_def.node: - if i.op == "QuantizedDepthwiseConv2DWithBias": - found_conv_fusion = True - break - - self.assertEqual(found_conv_fusion, True) - - @disable_random() - def test_depthwiseconv_biasadd_fusion_with_negative_input(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(x, paddings, "CONSTANT") - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.depthwise_conv2d(x_pad, conv_weights, strides=[1, 1, 1, 1], padding="VALID") - - normed = tf.compat.v1.layers.batch_normalization(conv, name="op_to_store") - out_name = normed.name.split(":")[0] - - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - found_conv_fusion = False - - for i in output_graph.graph_def.node: - if i.op == "QuantizedDepthwiseConv2DWithBias": - found_conv_fusion = True - break - - self.assertEqual(found_conv_fusion, False) - - @unittest.skipUnless( - bool(tf.version.VERSION.find("1.15.0-up") != -1 or tf.version.VERSION >= "2.1.0"), - "not supported the current tf version.", - ) - @disable_random() - def test_conv_biasadd_relu6_fusion(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(x, paddings, "CONSTANT") - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv2d(x_pad, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - normed = tf.compat.v1.layers.batch_normalization(conv) - - relu6 = tf.nn.relu6(normed, name="op_to_store") - - out_name = relu6.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - found_conv_fusion = True - - for i in output_graph.graph_def.node: - if i.op == "Relu6": - found_conv_fusion = False - break - self.assertEqual(found_conv_fusion, True) - - @disable_random() - def test_conv_biasadd_add_relu_fusion(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - top_relu = tf.nn.relu(x) - - conv_weights2 = tf.compat.v1.get_variable( - "weight2", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv2 = tf.nn.conv2d(top_relu, conv_weights2, strides=[1, 2, 2, 1], padding="SAME") - normed2 = tf.nn.bias_add(conv2, tf.constant([3.0, 1.2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 12, 2, 3, 4])) - relu = tf.nn.relu(normed2 + tf.constant([3.0])) - relu6 = tf.nn.relu6(relu, name="op_to_store") - - out_name = relu6.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - - found_conv_fusion = False - - for i in output_graph.graph_def.node: - if i.op.find("QuantizedConv2D") != -1: - found_conv_fusion = True - break - - self.assertEqual(found_conv_fusion, True) - - @disable_random() - def test_conv_squeeze_biasadd_relu_fusion(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - top_relu = tf.nn.relu(x) - - conv_weights2 = tf.compat.v1.get_variable( - "weight2", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv2 = tf.nn.conv2d(top_relu, conv_weights2, strides=[1, 2, 2, 1], padding="SAME") - squeeze = tf.squeeze(conv2) - normed2 = tf.nn.bias_add(conv2, tf.constant([3.0, 1.2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 12, 2, 3, 4])) - relu = tf.nn.relu(normed2) - identity = tf.identity(relu, name="op_to_store") - - out_name = identity.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - - correct_conv_fusion = False - - for i in output_graph.graph_def.node: - if i.op == "QuantizedConv2DWithBiasAndReluAndRequantize": - correct_conv_fusion = True - break - - self.assertEqual(correct_conv_fusion, True) - - @disable_random() - def test_conv_biasadd_addv2_relu_fallback_fusion_1(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - top_relu = tf.nn.leaky_relu(x) - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(x, paddings, "CONSTANT") - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv2d(x_pad, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - normed = tf.compat.v1.layers.batch_normalization(conv) - # relu = tf.nn.relu(normed) - - conv_weights2 = tf.compat.v1.get_variable( - "weight2", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv2 = tf.nn.conv2d(top_relu, conv_weights2, strides=[1, 2, 2, 1], padding="SAME") - normed2 = tf.compat.v1.layers.batch_normalization(conv2) - # relu2 = tf.nn.relu(normed2) - add = tf.raw_ops.AddV2(x=normed, y=normed2, name="addv2") - relu = tf.nn.relu(add) - relu6 = tf.nn.relu6(relu, name="op_to_store") - - out_name = relu6.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - - found_conv_fusion = False - - for i in output_graph.graph_def.node: - if i.op == "QuantizedConv2DWithBiasAndRequantize": - found_conv_fusion = True - break - - self.assertEqual(found_conv_fusion, True) - - @disable_random() - def test_conv_biasadd_addv2_relu_fallback_fusion_2(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - top_relu = tf.nn.relu(x) - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(top_relu, paddings, "CONSTANT") - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv2d(x_pad, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - normed = tf.compat.v1.layers.batch_normalization(conv) - # relu = tf.nn.relu(normed) - - conv_weights2 = tf.compat.v1.get_variable( - "weight2", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv2 = tf.nn.conv2d(top_relu, conv_weights2, strides=[1, 2, 2, 1], padding="SAME") - normed2 = tf.compat.v1.layers.batch_normalization(conv2) - # relu2 = tf.nn.relu(normed2) - add = tf.raw_ops.AddV2(x=normed, y=normed2, name="addv2") - relu = tf.nn.relu(add) - relu6 = tf.nn.relu6(relu, name="op_to_store") - - out_name = relu6.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - - found_conv_fusion = False - - for i in output_graph.graph_def.node: - if i.op == "QuantizedConv2DWithBiasSignedSumAndReluAndRequantize": - found_conv_fusion = True - break - - self.assertEqual(found_conv_fusion, True) - - @disable_random() - def test_conv_fusion_with_last_matmul(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - top_relu = tf.nn.relu(x) - # paddings = tf.constant([[0, 0], [1, 1], [1, 1], [0, 0]]) - # x_pad = tf.pad(top_relu, paddings, "CONSTANT") - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv2d(top_relu, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - normed = tf.compat.v1.layers.batch_normalization(conv) - - relu = tf.nn.relu(normed) - pooling = tf.nn.max_pool(relu, ksize=1, strides=[1, 2, 2, 1], padding="SAME") - reshape = tf.reshape(pooling, [-1, 3136]) - - y_data = np.random.random([3136, 1]) - - y = tf.constant(y_data, dtype=tf.float32, shape=[3136, 1]) - z = tf.matmul(reshape, y) - relu1 = tf.nn.relu(z) - y_data_1 = np.random.random([1, 1]) - y_1 = tf.constant(y_data_1, dtype=tf.float32, shape=[1, 1]) - - z_2nd_matmul = tf.matmul(relu1, y_1) - relu6 = tf.nn.relu6(z_2nd_matmul, name="op_to_store") - - out_name = relu6.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - - quantize_v2_count = 0 - for i in output_graph.graph_def.node: - if i.op == "QuantizeV2": - quantize_v2_count += 1 - break - - self.assertEqual(quantize_v2_count, 1) - - @disable_random() - def test_conv_fusion_with_last_conv(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - top_relu = tf.nn.relu(x) - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv2d(top_relu, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - normed = tf.compat.v1.layers.batch_normalization(conv) - - relu = tf.nn.relu(normed) - pooling = tf.nn.max_pool(relu, ksize=1, strides=[1, 2, 2, 1], padding="SAME") - conv_weights_2 = tf.compat.v1.get_variable( - "weight2", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv2 = tf.nn.conv2d(pooling, conv_weights_2, strides=[1, 2, 2, 1], padding="VALID") - conv_weights_3 = tf.compat.v1.get_variable( - "weight3", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - relu2 = tf.nn.relu(conv2) - conv3 = tf.nn.conv2d(relu2, conv_weights_3, strides=[1, 2, 2, 1], padding="VALID") - - relu3 = tf.nn.relu(conv3) - relu6 = tf.nn.relu6(relu3, name="op_to_store") - - out_name = relu6.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - - quantize_v2_count = 0 - for i in output_graph.graph_def.node: - if i.op == "QuantizeV2": - quantize_v2_count += 1 - break - - self.assertEqual(quantize_v2_count, 1) - - @disable_random() - def test_conv_fusion_with_max_pooling(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - - relu = tf.nn.relu(x) - pooling = tf.nn.max_pool(relu, ksize=1, strides=[1, 2, 2, 1], padding="SAME") - conv_weights = tf.compat.v1.get_variable( - "weight2", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv2d(pooling, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - biasadd = tf.compat.v1.layers.batch_normalization(conv, name="op_to_store") - out_name = biasadd.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - - quantized_pool_data_type = None - quantized_conv_data_type = None - for i in output_graph.graph_def.node: - if i.op.find("QuantizedMaxPool") != -1: - quantized_pool_data_type = i.attr["T"].type - if i.op.find("QuantizedConv2D") != -1: - quantized_conv_data_type = i.attr["Tinput"].type - - self.assertNotEqual(quantized_pool_data_type, None) - self.assertEqual(quantized_pool_data_type, quantized_conv_data_type) - - -class TestGraphConvFusion(unittest.TestCase): - rn50_fp32_pb_url = ( - "https://storage.googleapis.com/intel-optimized-tensorflow/models/v1_6/resnet50_fp32_pretrained_model.pb" - ) - pb_path = "/tmp/.neural_compressor/resnet50_fp32_pretrained_model.pb" - platform = platform.system().lower() - if platform == "windows": - pb_path = "C:\\tmp\.neural_compressor\\resnet50_fp32_pretrained_model.pb" - inputs = ["input"] - outputs = ["predict"] - - op_wise_config = { - "v0/resnet_v13/conv14/conv2d/Conv2D": (False, "minmax", False, 7.0), - "v0/resnet_v13/conv11/conv2d/Conv2D": (False, "minmax", False, 7.0), - "v0/resnet_v17/conv27/conv2d/Conv2D": (False, "minmax", False, 7.0), - } - - @classmethod - def setUpClass(self): - if not os.path.exists(self.pb_path): - if self.platform == "linux": - os.system( - "mkdir -p /tmp/.neural_compressor && wget {} -O {} ".format(self.rn50_fp32_pb_url, self.pb_path) - ) - elif self.platform == "windows": - os.system("md C:\\tmp\.neural_compressor && cd C:\\tmp\.neural_compressor") - from urllib import request - - request.urlretrieve(self.rn50_fp32_pb_url) - self.input_graph = tf.compat.v1.GraphDef() - with open(self.pb_path, "rb") as f: - self.input_graph.ParseFromString(f.read()) - - def test_conv_biasadd_relu_fusion(self): - tf.compat.v1.disable_eager_execution() - - self._tmp_graph_def = graph_util.remove_training_nodes(self.input_graph, self.outputs) - - self._tmp_graph_def = StripUnusedNodesOptimizer( - self._tmp_graph_def, self.inputs, self.outputs - ).do_transformation() - - self._tmp_graph_def = FoldBatchNormNodesOptimizer(self._tmp_graph_def).do_transformation() - op_wise_sequences = TensorflowQuery( - local_config_file=os.path.join(os.path.dirname(neural_compressor.__file__), "adaptor/tensorflow.yaml") - ).get_eightbit_patterns() - - output_graph, _, _ = QuantizeGraphForIntel( - self._tmp_graph_def, self.inputs, self.outputs, self.op_wise_config, op_wise_sequences, "cpu" - ).do_transform() - - node_name_type_mapping = {} - for i in output_graph.node: - node_name_type_mapping[i.name] = i.op - - should_disable_sum_node_name = "v0/resnet_v17/conv27/conv2d/Conv2D_eightbit_quantized_conv" - should_enable_sum_node_name = "v0/resnet_v13/conv11/conv2d/Conv2D_eightbit_quantized_conv" - should_disable_sum_flag = ( - should_disable_sum_node_name in node_name_type_mapping - and node_name_type_mapping[should_disable_sum_node_name] == "QuantizedConv2DWithBias" - ) - should_enable_sum_flag = ( - should_enable_sum_node_name in node_name_type_mapping - and node_name_type_mapping[should_enable_sum_node_name] == "QuantizedConv2DWithBiasSumAndRelu" - ) - self.assertEqual(should_enable_sum_flag, True) - self.assertEqual(should_disable_sum_flag, True) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/adaptor/tensorflow_adaptor/test_tensorflow_graph_debug_mode.py b/test/adaptor/tensorflow_adaptor/test_tensorflow_graph_debug_mode.py deleted file mode 100644 index e522f47c68e..00000000000 --- a/test/adaptor/tensorflow_adaptor/test_tensorflow_graph_debug_mode.py +++ /dev/null @@ -1,99 +0,0 @@ -import logging -import os -import unittest - -import tensorflow as tf -import yaml -from tensorflow.compat.v1 import graph_util - -from neural_compressor.adaptor.tf_utils.util import disable_random - -logger = logging.getLogger("neural_compressor") -logger.setLevel(logging.DEBUG) - - -def build_fake_yaml(): - fake_yaml = """ - model: - name: fake_yaml - framework: tensorflow - inputs: input - outputs: op_to_store - device: cpu - quantization: - model_wise: - weight: - granularity: per_tensor - scheme: sym - dtype: int8 - algorithm: minmax - evaluation: - accuracy: - metric: - topk: 1 - tuning: - strategy: - name: mse - accuracy_criterion: - relative: 0.01 - exit_policy: - performance_only: True - workspace: - path: saved - """ - y = yaml.load(fake_yaml, Loader=yaml.SafeLoader) - with open("fake_yaml.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - f.close() - - -class TestTensorflowGraphAdaptorDebugMode(unittest.TestCase): - @classmethod - def setUpClass(self): - build_fake_yaml() - - @classmethod - def tearDownClass(self): - os.remove("fake_yaml.yaml") - - @disable_random() - def test_graph_adaptor_debug_mode(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - top_relu = tf.nn.relu(x) - - conv_weights = tf.compat.v1.get_variable( - "weight2", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv2d(top_relu, conv_weights, strides=[1, 2, 2, 1], padding="SAME") - normed = tf.nn.bias_add(conv, tf.constant([3.0, 1.2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 12, 2, 3, 4])) - relu = tf.nn.relu(normed + tf.constant([3.0])) - relu6 = tf.nn.relu6(relu, name="op_to_store") - - out_name = relu6.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - - found_conv_fusion = False - - for i in output_graph.graph_def.node: - if i.op.find("QuantizedConv2D") != -1: - found_conv_fusion = True - break - - self.assertEqual(found_conv_fusion, True) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/adaptor/tensorflow_adaptor/test_tensorflow_graph_dump_tensor.py b/test/adaptor/tensorflow_adaptor/test_tensorflow_graph_dump_tensor.py deleted file mode 100644 index 6dd17807807..00000000000 --- a/test/adaptor/tensorflow_adaptor/test_tensorflow_graph_dump_tensor.py +++ /dev/null @@ -1,161 +0,0 @@ -# -# -*- coding: utf-8 -*- -# -import os -import unittest - -import numpy as np -import yaml - -np.random.seed(0) - - -def build_fake_yaml(): - fake_yaml = """ - model: - name: fake_yaml - framework: tensorflow - inputs: input - outputs: op_to_store - device: cpu - quantization: - model_wise: - weight: - granularity: per_tensor - scheme: sym - dtype: int8 - algorithm: minmax - evaluation: - accuracy: - metric: - topk: 1 - tuning: - strategy: - name: mse - accuracy_criterion: - relative: -0.01 - workspace: - path: saved - """ - y = yaml.load(fake_yaml, Loader=yaml.SafeLoader) - with open("fake_yaml.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - f.close() - - -def build_fake_yaml_kl(): - fake_yaml = """ - model: - name: fake_yaml - framework: tensorflow - inputs: input - outputs: op_to_store - device: cpu - quantization: - optimization: - arithmetic: False # optional. grappler arithmetic optimizer,default value is True. - model_wise: - activation: - algorithm: kl - weight: - granularity: per_tensor - scheme: sym - dtype: int8 - algorithm: minmax - evaluation: - accuracy: - metric: - topk: 1 - tuning: - strategy: - name: basic - accuracy_criterion: - relative: 0.99 - workspace: - path: saved - """ - y = yaml.load(fake_yaml, Loader=yaml.SafeLoader) - with open("fake_yaml_kl.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - f.close() - - -def build_fake_model(): - import tensorflow as tf - - graph = tf.Graph() - graph_def = tf.compat.v1.GraphDef() - - with tf.compat.v1.Session() as sess: - tf.compat.v1.set_random_seed(0) - x = tf.compat.v1.placeholder(tf.float32, [1, 30, 30, 1], name="input") - conv_weights = tf.compat.v1.get_variable( - "weight", [2, 2, 1, 1], initializer=tf.compat.v1.random_normal_initializer() - ) - - beta = tf.compat.v1.get_variable(name="beta", shape=[1], initializer=tf.compat.v1.random_normal_initializer()) - gamma = tf.compat.v1.get_variable(name="gamma", shape=[1], initializer=tf.compat.v1.random_normal_initializer()) - - x = tf.nn.relu(x) - conv1 = tf.nn.conv2d(x, conv_weights, strides=[1, 2, 2, 1], padding="SAME", name="last") - conv_bias = tf.compat.v1.layers.batch_normalization(conv1) - x = tf.nn.relu(conv_bias) - pool = tf.nn.max_pool(x, ksize=1, strides=[1, 2, 2, 1], padding="SAME") - - final_node = tf.nn.relu(pool, name="op_to_store") - sess.run(tf.compat.v1.global_variables_initializer()) - constant_graph = tf.compat.v1.graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[final_node.name.split(":")[0]] - ) - - graph_def.ParseFromString(constant_graph.SerializeToString()) - - with graph.as_default(): - tf.import_graph_def(graph_def, name="") - return graph - - -class TestGraphDumpToDisk(unittest.TestCase): - @classmethod - def setUpClass(self): - self.constant_graph = build_fake_model() - build_fake_yaml() - build_fake_yaml_kl() - self.kl_log_path = os.path.join(os.getcwd(), "saved/kl.log") - self.calibration_log_path = os.path.join(os.getcwd(), "saved/requant_min_max.log") - - @classmethod - def tearDownClass(self): - os.remove("fake_yaml.yaml") - os.remove("fake_yaml_kl.yaml") - os.remove(self.calibration_log_path) - - def test_dump_tensor_to_disk(self): - import tensorflow.compat.v1 as tf - - from neural_compressor.experimental import Quantization, common - - tf.disable_v2_behavior() - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 30, 30, 1), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.model = self.constant_graph - quantizer.fit() - - with open(self.calibration_log_path) as f: - data = f.readlines() - - found_kl = False - for i in data: - if i.find("Relu_1__print__;__KL:") != -1: - found_kl = True - - self.assertEqual(os.path.exists(self.calibration_log_path), True) - self.assertGreater(len(data), 1) - self.assertEqual(found_kl, True) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/adaptor/tensorflow_adaptor/test_tensorflow_graph_input_output.py b/test/adaptor/tensorflow_adaptor/test_tensorflow_graph_input_output.py index 1d320201488..2976fd75e28 100644 --- a/test/adaptor/tensorflow_adaptor/test_tensorflow_graph_input_output.py +++ b/test/adaptor/tensorflow_adaptor/test_tensorflow_graph_input_output.py @@ -12,63 +12,6 @@ from neural_compressor.adaptor.tf_utils.util import get_input_output_node_names -def build_fake_yaml(): - fake_yaml = """ - model: - name: fake_yaml - framework: tensorflow - device: cpu - quantization: - model_wise: - weight: - granularity: per_tensor - scheme: sym - dtype: int8 - algorithm: minmax - evaluation: - accuracy: - metric: - topk: 1 - tuning: - strategy: - name: basic - accuracy_criterion: - relative: 0.1 - workspace: - path: saved - """ - y = yaml.load(fake_yaml, Loader=yaml.SafeLoader) - with open("fake_yaml.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - f.close() - - -def build_fake_yaml_2(): - fake_yaml = """ - model: - name: fake_yaml - framework: tensorflow - device: cpu - evaluation: - accuracy: - metric: - topk: 1 - tuning: - strategy: - name: bayesian - accuracy_criterion: - relative: 0.01 - exit_policy: - performance_only: True - workspace: - path: saved - """ - y = yaml.load(fake_yaml, Loader=yaml.SafeLoader) - with open("fake_yaml_2.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - f.close() - - def build_fake_model_1(): with tf.compat.v1.Session(graph=tf.Graph()) as sess: dataset = tf.data.Dataset.range(10) @@ -124,8 +67,6 @@ class TestGraphInputOutputDetection(unittest.TestCase): @classmethod def setUpClass(self): - build_fake_yaml() - build_fake_yaml_2() if not os.path.exists(self.pb_path): if self.platform == "linux": os.system( @@ -146,8 +87,6 @@ def setUpClass(self): @classmethod def tearDownClass(self): - os.remove("fake_yaml.yaml") - os.remove("fake_yaml_2.yaml") os.remove("model_1.pb") os.remove("model_2.pb") os.remove("model_3.pb") @@ -202,40 +141,6 @@ def test_identify_input_output(self): self.assertEqual(inputs, []) self.assertEqual(outputs, []) - def test_no_input_output_config(self): - g = GraphAnalyzer() - g.graph = self.input_graph - g.parse_graph() - - float_graph_def = g.dump_graph() - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(20, 224, 224, 3), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.eval_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.model = float_graph_def - output_graph = quantizer.fit() - self.assertGreater(len(output_graph.graph_def.node), 0) - - def test_invalid_input_output_config(self): - g = GraphAnalyzer() - g.graph = self.input_graph - g.parse_graph() - - float_graph_def = g.dump_graph() - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml_2.yaml") - dataset = quantizer.dataset("dummy", shape=(20, 224, 224, 3), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.eval_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.model = float_graph_def - model = quantizer.fit() - # will detect the right inputs/outputs - self.assertNotEqual(model.input_node_names, ["x"]) - self.assertNotEqual(model.output_node_names, ["op_to_store"]) - if __name__ == "__main__": unittest.main() diff --git a/test/adaptor/tensorflow_adaptor/test_tensorflow_graph_matmul_fusion.py b/test/adaptor/tensorflow_adaptor/test_tensorflow_graph_matmul_fusion.py deleted file mode 100644 index c17d0facaa6..00000000000 --- a/test/adaptor/tensorflow_adaptor/test_tensorflow_graph_matmul_fusion.py +++ /dev/null @@ -1,484 +0,0 @@ -# -# -*- coding: utf-8 -*- -# -import os -import unittest - -import numpy as np -import tensorflow.compat.v1 as tf -import yaml -from tensorflow.python.framework import dtypes - -import neural_compressor -from neural_compressor.adaptor.tensorflow import TensorflowQuery -from neural_compressor.adaptor.tf_utils.util import disable_random - - -def build_fake_yaml(): - fake_yaml = """ - model: - name: fake_yaml - framework: tensorflow - inputs: x - outputs: op_to_store - device: cpu - evaluation: - accuracy: - metric: - topk: 1 - tuning: - strategy: - name: basic - accuracy_criterion: - relative: 0.01 - exit_policy: - performance_only: True - workspace: - path: saved - """ - y = yaml.load(fake_yaml, Loader=yaml.SafeLoader) - with open("fake_yaml.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - f.close() - - -class TestGraphMatMulFusion(unittest.TestCase): - @classmethod - def setUpClass(self): - build_fake_yaml() - self.op_wise_sequences = TensorflowQuery( - local_config_file=os.path.join(os.path.dirname(neural_compressor.__file__), "adaptor/tensorflow.yaml") - ).get_eightbit_patterns() - - @classmethod - def tearDownClass(self): - os.remove("fake_yaml.yaml") - - @disable_random() - def test_matmul_biasadd_relu_requantize_fusion(self): - g = tf.Graph() - with g.as_default(): - x_data = np.array([[0.1, 0.2], [0.2, 0.3]]) - y_data = np.array([[1, 2], [3, 4]], dtype=np.float32) - x = tf.placeholder(tf.float32, shape=[2, 2], name="x") - y = tf.constant(y_data, dtype=tf.float32, shape=[2, 2]) - z = tf.matmul(x, y) - z = tf.nn.bias_add(z, [1, 2]) - z = tf.nn.relu(z, name="op_to_store") - found_quantized_matmul = False - with tf.Session() as sess: - sess.run(z, feed_dict={x: x_data, y: y_data}) - float_graph_def = sess.graph.as_graph_def() - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(2, 2), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.eval_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.model = float_graph_def - output_graph = quantizer.fit() - - for i in output_graph.graph_def.node: - if i.op == "QuantizedMatMulWithBiasAndReluAndRequantize": - found_quantized_matmul = True - break - self.assertEqual(found_quantized_matmul, True) - - @disable_random() - def test_first_matmul_biasadd_relu_fusion(self): - x_data = np.array([[0.1, 0.2], [0.2, 0.3]]) - y_data = np.array([[1, 2], [3, 4]], dtype=np.float32) - x = tf.placeholder(tf.float32, shape=[2, 2], name="x") - y = tf.constant(y_data, dtype=tf.float32, shape=[2, 2]) - z = tf.matmul(x, y) - z = tf.nn.bias_add(z, [1, 2]) - z = tf.nn.relu(z, name="op_to_store") - - with tf.Session() as sess: - sess.run(z, feed_dict={x: x_data, y: y_data}) - float_graph_def = sess.graph.as_graph_def() - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(2, 2), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.eval_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.model = float_graph_def - output_graph = quantizer.fit() - - found_quantized_matmul = False - for i in output_graph.graph_def.node: - if ( - i.op == "QuantizeV2" - and i.name == "MatMul_eightbit_quantize_x" - and i.attr["T"].type == dtypes.quint8 - ): - found_quantized_matmul = True - break - - self.assertEqual(found_quantized_matmul, True) - - @disable_random() - def test_matmul_biasadd_requantize_dequantize_fusion(self): - g = tf.Graph() - with g.as_default(): - x_data = np.array([[0.1, 0.2], [0.2, 0.3]]) - y_data = np.array([[1, 2], [3, 4]], dtype=np.float32) - x = tf.placeholder(tf.float32, shape=[2, 2], name="x") - y = tf.constant(y_data, dtype=tf.float32, shape=[2, 2]) - z = tf.matmul(x, y) - z = tf.nn.bias_add(z, [1, 2]) - z = tf.identity(z, name="op_to_store") - found_quantized_matmul = False - if tf.version.VERSION < "2.2.0": - found_quantized_matmul = True - else: - with tf.Session() as sess: - sess.run(z, feed_dict={x: x_data, y: y_data}) - float_graph_def = sess.graph.as_graph_def() - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(2, 2), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.eval_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.model = float_graph_def - output_graph = quantizer.fit() - - for i in output_graph.graph_def.node: - if i.op == "QuantizedMatMulWithBiasAndDequantize": - found_quantized_matmul = True - break - self.assertEqual(found_quantized_matmul, True) - - @disable_random() - def test_matmul_biasadd_requantize_dequantize_last_fusion(self): - g = tf.Graph() - with g.as_default(): - x_data = np.array([[0.1, 0.2], [0.2, 0.3]]) - y_data = np.array([[1, 2], [3, 4]], dtype=np.float32) - x = tf.placeholder(tf.float32, shape=[2, 2], name="x") - y = tf.constant(y_data, dtype=tf.float32, shape=[2, 2]) - z = tf.matmul(x, y) - z = tf.nn.bias_add(z, [1, 2], name="op_to_store") - found_quantized_matmul = False - if tf.version.VERSION < "2.2.0": - found_quantized_matmul = True - else: - with tf.Session() as sess: - sess.run(z, feed_dict={x: x_data, y: y_data}) - float_graph_def = sess.graph.as_graph_def() - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(2, 2), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.eval_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.model = float_graph_def - output_graph = quantizer.fit() - - for i in output_graph.graph_def.node: - if i.op == "QuantizedMatMulWithBiasAndDequantize" and i.name == "op_to_store": - found_quantized_matmul = True - break - self.assertEqual(found_quantized_matmul, True) - - @disable_random() - def test_disable_matmul_fusion(self): - g = tf.Graph() - with g.as_default(): - x_data = np.array([[0.1, 0.2], [0.2, 0.3]]) - y_data = np.array([[1, 2], [3, 4]], dtype=np.float32) - x = tf.placeholder(tf.float32, shape=[2, 2], name="x") - y = tf.constant(y_data, dtype=tf.float32, shape=[2, 2]) - z = tf.matmul(x, y, name="no_quant_matmul") - z = tf.nn.relu6(z, name="op_to_store") - found_quantized_matmul = False - - with tf.Session() as sess: - sess.run(z, feed_dict={x: x_data, y: y_data}) - float_graph_def = sess.graph.as_graph_def() - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(2, 2), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.eval_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.model = float_graph_def - output_graph = quantizer.fit() - - for i in output_graph.graph_def.node: - if i.op == "QuantizedMatMulWithBiasAndDequantize" and i.name == "op_to_store": - found_quantized_matmul = True - break - self.assertEqual(found_quantized_matmul, False) - - @disable_random() - def test_disable_matmul_fusion_with_transpose_b_true(self): - g = tf.Graph() - with g.as_default(): - x_data = np.array([[0.1, 0.2], [0.2, 0.3]]) - y_data = np.array([[1, 2], [3, 4]], dtype=np.float32) - x = tf.placeholder(tf.float32, shape=[2, 2], name="x") - y = tf.constant(y_data, dtype=tf.float32, shape=[2, 2]) - z = tf.matmul(x, y, name="no_quant_matmul", transpose_b=True) - z = tf.nn.relu6(z, name="op_to_store") - found_quantized_matmul = False - - with tf.Session() as sess: - sess.run(z, feed_dict={x: x_data, y: y_data}) - float_graph_def = sess.graph.as_graph_def() - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(2, 2), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.eval_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.model = float_graph_def - output_graph = quantizer.fit() - - for i in output_graph.graph_def.node: - if i.op == "QuantizedMatMulWithBiasAndDequantize" and i.name == "op_to_store": - found_quantized_matmul = True - break - self.assertEqual(found_quantized_matmul, False) - - @disable_random() - @unittest.skipIf(float(tf.__version__[:3]) > 2.7, "only tf lower than 2.8 enable dummy biasadd") - def test_matmul_with_dummy_biasadd(self): - g = tf.Graph() - with g.as_default(): - x_data = np.array([[0.1, 0.2], [0.2, 0.3]]) - y_data = np.array([[1, 2], [3, 4]], dtype=np.float32) - x = tf.placeholder(tf.float32, shape=[2, 2], name="x") - y = tf.constant(y_data, dtype=tf.float32, shape=[2, 2]) - z = tf.matmul(x, y, name="no_quant_matmul") - z = tf.identity(z, name="op_to_store") - found_quantized_matmul = True - - with tf.Session() as sess: - sess.run(z, feed_dict={x: x_data, y: y_data}) - float_graph_def = sess.graph.as_graph_def() - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(2, 2), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.eval_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.model = float_graph_def - output_graph = quantizer.fit() - - for i in output_graph.graph_def.node: - if i.op == "MatMul": - found_quantized_matmul = False - break - self.assertEqual(found_quantized_matmul, True) - - @disable_random() - @unittest.skipIf(float(tf.__version__[:3]) > 2.7, "only tf lower than 2.8 enable dummy biasadd") - def test_matmul_with_nan(self): - g = tf.Graph() - with g.as_default(): - x_data = np.array([[0.1, 0.2], [0.2, 0.3]]) - nan_array = np.empty((2, 2), dtype=np.float32) - nan_array[:] = np.NaN - x = tf.placeholder(tf.float32, shape=[2, 2], name="x") - z = tf.matmul(x, nan_array, name="no_quant_matmul") - z = tf.identity(z, name="op_to_store") - found_quantized_matmul = True - - with tf.Session() as sess: - sess.run(z, feed_dict={x: x_data}) - float_graph_def = sess.graph.as_graph_def() - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(2, 2), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.eval_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.model = float_graph_def - output_graph = quantizer.fit() - - for i in output_graph.graph_def.node: - if i.op == "MatMul": - found_quantized_matmul = False - break - self.assertEqual(found_quantized_matmul, True) - - @disable_random() - def test_matmul_with_reshape_transpose(self): - g = tf.Graph() - with g.as_default(): - x_data = np.array([[0.1, 0.2], [0.2, 0.3]]) - y_data = np.array([[1, 2], [3, 4]], dtype=np.float32) - x = tf.placeholder(tf.float32, shape=[2, 2], name="x") - y = tf.constant(y_data, dtype=tf.float32, shape=[2, 2]) - transpose = tf.transpose(y, perm=[1, 0]) - reshape = tf.reshape(transpose, [2, 2]) - z = tf.matmul(x, reshape, name="no_quant_matmul") - z = tf.nn.bias_add(z, [1, 2], name="op_to_store") - found_quantized_matmul = True - - with tf.Session() as sess: - sess.run(z, feed_dict={x: x_data, y: y_data}) - float_graph_def = sess.graph.as_graph_def() - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(2, 2), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.eval_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.model = float_graph_def - output_graph = quantizer.fit() - for i in output_graph.graph_def.node: - if i.op == "MatMul": - found_quantized_matmul = False - break - self.assertEqual(found_quantized_matmul, True) - - @disable_random() - def test_matmul_with_add(self): - g = tf.Graph() - with g.as_default(): - x_data = np.array([[0.1, 0.2], [0.2, 0.3]]) - y_data = np.array([[1, 2], [3, 4]], dtype=np.float32) - x = tf.placeholder(tf.float32, shape=[2, 2], name="x") - y = tf.constant(y_data, dtype=tf.float32, shape=[2, 2]) - transpose = tf.transpose(y, perm=[1, 0]) - reshape = tf.reshape(transpose, [2, 2]) - z = tf.matmul(x, reshape, name="no_quant_matmul") - z = tf.math.add(z, [1, 2], name="op_to_store") - found_quantized_matmul = True - - with tf.Session() as sess: - sess.run(z, feed_dict={x: x_data, y: y_data}) - float_graph_def = sess.graph.as_graph_def() - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(2, 2), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.eval_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.model = float_graph_def - output_graph = quantizer.fit() - for i in output_graph.graph_def.node: - if i.op == "MatMul": - found_quantized_matmul = False - break - self.assertEqual(found_quantized_matmul, True) - - @disable_random() - def test_matmul_biasadd_requantize_dequantize_fusion_with_softmax(self): - g = tf.Graph() - with g.as_default(): - x_data = np.array([[0.1, 0.2], [0.2, 0.3]]) - y_data = np.array([[1, 2], [3, 4]], dtype=np.float32) - x = tf.placeholder(tf.float32, shape=[2, 2], name="x") - y = tf.constant(y_data, dtype=tf.float32, shape=[2, 2]) - z = tf.matmul(x, y) - biasadd = tf.nn.bias_add(z, [1, 2]) - biasadd1 = tf.nn.bias_add(biasadd, [1, 1]) - - y1 = tf.constant(x_data, dtype=tf.float32, shape=[2, 2]) - matmul1 = tf.matmul(biasadd1, y1) - - biasadd2 = tf.nn.bias_add(matmul1, [1, 1]) - - z = tf.nn.softmax(biasadd2, name="op_to_store") - found_quantized_matmul = False - if tf.version.VERSION < "2.2.0": - found_quantized_matmul = False - else: - with tf.Session() as sess: - sess.run(z, feed_dict={x: x_data, y: y_data}) - float_graph_def = sess.graph.as_graph_def() - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(2, 2), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.eval_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.model = float_graph_def - output_graph = quantizer.fit() - count = 0 - for i in output_graph.model.as_graph_def().node: - if i.op == "QuantizedMatMulWithBiasAndDequantize": - count += 1 - found_quantized_matmul = bool(count > 1) - # TF2.6 has enabled matmul_biasadd_requantize_dequantize_fusion_with_softmax - if tf.__version__ < "2.6.0": - self.assertEqual(found_quantized_matmul, False) - else: - self.assertEqual(found_quantized_matmul, True) - - def test_matmul_biasadd_relu_non_const_weight(self): - g = tf.Graph() - with g.as_default(): - x_data = np.array([[0.1, 0.2], [0.2, 0.3]]) - x = tf.placeholder(tf.float32, shape=[2, 2], name="x") - y = tf.matmul(x, x, name="no_quant_matmul") - biasadd = tf.nn.bias_add(y, [1, 2]) - z = tf.nn.relu(biasadd) - found_quantized_matmul = True - - with tf.Session() as sess: - sess.run(z, feed_dict={x: x_data}) - float_graph_def = sess.graph.as_graph_def() - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(2, 2), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.eval_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.model = float_graph_def - output_graph = quantizer.fit() - - for i in output_graph.graph_def.node: - if i.op == "MatMul": - found_quantized_matmul = False - break - self.assertEqual(found_quantized_matmul, False) - - def test_matmul_biasadd_non_const_weight(self): - g = tf.Graph() - with g.as_default(): - x_data = np.array([[0.1, 0.2], [0.2, 0.3]]) - x = tf.placeholder(tf.float32, shape=[2, 2], name="x") - y = tf.matmul(x, x, name="no_quant_matmul") - z = tf.nn.bias_add(y, [1, 2]) - found_quantized_matmul = True - - with tf.Session() as sess: - sess.run(z, feed_dict={x: x_data}) - float_graph_def = sess.graph.as_graph_def() - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(2, 2), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.eval_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.model = float_graph_def - output_graph = quantizer.fit() - - for i in output_graph.graph_def.node: - if i.op == "MatMul": - found_quantized_matmul = False - break - self.assertEqual(found_quantized_matmul, False) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/adaptor/tensorflow_adaptor/test_tensorflow_graph_meta_pass.py b/test/adaptor/tensorflow_adaptor/test_tensorflow_graph_meta_pass.py deleted file mode 100644 index e676371f7fe..00000000000 --- a/test/adaptor/tensorflow_adaptor/test_tensorflow_graph_meta_pass.py +++ /dev/null @@ -1,202 +0,0 @@ -# -# -*- coding: utf-8 -*- -# -import os -import unittest - -import tensorflow as tf -import yaml -from tensorflow.compat.v1 import graph_util - -from neural_compressor.adaptor.tf_utils.util import disable_random - - -def build_fake_yaml(): - fake_yaml = """ - model: - name: fake_yaml - framework: tensorflow - inputs: input - outputs: op_to_store - device: cpu - quantization: - model_wise: - weight: - granularity: per_tensor - scheme: sym - dtype: int8 - algorithm: minmax - evaluation: - accuracy: - metric: - topk: 1 - tuning: - strategy: - name: basic - exit_policy: - performance_only: True - workspace: - path: saved - """ - y = yaml.load(fake_yaml, Loader=yaml.SafeLoader) - with open("fake_yaml.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - f.close() - - -class TestMetaPass(unittest.TestCase): - @classmethod - def setUpClass(self): - build_fake_yaml() - - @classmethod - def tearDownClass(self): - os.remove("fake_yaml.yaml") - - @disable_random() - def test_tensorflow_graph_meta_pass_with_different_mode(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - top_relu = tf.nn.relu(x) - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv2d(top_relu, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - normed = tf.compat.v1.layers.batch_normalization(conv) - - relu = tf.nn.relu(normed) - sq = tf.squeeze(relu, [0]) - reshape = tf.reshape(sq, [729, 16]) - conv_weights2 = tf.compat.v1.get_variable( - "weight2", [16, 729], initializer=tf.compat.v1.random_normal_initializer() - ) - - matmul = tf.matmul(reshape, conv_weights2) - # normed2 = tf.compat.v1.layers.batch_normalization(matmul) - bias = tf.compat.v1.get_variable("bias", [729], initializer=tf.compat.v1.random_normal_initializer()) - normed2 = tf.nn.bias_add(matmul, bias, name="bias_add") - - relu6 = tf.nn.relu6(normed2) - reshape2 = tf.reshape(relu6, [1, 729, 729, 1], name="op_to_store") - - out_name = reshape2.name.split(":")[0] - - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - - found_reshape = False - for i in output_graph.graph_def.node: - if i.op == "Reshape": - found_reshape = True - break - - self.assertEqual(found_reshape, True) - - @disable_random() - def test_tensorflow_graph_meta_pass_with_same_mode(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - top_relu = tf.nn.relu(x) - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv2d(top_relu, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - normed = tf.compat.v1.layers.batch_normalization(conv) - - relu = tf.nn.relu(normed) - sq = tf.squeeze(relu, [0]) - reshape = tf.reshape(sq, [1, 27, 27, 16]) - conv_weights2 = tf.compat.v1.get_variable( - "weight2", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv2 = tf.nn.conv2d(reshape, conv_weights2, strides=[1, 2, 2, 1], padding="VALID") - normed2 = tf.compat.v1.layers.batch_normalization(conv2) - - relu6 = tf.nn.relu6(normed2, name="op_to_store") - - out_name = relu6.name.split(":")[0] - - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - quantize_count = 0 - dequantize_count = 0 - - for i in output_graph.graph_def.node: - if i.op == "QuantizeV2": - quantize_count += 1 - if i.op == "Dequantize": - dequantize_count += 1 - - self.assertEqual(quantize_count, 1) - self.assertEqual(dequantize_count, 1) - - @disable_random() - def test_tensorflow_graph_meta_with_reshape_only(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - top_relu = tf.nn.relu(x) - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv2d(top_relu, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - normed = tf.compat.v1.layers.batch_normalization(conv) - - relu = tf.nn.relu(normed) - reshape = tf.reshape(relu, [1, 27, 27, 16]) - conv_weights2 = tf.compat.v1.get_variable( - "weight2", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv2 = tf.nn.conv2d(reshape, conv_weights2, strides=[1, 2, 2, 1], padding="VALID") - normed2 = tf.compat.v1.layers.batch_normalization(conv2) - - relu6 = tf.nn.relu6(normed2, name="op_to_store") - - out_name = relu6.name.split(":")[0] - - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - quantize_count = 0 - dequantize_count = 0 - - for i in output_graph.graph_def.node: - if i.op == "QuantizeV2": - quantize_count += 1 - if i.op == "Dequantize": - dequantize_count += 1 - - self.assertEqual(quantize_count, 1) - self.assertEqual(dequantize_count, 1) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/adaptor/tensorflow_adaptor/test_tensorflow_graph_pad_conv.py b/test/adaptor/tensorflow_adaptor/test_tensorflow_graph_pad_conv.py deleted file mode 100644 index 2be01967846..00000000000 --- a/test/adaptor/tensorflow_adaptor/test_tensorflow_graph_pad_conv.py +++ /dev/null @@ -1,179 +0,0 @@ -import os -import unittest - -import tensorflow as tf -import yaml -from tensorflow.compat.v1 import graph_util - -from neural_compressor.adaptor.tf_utils.util import disable_random - - -def build_fake_yaml(): - fake_yaml = """ - model: - name: fake_yaml - framework: tensorflow - inputs: input - outputs: op_to_store - device: cpu - quantization: - model_wise: - weight: - granularity: per_tensor - scheme: sym - dtype: int8 - algorithm: minmax - evaluation: - accuracy: - metric: - topk: 1 - tuning: - strategy: - name: mse - accuracy_criterion: - relative: 0.01 - exit_policy: - performance_only: True - workspace: - path: saved - """ - y = yaml.load(fake_yaml, Loader=yaml.SafeLoader) - with open("fake_yaml.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - f.close() - - -class TestFoldPadConv(unittest.TestCase): - @classmethod - def setUpClass(self): - build_fake_yaml() - - @classmethod - def tearDownClass(self): - os.remove("fake_yaml.yaml") - - @disable_random() - def test_fold_pad_conv(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(x, paddings, "CONSTANT") - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv2d(x_pad, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - normed = tf.compat.v1.layers.batch_normalization(conv) - relu = tf.nn.relu(normed, name="op_to_store") - out_name = relu.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - found_pad = False - - if tf.__version__ >= "2.0.0": - for i in output_graph.graph_def.node: - if i.op == "Pad": - found_pad = True - break - self.assertEqual(found_pad, True) - - @disable_random() - def test_fold_pad_conv2(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(x, paddings, "CONSTANT") - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv2d(x_pad, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - normed = tf.compat.v1.layers.batch_normalization(conv) - relu = tf.nn.relu(normed) - - paddings2 = tf.constant([[0, 0], [1, 1], [1, 1], [0, 0]]) - x_pad2 = tf.pad(x, paddings2, "CONSTANT") - conv_weights2 = tf.compat.v1.get_variable( - "weight2", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv2 = tf.nn.conv2d(x_pad2, conv_weights2, strides=[1, 2, 2, 1], padding="VALID") - normed2 = tf.compat.v1.layers.batch_normalization(conv2) - relu2 = tf.nn.relu(normed2) - add = tf.math.add(relu, relu2, name="op_to_store") - out_name = add.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - found_pad = False - - if tf.__version__ >= "2.0.0": - for i in output_graph.graph_def.node: - if i.op == "Pad": - found_pad = True - break - self.assertEqual(found_pad, True) - - @disable_random() - def test_fold_pad_conv3(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(x, paddings, "CONSTANT") - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv2d(x_pad, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - normed = tf.compat.v1.layers.batch_normalization(conv) - relu = tf.nn.relu(normed) - - conv_weights2 = tf.compat.v1.get_variable( - "weight2", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv2 = tf.nn.conv2d(x, conv_weights2, strides=[1, 2, 2, 1], padding="SAME") - normed2 = tf.compat.v1.layers.batch_normalization(conv2) - relu2 = tf.nn.relu(normed2) - add = tf.math.add(relu, relu2, name="op_to_store") - out_name = add.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - found_pad = False - - if tf.__version__ >= "2.0.0": - for i in output_graph.graph_def.node: - if i.op == "Pad": - found_pad = True - break - - self.assertEqual(found_pad, True) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/adaptor/tensorflow_adaptor/test_tensorflow_graph_post_cse_optimize.py b/test/adaptor/tensorflow_adaptor/test_tensorflow_graph_post_cse_optimize.py deleted file mode 100644 index 2732bf2ca6d..00000000000 --- a/test/adaptor/tensorflow_adaptor/test_tensorflow_graph_post_cse_optimize.py +++ /dev/null @@ -1,167 +0,0 @@ -import os -import unittest - -import numpy as np -import tensorflow as tf -import yaml -from tensorflow.compat.v1 import graph_util - -from neural_compressor.adaptor.tf_utils.util import disable_random - - -def build_fake_yaml(): - fake_yaml = """ - model: - name: fake_yaml - framework: tensorflow - inputs: input - outputs: op_to_store - device: cpu - quantization: - model_wise: - weight: - granularity: per_tensor - scheme: sym - dtype: int8 - algorithm: minmax - evaluation: - accuracy: - metric: - topk: 1 - tuning: - strategy: - name: mse - accuracy_criterion: - relative: 0.9 - exit_policy: - performance_only: True - workspace: - path: saved - """ - y = yaml.load(fake_yaml, Loader=yaml.SafeLoader) - with open("fake_yaml.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - f.close() - - -class TestPostCSEOptimizer(unittest.TestCase): - @classmethod - def setUpClass(self): - build_fake_yaml() - - import tensorflow as tf - - self.enable_s8 = bool(tf.version.VERSION.find("1.15.0-up") != -1 or tf.version.VERSION >= "2.1.0") - - @classmethod - def tearDownClass(self): - os.remove("fake_yaml.yaml") - - @disable_random() - def test_post_cse(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - x = tf.nn.relu(x) - xw = tf.constant(np.random.random((2, 2, 16, 16)), dtype=tf.float32, name="y") - x = tf.nn.conv2d(input=x, filters=xw, strides=[1, 1, 1, 1], padding="VALID") - - y = tf.constant(np.random.random((1, 55, 55, 16)), dtype=tf.float32, name="y") - - z = tf.math.add(x, y, name="add") - - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv2d(z, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - normed = tf.compat.v1.layers.batch_normalization(conv) - relu = tf.nn.relu(normed) - - conv_weights2 = tf.compat.v1.get_variable( - "weight2", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv2 = tf.nn.conv2d(z, conv_weights2, strides=[1, 2, 2, 1], padding="VALID") - normed2 = tf.compat.v1.layers.batch_normalization(conv2) - relu2 = tf.nn.relu(normed2) - add = tf.math.add(relu, relu2, name="op_to_store") - out_name = add.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - quantize_v2_count = 0 - - for i in output_graph.graph_def.node: - if i.op == "QuantizeV2": - quantize_v2_count += 1 - - if self.enable_s8: - self.assertEqual(quantize_v2_count, 2) - else: - self.assertEqual(quantize_v2_count, 1) - - @disable_random() - def test_post_cse2(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - x = tf.nn.relu(x) - xw = tf.constant(np.random.random((2, 2, 16, 16)), dtype=tf.float32, name="y") - x = tf.nn.conv2d(input=x, filters=xw, strides=[1, 1, 1, 1], padding="VALID") - - y = tf.constant(np.random.random((1, 55, 55, 16)), dtype=tf.float32, name="y") - - z = tf.math.add(x, y, name="add") - - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv2d(z, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - normed = tf.compat.v1.layers.batch_normalization(conv) - relu = tf.nn.relu(normed) - - conv_weights2 = tf.compat.v1.get_variable( - "weight2", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv2 = tf.nn.conv2d(z, conv_weights2, strides=[1, 2, 2, 1], padding="VALID") - normed2 = tf.compat.v1.layers.batch_normalization(conv2) - relu2 = tf.nn.relu(normed2) - add = tf.math.add(relu, relu2) - ones_const = tf.constant(1, dtype=tf.float32) - ones_const2 = tf.constant(1, dtype=tf.float32) - mul1 = tf.math.multiply(add, ones_const) - mul2 = tf.math.multiply(mul1, ones_const) - mul3 = tf.math.multiply(mul2, ones_const2, name="op_to_store") - out_name = mul3.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - quantize_v2_count = 0 - - for i in output_graph.graph_def.node: - if i.op == "QuantizeV2": - quantize_v2_count += 1 - - if self.enable_s8: - self.assertEqual(quantize_v2_count, 2) - else: - self.assertEqual(quantize_v2_count, 1) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/adaptor/tensorflow_adaptor/test_tensorflow_inspect_tensor.py b/test/adaptor/tensorflow_adaptor/test_tensorflow_inspect_tensor.py deleted file mode 100644 index efec5651687..00000000000 --- a/test/adaptor/tensorflow_adaptor/test_tensorflow_inspect_tensor.py +++ /dev/null @@ -1,277 +0,0 @@ -""" -test_tensorflow_inspect_tensor.py: test inspect_tensor API -1. Create a quantizer for fake tensorflow model -2. Call inspect_tensor to dump the activation in local disk for both fp32 model and quantized model -3. Compare the inspecting result between fp32 model and quantized model - -Note: - use '-s' to disable pytest capturing the sys.stderr which will be used in quantization process -""" - -import logging -import os -import pickle -import shutil -import unittest - -import numpy as np -import yaml -from packaging import version - -np.random.seed(0) - - -def build_fake_yaml(): - fake_yaml = """ - model: - name: fake_yaml - framework: tensorflow - inputs: input - outputs: output - device: cpu - quantization: - model_wise: - weight: - granularity: per_tensor - scheme: sym - dtype: int8 - algorithm: minmax - evaluation: - accuracy: - metric: - topk: 1 - """ - y = yaml.load(fake_yaml, Loader=yaml.SafeLoader) - with open("fake_yaml.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - f.close() - - -def build_fake_model(): - import tensorflow as tf - - graph = tf.Graph() - graph_def = tf.compat.v1.GraphDef() - - with tf.compat.v1.Session() as sess: - tf.compat.v1.set_random_seed(0) - x = tf.compat.v1.placeholder(tf.float32, [1, 64, 64, 3], name="input") - conv_weights1 = tf.compat.v1.get_variable( - "weight1", [2, 2, 3, 3], initializer=tf.compat.v1.random_normal_initializer() - ) - x = tf.nn.conv2d(x, conv_weights1, strides=[1, 2, 2, 1], padding="SAME", name="conv2d_1") - x = tf.nn.relu(x) - conv_weights2 = tf.compat.v1.get_variable( - "weight2", [3, 3, 3, 3], initializer=tf.compat.v1.random_normal_initializer() - ) - x = tf.nn.conv2d(x, conv_weights2, strides=[1, 3, 3, 1], padding="SAME", name="conv2d_2") - x = tf.compat.v1.layers.batch_normalization(x) - x = tf.nn.relu(x) - depthwise_weights = tf.compat.v1.get_variable( - "depthwise_weights", [3, 3, 3, 6], initializer=tf.compat.v1.random_normal_initializer() - ) - x = tf.nn.depthwise_conv2d( - x, depthwise_weights, strides=[1, 1, 1, 1], padding="VALID", name="depthwise_conv2d_1" - ) - x = tf.nn.max_pool(x, ksize=2, strides=[1, 2, 2, 1], padding="SAME", name="pool_1") - # TODO to support inspect max_pool - x = tf.nn.relu(x, name="output") - sess.run(tf.compat.v1.global_variables_initializer()) - constant_graph = tf.compat.v1.graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[x.name.split(":")[0]] - ) - - graph_def.ParseFromString(constant_graph.SerializeToString()) - - with graph.as_default(): - tf.import_graph_def(graph_def, name="") - return graph - - -def build_fake_diagnosis_yaml(): - fake_diagnosis_yaml = """ - model: - name: fake_diagnosis_yaml - framework: tensorflow - inputs: input - outputs: output - device: cpu - quantization: - model_wise: - weight: - granularity: per_tensor - scheme: sym - dtype: int8 - algorithm: minmax - evaluation: - accuracy: - metric: - topk: 1 - tuning: - strategy: - name: mse - accuracy_criterion: - relative: -0.01 - diagnosis: True - """ - y = yaml.load(fake_diagnosis_yaml, Loader=yaml.SafeLoader) - with open("fake_diagnosis_yaml.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - f.close() - - -def build_fake_diagnosis_yaml2(): - fake_diagnosis_yaml2 = """ - model: - name: fake_diagnosis_yaml2 - framework: tensorflow - inputs: input - outputs: output - device: cpu - quantization: - model_wise: - weight: - granularity: per_tensor - scheme: sym - dtype: int8 - algorithm: minmax - evaluation: - accuracy: - metric: - topk: 1 - tuning: - strategy: - name: mse - accuracy_criterion: - relative: -0.01 - diagnosis: True - """ - y = yaml.load(fake_diagnosis_yaml2, Loader=yaml.SafeLoader) - with open("fake_diagnosis_yaml2.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - f.close() - - -class TestTensorflowInspectTensor(unittest.TestCase): - @classmethod - def setUpClass(self): - from neural_compressor.config import options - - build_fake_yaml() - build_fake_diagnosis_yaml() - build_fake_diagnosis_yaml2() - self.model = build_fake_model() - self.fp32_dumped_tensor_path = os.path.join(os.getcwd(), "./fake_graph_inspect_res_fp32/") - self.quan_dumped_tensor_path = os.path.join(os.getcwd(), "./fake_graph_inspect_res_quan/") - self.fp32_dumped_tensor_file_path = os.path.join(self.fp32_dumped_tensor_path, "inspect_result.pkl") - self.quan_dumped_tensor_file_path = os.path.join(self.quan_dumped_tensor_path, "inspect_result.pkl") - self.workspace = os.path.abspath(options.workspace) - - @classmethod - def tearDownClass(self): - os.remove("fake_yaml.yaml") - os.remove(self.fp32_dumped_tensor_file_path) - os.rmdir(self.fp32_dumped_tensor_path) - os.remove(self.quan_dumped_tensor_file_path) - os.rmdir(self.quan_dumped_tensor_path) - shutil.rmtree(self.workspace) - # shutil.rmtree(os.path.join(os.getcwd(), 'save_path_test')) - - def test_tensorflow_inspect_tensor(self): - import tensorflow.compat.v1 as tf - - from neural_compressor.experimental import Quantization, common - from neural_compressor.utils.utility import load_data_from_pkl - - tf.disable_v2_behavior() - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(128, 64, 64, 3), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.model = self.model - q_model = quantizer.fit() - self.quan_graph_def = q_model.graph_def - self.fp32_graph_def = quantizer.model.graph_def - self.dataloader = quantizer.calib_dataloader - self.node_list = ["conv2d_1", "conv2d_2", "depthwise_conv2d_1"] - # Tensorflow 2.5.0 enabled the s8 input for pooling op - # TODO check the specific version - if version.parse(tf.version.VERSION) >= version.parse("2.6.0"): - self.node_list.append("pool_1") - self.quantizer = quantizer - self.iteration_list = [1, 5] - - logging.getLogger().debug(f"Start to inspect tensor :{self.node_list} in fp32 model.") - quantizer = self.quantizer - quantizer.strategy.adaptor.inspect_tensor( - self.fp32_graph_def, - dataloader=self.dataloader, - op_list=self.node_list, - iteration_list=self.iteration_list, - inspect_type="all", - save_to_disk=True, - save_path=self.fp32_dumped_tensor_path, - quantization_cfg=quantizer.strategy.tune_cfg, - ) - self.assertEqual(os.path.exists(self.fp32_dumped_tensor_file_path), True) - - logging.getLogger().debug(f"Start to inspect tensor :{self.node_list} in quan model.") - quantizer = self.quantizer - quantizer.strategy.adaptor.inspect_tensor( - self.quan_graph_def, - dataloader=self.dataloader, - op_list=self.node_list, - iteration_list=self.iteration_list, - inspect_type="all", - save_to_disk=True, - save_path=self.quan_dumped_tensor_path, - quantization_cfg=quantizer.strategy.tune_cfg, - ) - self.assertEqual(os.path.exists(self.quan_dumped_tensor_file_path), True) - - fp32_data = load_data_from_pkl(self.fp32_dumped_tensor_path, "inspect_result.pkl") - quan_data = load_data_from_pkl(self.quan_dumped_tensor_path, "inspect_result.pkl") - self.assertEqual(fp32_data.keys(), quan_data.keys()) - self.assertIn("activation", fp32_data) - self.assertEqual(len(fp32_data["activation"]), len(quan_data["activation"])) # have same itertaion index - self.assertEqual(len(self.iteration_list), len(fp32_data["activation"])) - for iter_indx, iter in enumerate(self.iteration_list): - fp32_iter_data = fp32_data["activation"][iter_indx] - quan_iter_data = quan_data["activation"][iter_indx] - for node_name in fp32_iter_data.keys(): - self.assertEqual(fp32_iter_data[node_name][node_name].shape, quan_iter_data[node_name][node_name].shape) - - def test_tensorflow_diagnosis(self): - import tensorflow.compat.v1 as tf - - from neural_compressor.experimental import Quantization, common - - tf.disable_v2_behavior() - quantizer = Quantization("fake_diagnosis_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(128, 64, 64, 3), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.model = self.model - quantizer.fit() - - self.assertEqual(os.path.exists(os.path.join(self.workspace, "inspect_saved/fp32/inspect_result.pkl")), True) - self.assertEqual(os.path.exists(os.path.join(self.workspace, "inspect_saved/quan/inspect_result.pkl")), True) - - def test_tensorflow_diagnosis2(self): - import tensorflow.compat.v1 as tf - - from neural_compressor.experimental import Quantization, common - - tf.disable_v2_behavior() - quantizer = Quantization("fake_diagnosis_yaml2.yaml") - dataset = quantizer.dataset("dummy", shape=(128, 64, 64, 3), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.model = self.model - quantizer.fit() - self.assertEqual(os.path.exists(os.path.join(self.workspace, "inspect_saved/fp32/inspect_result.pkl")), True) - self.assertEqual(os.path.exists(os.path.join(self.workspace, "inspect_saved/quan/inspect_result.pkl")), True) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/adaptor/tensorflow_adaptor/test_tensorflow_inspect_tensor_in_mse_tuning.py b/test/adaptor/tensorflow_adaptor/test_tensorflow_inspect_tensor_in_mse_tuning.py deleted file mode 100644 index d2b7db27d65..00000000000 --- a/test/adaptor/tensorflow_adaptor/test_tensorflow_inspect_tensor_in_mse_tuning.py +++ /dev/null @@ -1,146 +0,0 @@ -""" -test_tensorflow_inspect_tensor_in_mse_tuning.py: -test inspect_tensor API called by mse tuning strategy -1. Create a quantizer for fake tensorflow model -2. The quantizer fitting process will call inspect_tensor API for both fp32 model and quantized model -3. Check the inspecting result in local disk - -Note: - use '-s' to disable pytest capturing the sys.stderr which will be used in quantization process -""" - -import logging -import os -import pickle -import platform -import shutil -import unittest - -import numpy as np -import yaml - -np.random.seed(0) - - -def build_fake_yaml(): - fake_yaml = """ - model: - name: fake_yaml - framework: tensorflow - inputs: input - outputs: output - device: cpu - quantization: - model_wise: - weight: - granularity: per_tensor - scheme: sym - dtype: int8 - algorithm: minmax - evaluation: - accuracy: - metric: - topk: 1 - tuning: - strategy: - name: mse - accuracy_criterion: - relative: -0.01 - workspace: - path: saved - """ - y = yaml.load(fake_yaml, Loader=yaml.SafeLoader) - with open("fake_yaml.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - f.close() - - -def build_fake_model(): - import tensorflow as tf - - graph = tf.Graph() - graph_def = tf.compat.v1.GraphDef() - - with tf.compat.v1.Session() as sess: - tf.compat.v1.set_random_seed(0) - x = tf.compat.v1.placeholder(tf.float32, [1, 28, 28, 1], name="input") - conv_weights1 = tf.compat.v1.get_variable( - "weight1", [2, 2, 1, 1], initializer=tf.compat.v1.random_normal_initializer() - ) - x = tf.nn.conv2d(x, conv_weights1, strides=[1, 2, 2, 1], padding="SAME", name="conv2d_1") - x = tf.nn.relu(x) - conv_weights2 = tf.compat.v1.get_variable( - "weight2", [3, 3, 1, 1], initializer=tf.compat.v1.random_normal_initializer() - ) - x = tf.nn.conv2d(x, conv_weights2, strides=[1, 3, 3, 1], padding="SAME", name="conv2d_2") - x = tf.compat.v1.layers.batch_normalization(x) - x = tf.nn.relu(x) - x = tf.nn.max_pool(x, ksize=1, strides=[1, 2, 2, 1], padding="SAME", name="pool_1") - # TODO to support inspect max_pool - x = tf.nn.relu(x, name="output") - sess.run(tf.compat.v1.global_variables_initializer()) - constant_graph = tf.compat.v1.graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[x.name.split(":")[0]] - ) - - graph_def.ParseFromString(constant_graph.SerializeToString()) - - with graph.as_default(): - tf.import_graph_def(graph_def, name="") - return graph - - -def load_data_from_pkl(path, filename): - try: - file_path = os.path.join(path, filename) - with open(file_path, "rb") as fp: - data = pickle.load(fp) - return data - except FileExistsError: - logging.getLogger().info("Can not open %s." % path) - - -class TestTensorflowInspectTensortinMSETuning(unittest.TestCase): - @classmethod - def setUpClass(self): - build_fake_yaml() - if platform.system().lower() == "linux": - self.cfg_path = os.path.join(os.getcwd(), "./nc_workspace/") - self.dumped_tensor_path = os.path.join(os.getcwd(), "./nc_workspace/") - else: - self.cfg_path = os.path.join(os.getcwd(), "nc_workspace\\") - self.dumped_tensor_path = os.path.join(os.getcwd(), "nc_workspace\\") - self.cfg_file_path = os.path.join(self.cfg_path, "cfg.pkl") - self.dumped_tensor_file_path = os.path.join(self.dumped_tensor_path, "inspect_result.pkl") - - @classmethod - def tearDownClass(self): - os.remove("fake_yaml.yaml") - os.remove(self.dumped_tensor_file_path) - shutil.rmtree(self.dumped_tensor_path) - - def test_tensorflow_inspect_tensort_in_mse_tuning(self): - import tensorflow.compat.v1 as tf - - from neural_compressor.experimental import Quantization, common - - tf.disable_v2_behavior() - model = build_fake_model() - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(128, 28, 28, 1), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.model = model - quantizer.fit() - self.assertEqual(os.path.exists(self.dumped_tensor_path), True) - data = load_data_from_pkl(self.dumped_tensor_path, "inspect_result.pkl") - self.assertEqual("activation" in data, True) - self.assertEqual(set(data["activation"][0].keys()), set(["pool_1", "conv2d_2", "conv2d_1"])) - self.assertEqual(len(data["activation"][0].keys()), 3) - self.assertEqual(data["activation"][0]["pool_1"]["pool_1"].shape, (1, 3, 3, 1)) - self.assertEqual(data["activation"][0]["conv2d_1"]["conv2d_1"].shape, (1, 14, 14, 1)) - self.assertEqual(data["activation"][0]["conv2d_2"]["conv2d_2"].shape, (1, 5, 5, 1)) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/adaptor/tensorflow_adaptor/test_tensorflow_quantize_input.py b/test/adaptor/tensorflow_adaptor/test_tensorflow_quantize_input.py deleted file mode 100644 index d321254a84e..00000000000 --- a/test/adaptor/tensorflow_adaptor/test_tensorflow_quantize_input.py +++ /dev/null @@ -1,106 +0,0 @@ -import os -import shutil -import unittest - -import tensorflow as tf -import yaml -from tensorflow.compat.v1 import graph_util - -from neural_compressor.adaptor.tensorflow import TensorFlowAdaptor -from neural_compressor.adaptor.tf_utils.util import disable_random - - -def build_fake_yaml(): - fake_yaml = """ - model: - name: fake_yaml - framework: tensorflow - inputs: input - outputs: op_to_store - device: cpu - evaluation: - accuracy: - metric: - topk: 1 - tuning: - accuracy_criterion: - relative: 0.0001 - workspace: - path: saved - """ - y = yaml.load(fake_yaml, Loader=yaml.SafeLoader) - with open("fake_yaml.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - f.close() - - -class TestQuantizeInput(unittest.TestCase): - @classmethod - def setUpClass(self): - build_fake_yaml() - - @classmethod - def tearDownClass(self): - os.remove("fake_yaml.yaml") - shutil.rmtree("./saved", ignore_errors=True) - - @disable_random() - @unittest.skipIf( - tf.version.VERSION < "2.1.0", - "Quantize input needs tensorflow 2.1.0 and newer, so test_quantize_input is skipped", - ) - def test_quantize_input(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(x, paddings, "CONSTANT") - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv2d(x_pad, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - - conv_bias = tf.compat.v1.get_variable("bias", [16], initializer=tf.compat.v1.random_normal_initializer()) - - conv_bias = tf.math.add(conv, conv_bias) - relu6 = tf.nn.relu6(conv_bias, name="op_to_store") - - out_name = relu6.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - constant_graph = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - - for i in constant_graph.node: - if i.op.find("Add") != -1: - i.op = "Add" - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("./fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = constant_graph - q_model = quantizer.fit() - - framework_specific_info = { - "device": "cpu", - "approach": "post_training_static_quant", - "random_seed": 1978, - "inputs": ["input"], - "outputs": ["op_to_store"], - "workspace_path": "saved", - "format": "default", - "backend": "default", - } - - quantize_input_graph, _ = TensorFlowAdaptor(framework_specific_info).quantize_input(q_model.graph) - Not_found_QuantizedV2 = True - for i in quantize_input_graph.as_graph_def().node: - if i.op == "QuantizeV2": - Not_found_QuantizedV2 = False - break - self.assertEqual(Not_found_QuantizedV2, True) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/adaptor/tensorflow_adaptor/test_tensorflow_query_yaml.py b/test/adaptor/tensorflow_adaptor/test_tensorflow_query_yaml.py index d0ef998bb31..fba7bb34dd9 100644 --- a/test/adaptor/tensorflow_adaptor/test_tensorflow_query_yaml.py +++ b/test/adaptor/tensorflow_adaptor/test_tensorflow_query_yaml.py @@ -199,50 +199,6 @@ def test_convert_internal_patterns(self): self.assertEqual([["AvgPool"]] in internal_patterns, True) self.assertEqual([["MatMul"], ("BiasAdd",), ("Relu",)] in internal_patterns, True) - @disable_random() - def test_grappler_cfg(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 30, 30, 1], name="input") - conv_weights = tf.compat.v1.get_variable( - "weight", [2, 2, 1, 1], initializer=tf.compat.v1.random_normal_initializer() - ) - conv_bias = tf.compat.v1.get_variable("bias", [1], initializer=tf.compat.v1.random_normal_initializer()) - - x = tf.nn.relu(x) - conv = tf.nn.conv2d(x, conv_weights, strides=[1, 2, 2, 1], padding="SAME", name="last") - normed = tf.compat.v1.layers.batch_normalization(conv) - - relu = tf.nn.relu(normed) - relu2 = tf.nn.relu(relu) - pool = tf.nn.max_pool(relu2, ksize=1, strides=[1, 2, 2, 1], name="maxpool", padding="SAME") - conv1 = tf.nn.conv2d(pool, conv_weights, strides=[1, 2, 2, 1], padding="SAME", name="last") - conv_bias = tf.nn.bias_add(conv1, conv_bias) - x = tf.nn.relu(conv_bias) - final_node = tf.nn.relu(x, name="op_to_store") - - out_name = final_node.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml_grappler.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 30, 30, 1), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - - disable_arithmetic = False - for i in output_graph.graph_def.node: - if i.name == "maxpool_eightbit_quantize_Relu_2" and i.input[0] == "Relu_2": - disable_arithmetic = True - # if tf.version.VERSION >= '2.3.0': - # self.assertEqual(False, disable_arithmetic) - # else: - self.assertEqual(True, disable_arithmetic) - class TestFrameworkQueryYaml(unittest.TestCase): @classmethod diff --git a/test/adaptor/tensorflow_adaptor/test_tensorflow_set_tensor.py b/test/adaptor/tensorflow_adaptor/test_tensorflow_set_tensor.py deleted file mode 100644 index a53f3ae724f..00000000000 --- a/test/adaptor/tensorflow_adaptor/test_tensorflow_set_tensor.py +++ /dev/null @@ -1,174 +0,0 @@ -import os -import shutil -import unittest - -import numpy as np -import tensorflow as tf -import yaml -from tensorflow.compat.v1 import graph_util - -from neural_compressor.adaptor.tensorflow import TensorFlowAdaptor -from neural_compressor.adaptor.tf_utils.util import disable_random - - -def build_fake_yaml(): - fake_yaml = """ - model: - name: fake_yaml - framework: tensorflow - inputs: input - outputs: op_to_store - device: cpu - evaluation: - accuracy: - metric: - topk: 1 - tuning: - accuracy_criterion: - relative: 0.0001 - workspace: - path: saved - """ - y = yaml.load(fake_yaml, Loader=yaml.SafeLoader) - with open("fake_yaml.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - f.close() - - -class TestSetTensor(unittest.TestCase): - @classmethod - def setUpClass(self): - build_fake_yaml() - - @classmethod - def tearDownClass(self): - os.remove("fake_yaml.yaml") - shutil.rmtree("./saved", ignore_errors=True) - - @disable_random() - def test_fp32bias(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(x, paddings, "CONSTANT") - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv2d(x_pad, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - - conv_bias = tf.compat.v1.get_variable( - "bias", [16], dtype=tf.float32, initializer=tf.compat.v1.random_normal_initializer() - ) - - conv_bias = tf.math.add(conv, conv_bias) - relu6 = tf.nn.relu6(conv_bias, name="op_to_store") - - out_name = relu6.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - constant_graph = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("./fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = constant_graph - q_model = quantizer.fit() - - framework_specific_info = { - "device": "cpu", - "workspace_path": "saved", - "random_seed": 1978, - "inputs": ["input"], - "outputs": ["op_to_store"], - "approach": "post_training_static_quant", - "format": "default", - "backend": "default", - } - adaptor = TensorFlowAdaptor(framework_specific_info) - adaptor.set_tensor(q_model, {"bias": np.random.random(16)}) - - from tensorflow.core.framework import attr_value_pb2 - from tensorflow.python.framework import dtypes - - for node in q_model.graph_def.node: - if node.name == "bias": - self.assertEqual(node.attr["dtype"], attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - - @disable_random() - def test_int32bias(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(x, paddings, "CONSTANT") - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv2d(x_pad, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - - conv_bias = tf.compat.v1.get_variable("bias", [16], dtype=tf.float32) - - conv_bias = tf.math.add(conv, conv_bias) - relu6 = tf.nn.relu6(conv_bias, name="relu_0") - - conv_weights1 = tf.compat.v1.get_variable( - "weight1", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv1 = tf.nn.conv2d(relu6, conv_weights1, strides=[1, 2, 2, 1], padding="VALID") - - conv_bias1 = tf.compat.v1.get_variable("bias1", [16], dtype=tf.float32) - - conv_bias1 = tf.math.add(conv1, conv_bias1) - relu6 = tf.nn.relu6(conv_bias1, name="relu_1") - - conv_weights2 = tf.compat.v1.get_variable( - "weight2", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv2 = tf.nn.conv2d(relu6, conv_weights2, strides=[1, 2, 2, 1], padding="VALID") - - conv_bias2 = tf.compat.v1.get_variable("bias2", [16], dtype=tf.float32) - - conv_bias2 = tf.math.add(conv2, conv_bias2) - relu6 = tf.nn.relu6(conv_bias2, name="op_to_store") - out_name = relu6.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - constant_graph = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - - for i in constant_graph.node: - if i.op.find("Add") != -1: - i.op = "Add" - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("./fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = constant_graph - q_model = quantizer.fit() - - framework_specific_info = { - "device": "cpu", - "workspace_path": "saved", - "random_seed": 1978, - "inputs": ["input"], - "outputs": ["op_to_store"], - "approach": "post_training_static_quant", - "format": "default", - "backend": "default", - } - adaptor = TensorFlowAdaptor(framework_specific_info) - adaptor.set_tensor(q_model, {"bias1": np.random.randint(6, size=2, dtype="int32")}) - from tensorflow.core.framework import attr_value_pb2 - from tensorflow.python.framework import dtypes - - for node in q_model.graph_def.node: - if node.name == "bias2": - self.assertEqual(node.attr["dtype"], attr_value_pb2.AttrValue(type=dtypes.qint32.as_datatype_enum)) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/adaptor/tensorflow_adaptor/test_tensorflow_strip_equivalent_nodes.py b/test/adaptor/tensorflow_adaptor/test_tensorflow_strip_equivalent_nodes.py deleted file mode 100644 index 8051d92b941..00000000000 --- a/test/adaptor/tensorflow_adaptor/test_tensorflow_strip_equivalent_nodes.py +++ /dev/null @@ -1,95 +0,0 @@ -# -# -*- coding: utf-8 -*- -# - -import os -import unittest - -import numpy as np -import tensorflow as tf -import yaml -from tensorflow.compat.v1 import graph_util - -from neural_compressor.adaptor.tf_utils.util import disable_random -from neural_compressor.experimental import Quantization, common - - -def build_fake_yaml(): - fake_yaml = """ - model: - name: fake_yaml - framework: tensorflow - inputs: input - device: cpu - quantization: - model_wise: - weight: - granularity: per_tensor - scheme: sym - dtype: int8 - algorithm: minmax - evaluation: - accuracy: - metric: - topk: 1 - tuning: - strategy: - name: basic - accuracy_criterion: - relative: 0.1 - exit_policy: - performance_only: True - workspace: - path: saved - """ - y = yaml.load(fake_yaml, Loader=yaml.SafeLoader) - with open("fake_yaml.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - f.close() - - -class TestConvBiasAddAddReluFusion(unittest.TestCase): - @classmethod - def setUpClass(self): - build_fake_yaml() - - @classmethod - def tearDownClass(self): - os.remove("fake_yaml.yaml") - - @disable_random() - def test_conv_relu_fusion(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - top_relu = tf.nn.relu(x) - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(top_relu, paddings, "CONSTANT") - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv1 = tf.nn.conv2d(x_pad, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - conv2 = tf.nn.conv2d(x_pad, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - conv_add = tf.math.add(conv1, conv2) - relu6 = tf.nn.relu6(conv_add) - out_name = relu6.name.split(":")[0] - - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - - found_conv_num = 0 - for i in output_graph.graph_def.node: - if "QuantizedConv2D" in i.op: - found_conv_num += 1 - self.assertEqual(found_conv_num, 1) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/benchmark/test_benchmark.py b/test/benchmark/test_benchmark.py deleted file mode 100644 index 1f34af16b19..00000000000 --- a/test/benchmark/test_benchmark.py +++ /dev/null @@ -1,284 +0,0 @@ -"""Tests for neural_compressor benchmark.""" - -import os -import platform -import re -import tempfile -import unittest - -import numpy as np -import psutil -import tensorflow as tf -import yaml - -from neural_compressor.adaptor.tf_utils.util import write_graph - - -def build_fake_yaml(): - fake_yaml = """ - model: - name: fake_yaml - framework: tensorflow - inputs: x - outputs: op_to_store - device: cpu - evaluation: - accuracy: - metric: - topk: 1 - performance: - warmup: 5 - iteration: 10 - configs: - cores_per_instance: 4 - num_of_instance: 2 - tuning: - accuracy_criterion: - relative: 0.01 - """ - y = yaml.load(fake_yaml, Loader=yaml.SafeLoader) - with open("fake_yaml.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - f.close() - - -def build_benchmark(): - seq = """ -from argparse import ArgumentParser -arg_parser = ArgumentParser(description='Parse args') -arg_parser.add_argument('--input_model', dest='input_model', default='input_model', help='input odel') -args = arg_parser.parse_args() -from neural_compressor.data import Datasets -dataset = Datasets('tensorflow')['dummy']((100, 32, 32, 1), label=True) -from neural_compressor.experimental import Benchmark, common -from neural_compressor.conf.config import BenchmarkConf -benchmarker = Benchmark('fake_yaml.yaml') -benchmarker.b_dataloader = common.DataLoader(dataset, batch_size=10) -benchmarker.model = args.input_model -benchmarker.fit() - """ - - seq1 = """ -from argparse import ArgumentParser -arg_parser = ArgumentParser(description='Parse args') -arg_parser.add_argument('--input_model', dest='input_model', default='input_model', help='input odel') -args = arg_parser.parse_args() -from neural_compressor.data import Datasets -dataset = Datasets('tensorflow')['dummy']((100, 32, 32, 1), label=True) -from neural_compressor.experimental import Benchmark, common -from neural_compressor.conf.config import BenchmarkConf -conf = BenchmarkConf('fake_yaml.yaml') -benchmarker = Benchmark(conf) -benchmarker.b_dataloader = common.DataLoader(dataset, batch_size=10) -benchmarker.model = args.input_model -benchmarker.fit() - """ - - # test normal case - with open("fake.py", "w", encoding="utf-8") as f: - f.writelines(seq) - # test batchsize > len(dataset), use first batch - fake_data_5 = seq.replace("100, 32, 32, 1", "5, 32, 32, 1") - with open("fake_data_5.py", "w", encoding="utf-8") as f: - f.writelines(fake_data_5) - # test batchsize < len(dataset) < 2*batchsize, discard first batch - fake_data_15 = seq1.replace("100, 32, 32, 1", "15, 32, 32, 1") - with open("fake_data_15.py", "w", encoding="utf-8") as f: - f.writelines(fake_data_15) - # test 2*batchsize < len(dataset) < warmup*batchsize, discard last batch - fake_data_25 = seq1.replace("100, 32, 32, 1", "25, 32, 32, 1") - with open("fake_data_25.py", "w", encoding="utf-8") as f: - f.writelines(fake_data_25) - - -def build_benchmark2(): - seq = [ - "from argparse import ArgumentParser\n", - "arg_parser = ArgumentParser(description='Parse args')\n", - "arg_parser.add_argument('--input_model', dest='input_model', default='input_model', help='input model')\n", - "args = arg_parser.parse_args()\n", - "from neural_compressor.data import Datasets\n", - "dataset = Datasets('tensorflow')['dummy']((5, 32, 32, 1), label=True)\n", - "from neural_compressor.experimental import Benchmark, common\n", - "benchmarker = Benchmark()\n", - "benchmarker.model = args.input_model\n", - "benchmarker.b_dataloader = common.DataLoader(dataset)\n", - "benchmarker.fit()\n", - ] - - seq1 = """ -from argparse import ArgumentParser -arg_parser = ArgumentParser(description='Parse args') -arg_parser.add_argument('--input_model', dest='input_model', default='input_model', help='input odel') -args = arg_parser.parse_args() - -from neural_compressor.conf.config import conf -from neural_compressor.experimental import Benchmark, common -conf.evaluation.performance.dataloader.dataset = {'dummy': {'shape': [100,32,32,1], 'label':True}} -benchmarker = Benchmark(conf) -benchmarker.model = args.input_model -benchmarker.fit() - """ - - seq2 = """ -from argparse import ArgumentParser -arg_parser = ArgumentParser(description='Parse args') -arg_parser.add_argument('--input_model', dest='input_model', default='input_model', help='input model') -args = arg_parser.parse_args() - -class Metric: - def update(self, pred, label): - pass - - def reset(self): - pass - - def result(self): - return 1. - -from neural_compressor.conf.config import conf -from neural_compressor.experimental import Benchmark, common -conf.evaluation.accuracy.dataloader.dataset = {'dummy': {'shape': [100,32,32,1], 'label':True}} -benchmarker = Benchmark(conf) -benchmarker.model = args.input_model -benchmarker.metric = Metric() -benchmarker.fit('accuracy') - """ - - with open("fake2.py", "w", encoding="utf-8") as f: - f.writelines(seq) - with open("fake3.py", "w", encoding="utf-8") as f: - f.writelines(seq1) - with open("fake4.py", "w", encoding="utf-8") as f: - f.writelines(seq2) - - -def build_fake_model(): - graph_path = tempfile.mkstemp(suffix=".pb")[1] - try: - graph = tf.Graph() - graph_def = tf.GraphDef() - with tf.Session(graph=graph) as sess: - x = tf.placeholder(tf.float64, shape=(None, 32, 32, 1), name="x") - y_1 = tf.constant(np.random.random((3, 3, 1, 1)), name="y_1") - y_2 = tf.constant(np.random.random((3, 3, 1, 1)), name="y_2") - conv1 = tf.nn.conv2d(input=x, filter=y_1, strides=[1, 1, 1, 1], padding="VALID", name="conv1") - op = tf.nn.conv2d(input=conv1, filter=y_2, strides=[1, 1, 1, 1], padding="VALID", name="op_to_store") - - sess.run(tf.global_variables_initializer()) - constant_graph = tf.graph_util.convert_variables_to_constants(sess, sess.graph_def, ["op_to_store"]) - - graph_def.ParseFromString(constant_graph.SerializeToString()) - write_graph(graph_def, graph_path) - except: - graph = tf.Graph() - graph_def = tf.compat.v1.GraphDef() - with tf.compat.v1.Session(graph=graph) as sess: - x = tf.compat.v1.placeholder(tf.float64, shape=(None, 32, 32, 1), name="x") - y_1 = tf.constant(np.random.random((3, 3, 1, 1)), name="y_1") - y_2 = tf.constant(np.random.random((3, 3, 1, 1)), name="y_2") - conv1 = tf.nn.conv2d(input=x, filters=y_1, strides=[1, 1, 1, 1], padding="VALID", name="conv1") - op = tf.nn.conv2d(input=conv1, filters=y_2, strides=[1, 1, 1, 1], padding="VALID", name="op_to_store") - - sess.run(tf.compat.v1.global_variables_initializer()) - constant_graph = tf.compat.v1.graph_util.convert_variables_to_constants( - sess, sess.graph_def, ["op_to_store"] - ) - - graph_def.ParseFromString(constant_graph.SerializeToString()) - write_graph(graph_def, graph_path) - return graph_path - - -class TestObjective(unittest.TestCase): - @classmethod - def setUpClass(self): - self.graph_path = build_fake_model() - build_fake_yaml() - build_benchmark() - build_benchmark2() - self.cpu_counts = psutil.cpu_count(logical=False) - self.platform = platform.system().lower() - - @classmethod - def tearDownClass(self): - if os.path.exists("fake_yaml.yaml"): - os.remove("fake_yaml.yaml") - if os.path.exists("fake.py"): - os.remove("fake.py") - if os.path.exists("fake2.py"): - os.remove("fake2.py") - if os.path.exists("fake3.py"): - os.remove("fake3.py") - if os.path.exists("fake4.py"): - os.remove("fake4.py") - if os.path.exists("fake_data_5.py"): - os.remove("fake_data_5.py") - if os.path.exists("fake_data_15.py"): - os.remove("fake_data_15.py") - if os.path.exists("fake_data_25.py"): - os.remove("fake_data_25.py") - - def test_benchmark(self): - os.system("python fake.py --input_model={}".format(self.graph_path)) - for i in range(2): - with open(f"2_4_{i}.log", "r") as f: - for line in f: - throughput = re.search(r"Throughput:\s+(\d+(\.\d+)?) images/sec", line) - self.assertIsNotNone(throughput) - os.system("rm *.log") - - def test_benchmark_data_5(self): - os.system("python fake_data_5.py --input_model={}".format(self.graph_path)) - for i in range(2): - with open(f"2_4_{i}.log", "r") as f: - for line in f: - throughput = re.search(r"Throughput:\s+(\d+(\.\d+)?) images/sec", line) - self.assertIsNotNone(throughput) - os.system("rm *.log") - - def test_benchmark_data_15(self): - os.system("python fake_data_15.py --input_model={}".format(self.graph_path)) - for i in range(2): - with open(f"2_4_{i}.log", "r") as f: - for line in f: - throughput = re.search(r"Throughput:\s+(\d+(\.\d+)?) images/sec", line) - self.assertIsNotNone(throughput) - os.system("rm *.log") - - def test_benchmark_data_25(self): - os.system("python fake_data_25.py --input_model={}".format(self.graph_path)) - for i in range(2): - with open(f"2_4_{i}.log", "r") as f: - for line in f: - throughput = re.search(r"Throughput:\s+(\d+(\.\d+)?) images/sec", line) - self.assertIsNotNone(throughput) - os.system("rm *.log") - - def test_benchmark_without_yaml(self): - os.system("python fake2.py --input_model={} 2>&1 | tee benchmark.log".format(self.graph_path)) - with open("benchmark.log", "r") as f: - for line in f: - throughput = re.search(r"Throughput sum: (\d+(\.\d+)?)", line) - self.assertIsNotNone(throughput) - os.system("rm *.log") - - def test_benchmark_with_conf(self): - os.system("python fake3.py --input_model={}".format(self.graph_path)) - with open(f"1_{self.cpu_counts}_0.log", "r") as f: - for line in f: - throughput = re.search(r"Throughput:\s+(\d+(\.\d+)?) images/sec", line) - self.assertIsNotNone(throughput) - os.system("rm *.log") - - def test_benchmark_with_custom_metric(self): - os.system("python fake4.py --input_model={} 2>&1 | tee benchmark.log".format(self.graph_path)) - with open("benchmark.log", "r") as f: - for line in f: - accuracy = re.search(r"Accuracy is\s+(\d+(\.\d+)?)", line) - self.assertIsNotNone(accuracy) - os.system("rm *.log") - - -if __name__ == "__main__": - unittest.main() diff --git a/test/config/test_config_1x.py b/test/config/test_config_1x.py deleted file mode 100644 index b7637d92234..00000000000 --- a/test/config/test_config_1x.py +++ /dev/null @@ -1,807 +0,0 @@ -"""Tests for 1.x config file.""" - -import os -import unittest - -from neural_compressor.conf import config as conf -from neural_compressor.utils.constant import * - - -def helper(content): - with open("fake_conf.yaml", "w", encoding="utf-8") as f: - f.write(content) - - -class TestConfig(unittest.TestCase): - def test_config(self): - from neural_compressor import PostTrainingQuantConfig - - config = PostTrainingQuantConfig() - self.assertEqual(config.recipes["smooth_quant"], False) - self.assertEqual(config.recipes["fast_bias_correction"], False) - self.assertEqual(config.recipes["weight_correction"], False) - self.assertEqual(config.recipes["dedicated_qdq_pair"], False) - self.assertEqual(config.recipes["add_qdq_pair_to_weight"], False) - self.assertEqual(config.recipes["graph_optimization_level"], None) - - -class TestPyConf(unittest.TestCase): - def test_config(self): - from neural_compressor.conf.config import ( - BenchmarkConf, - DistillationConf, - GraphOptConf, - PruningConf, - QuantConf, - conf, - ) - - conf.tuning.accuracy_criterion.relative = 0.2 - a = QuantConf(conf) - self.assertEqual(a.usr_cfg.tuning.accuracy_criterion.relative, 0.2) - - conf.quantization.op_wise = { - "op1": FP32, - "op2": {"activation": INT8_SYM_KL_PERTENSOR}, - "op3": {"activation": INT8_SYM_KL_PERCHANNEL, "weight": INT8_SYM_MINMAX_PERTENSOR}, - } - conf.quantization.model_wise = {"activation": INT8_SYM_KL_PERTENSOR, "weight": INT8_SYM_MINMAX_PERTENSOR} - a = QuantConf(conf) - self.assertEqual(a.usr_cfg.quantization.model_wise.weight.scheme, ["sym"]) - - conf.evaluation.performance.dataloader.dataset = {"dummy": {"shape": "224,224,3"}} - conf.evaluation.accuracy.dataloader.dataset = {"dummy": {"shape": "224,224,3", "low": "0.1"}} - - conf.evaluation.performance.dataloader.transform = { - "Resize": {"size": [100, 100]}, - "BilinearImagenet": {"height": 300, "width": 300, "mean_value": [0.2, 0.2, 0.2]}, - } - conf.evaluation.performance.dataloader.batch_size = 6 - conf.evaluation.accuracy.metric = {"RMSE": {}} - conf.tuning.strategy.name = "mse" - a = BenchmarkConf(conf) - self.assertEqual(a.usr_cfg.evaluation.performance.dataloader.batch_size, 6) - self.assertEqual(a.usr_cfg.evaluation.performance.dataloader.dataset, {"dummy": {"shape": (224, 224, 3)}}) - self.assertEqual(a.usr_cfg.evaluation.accuracy.metric, {"RMSE": {}}) - a = QuantConf(conf) - self.assertEqual(a.usr_cfg.tuning.strategy.name, "mse") - - conf.evaluation.accuracy.metric = {"topk": 5} - conf.graph_optimization.precisions = "bf16" - conf.pruning.train.criterion = {"CrossEntropyLoss": {}} - conf.pruning.train.optimizer = {} - a = PruningConf(conf) - self.assertEqual( - a.usr_cfg.pruning.train.criterion, {"CrossEntropyLoss": {"from_logits": False, "reduction": "mean"}} - ) - - self.assertEqual(a.usr_cfg.evaluation.accuracy.metric, {"topk": 5}) - conf.graph_optimization.op_wise = BF16 - a = GraphOptConf(conf) - self.assertEqual( - a.usr_cfg.graph_optimization.op_wise, {"weight": {"dtype": ["bf16"]}, "activation": {"dtype": ["bf16"]}} - ) - - conf.distillation.train.iteration = 900 - a = DistillationConf(conf) - self.assertEqual(a.usr_cfg.distillation.train.iteration, 900) - - -class TestConf(unittest.TestCase): - @classmethod - def tearDownClass(self): - os.remove("fake_conf.yaml") - - def test_main_key(self): - test = """ - model: - name: main_key_yaml - framework: pytorch - test: cpu - """ - helper(test) - self.assertRaises(RuntimeError, conf.Conf, "fake_conf.yaml") - - def test_framework(self): - test = """ - model: - name: framework_yaml - framework: pytorch, mxnet - """ - helper(test) - self.assertRaises(RuntimeError, conf.Conf, "fake_conf.yaml") - - test = """ - device: cpu - """ - helper(test) - self.assertRaises(RuntimeError, conf.Conf, "fake_conf.yaml") - - def test_device(self): - test = """ - model: - name: device_yaml - framework: mxnet - device: xpu - """ - helper(test) - self.assertRaises(RuntimeError, conf.Conf, "fake_conf.yaml") - - test = """ - model: - name: device_yaml - framework: mxnet - device: cpu, gpu - """ - helper(test) - self.assertRaises(RuntimeError, conf.Conf, "fake_conf.yaml") - - def test_version(self): - test = """ - model: - name: version_yaml - framework: mxnet - """ - helper(test) - config = conf.Conf("fake_conf.yaml") - self.assertEqual(config.usr_cfg.version, 2.0) - - test = """ - version: 2.0 - - model: - name: version_yaml - framework: mxnet - """ - helper(test) - config = conf.Conf("fake_conf.yaml") - self.assertEqual(config.usr_cfg.version, 2.0) - - def test_calibration(self): - test = """ - model: - name: calib_yaml - framework: mxnet - quantization: - calibration: - sampling_sizes: 10 - """ - helper(test) - self.assertRaises(RuntimeError, conf.Conf, "fake_conf.yaml") - - test = """ - model: - name: calib_yaml - framework: mxnet - quantization: - calibration: - sampling_size: - """ - helper(test) - self.assertRaises(RuntimeError, conf.Conf, "fake_conf.yaml") - - test = """ - model: - name: calib_yaml - framework: mxnet - quantization: - calibration: - dataloader: - """ - helper(test) - self.assertRaises(RuntimeError, conf.Conf, "fake_conf.yaml") - - test = """ - model: - name: calib_yaml - framework: mxnet - quantization: - calibration: - op_wise: { - 'test': { - 'activation': [{'dtype': 'uint8'}, {'algorithm': 'minmax'}] - } - } - - """ - helper(test) - self.assertRaises(RuntimeError, conf.Conf, "fake_conf.yaml") - - def test_quantization(self): - test = """ - model: - name: quant_yaml - framework: mxnet - quantization: - model_wise: - weights: - granularity: per_channel - """ - helper(test) - self.assertRaises(RuntimeError, conf.Conf, "fake_conf.yaml") - - test = """ - model: - name: quant_yaml - framework: mxnet - quantization: - model_wise: - approach: - """ - helper(test) - self.assertRaises(RuntimeError, conf.Conf, "fake_conf.yaml") - - test = """ - model: - name: quant_yaml - framework: mxnet - quantization: - approach: post_training_static_quant, quant_aware_training - """ - helper(test) - self.assertRaises(RuntimeError, conf.Conf, "fake_conf.yaml") - - test = """ - model: - name: quant_yaml - framework: mxnet - quantization: - model_wise: - activation: - scheme: asym - dtype: int8 - weight: - scheme: asym - dtype: int8 - """ - helper(test) - conf.Conf("fake_conf.yaml") - - test = """ - model: - name: quant_yaml - framework: mxnet - quantization: - model_wise: - activation: - scheme: - dtype: int8 - weight: - scheme: asym - dtype: int8 - """ - helper(test) - self.assertRaises(RuntimeError, conf.Conf, "fake_conf.yaml") - - def test_tuning(self): - test = """ - model: - name: tuning_yaml - framework: mxnet - tuning: - accuracy_criterion: - relative: 0.01 - strategy: - name: basic, mse - """ - helper(test) - self.assertRaises(RuntimeError, conf.Conf, "fake_conf.yaml") - - test = """ - model: - name: tuning_yaml - framework: mxnet - tuning: - accuracy_criterion: - relative: 0.01 - """ - helper(test) - self.assertRaises(RuntimeError, conf.Conf, "fake_conf.yaml") - - test = """ - model: - name: tuning_yaml - framework: mxnet - tuning: - accuracy_criterion: - relative: 0.01 - strategy: - name: fake - """ - helper(test) - self.assertRaises(RuntimeError, conf.Conf, "fake_conf.yaml") - - test = """ - model: - name: tuning_yaml - framework: mxnet - tuning: - accuracy_criterion: - relative: - strategy: - name: basic - """ - helper(test) - self.assertRaises(RuntimeError, conf.Conf, "fake_conf.yaml") - - test = """ - model: - name: tuning_yaml - framework: mxnet - tuning: - accuracy_criterion: - exit_policy: - timeout: 3 - """ - helper(test) - self.assertRaises(RuntimeError, conf.Conf, "fake_conf.yaml") - - test = """ - model: - name: tuning_yaml - framework: mxnet - tuning: - accuracy_criterion: - relative: 0.01 - absolute: 0.01 - """ - helper(test) - self.assertRaises(RuntimeError, conf.Conf, "fake_conf.yaml") - - def test_workspace(self): - test = """ - model: - name: workspace_yaml - framework: mxnet - tuning: - workspace: - -path: ./workspace - """ - helper(test) - self.assertRaises(RuntimeError, conf.Conf, "fake_conf.yaml") - - def test_inputs_outputs(self): - test = """ - model: - name: inout_yaml - framework: mxnet - inputs: x, y - """ - helper(test) - config = conf.Conf("fake_conf.yaml") - self.assertEqual(config.usr_cfg.model.inputs, ["x", "y"]) - - def test_objective(self): - test = """ - model: - name: inout_yaml - framework: mxnet - inputs: x, y - tuning: - multi_objectives: - objective: accuracy - higher_is_better: True - """ - helper(test) - config = conf.Conf("fake_conf.yaml") - self.assertEqual(config.usr_cfg.tuning.multi_objectives.higher_is_better, [True]) - - test = """ - model: - name: inout_yaml - framework: mxnet - inputs: x, y - tuning: - multi_objectives: - objective: accuracy, performance - higher_is_better: True, False - """ - helper(test) - config = conf.Conf("fake_conf.yaml") - self.assertEqual(config.usr_cfg.tuning.multi_objectives.higher_is_better, [True, False]) - - test = """ - model: - name: inout_yaml - framework: mxnet - inputs: x, y - tuning: - multi_objectives: - objective: accuracy, performance - higher_is_better: True False - """ - helper(test) - config = conf.Conf("fake_conf.yaml") - self.assertEqual(config.usr_cfg.tuning.multi_objectives.higher_is_better, [True, False]) - - test = """ - model: - name: inout_yaml - framework: mxnet - inputs: x, y - tuning: - multi_objectives: - objective: accuracy, performance - higher_is_better: [True, False] - """ - helper(test) - config = conf.Conf("fake_conf.yaml") - self.assertEqual(config.usr_cfg.tuning.multi_objectives.higher_is_better, [True, False]) - - test = """ - model: - name: inout_yaml - framework: mxnet - inputs: x, y - tuning: - multi_objectives: - objective: accuracy, performance - higher_is_better: True False - weight: [0.2, 0.1, 0.7] - """ - helper(test) - self.assertRaises(RuntimeError, conf.Conf, "fake_conf.yaml") - - def test_modelwise_conf_merge(self): - test = """ - model: - name: inout_yaml - framework: mxnet - quantization: - model_wise: - weight: - algorithm: minmax - activation: - algorithm: minmax - """ - helper(test) - config = conf.QuantConf("fake_conf.yaml") - - framework_modelwise_capability = { - "CONV2D": { - "activation": { - "dtype": ["uint8", "fp32"], - "scheme": ["asym", "sym"], - "granularity": ["per_tensor"], - "algorithm": ["minmax", "kl"], - }, - "weight": { - "dtype": ["int8", "fp32"], - "scheme": [ - "sym", - ], - "granularity": ["per_channel", "per_tensor"], - "algorithm": ["minmax"], - }, - }, - } - - tune_space = config.modelwise_tune_space(framework_modelwise_capability) - self.assertEqual(tune_space["CONV2D"]["activation"]["algorithm"], ["minmax"]) - - def test_metric(self): - test = """ - model: - name: metric_yaml - framework: mxnet - evaluation: - accuracy: - multi_metrics: - topk: 1 - MSE: {} - """ - helper(test) - - metrics = {"topk": 1, "MSE": {}} - config = conf.QuantConf("fake_conf.yaml") - self.assertEqual(config.usr_cfg.evaluation.accuracy.multi_metrics, metrics) - - test = """ - model: - name: metric_yaml - framework: mxnet - evaluation: - accuracy: - multi_metrics: - weight: 0.5 0.5 0.6 - topk: 1 - MSE: {} - """ - helper(test) - self.assertRaises((AssertionError, RuntimeError), conf.Conf, "fake_conf.yaml") - - test = """ - model: - name: metric_yaml - framework: mxnet - evaluation: - accuracy: - multi_metrics: - higher_is_better: True, False - topk: 1 - MSE: {} - """ - helper(test) - config = conf.QuantConf("fake_conf.yaml") - self.assertEqual(config.usr_cfg.evaluation.accuracy.multi_metrics.higher_is_better, [True, False]) - - def test_modelwise_conf_merge2(self): - test = """ - model: - name: inout_yaml - framework: mxnet - quantization: - model_wise: - weight: - algorithm: minmax - activation: - algorithm: minmax - dtype: ['uint8', 'fp32'] - """ - helper(test) - config = conf.QuantConf("fake_conf.yaml") - - framework_modelwise_capability = { - "CONV2D": { - "activation": { - "dtype": ["iint8", "fp32"], - "scheme": ["asym", "sym"], - "granularity": ["per_tensor"], - "algorithm": ["minmax", "kl"], - }, - "weight": { - "dtype": ["int8", "fp32"], - "scheme": [ - "sym", - ], - "granularity": ["per_channel", "per_tensor"], - "algorithm": ["minmax"], - }, - }, - } - - tune_space = config.modelwise_tune_space(framework_modelwise_capability) - self.assertEqual(tune_space["CONV2D"]["activation"]["dtype"], ["fp32"]) - - def test_prune(self): - test_pytorch_prune = """ - model: - name: imagenet_prune - framework: pytorch - - pruning: - train: - start_epoch: 0 - end_epoch: 4 - dataloader: - batch_size: 30 - dataset: - ImageFolder: - root: /path/to/training/dataset - optimizer: - SGD: - learning_rate: 0.1 - momentum: 0.1 - nesterov: True - weight_decay: 0.1 - criterion: - CrossEntropyLoss: - reduction: sum - approach: - weight_compression: - initial_sparsity: 0.0 - target_sparsity: 0.97 - pruners: - - !Pruner - start_epoch: 1 - end_epoch: 3 - names: ['layer1.0.conv1.weight'] - - - !Pruner - start_epoch: 0 - end_epoch: 4 - target_sparsity: 0.6 - update_frequency: 2 - names: ['layer1.0.conv2.weight'] - """ - helper(test_pytorch_prune) - config = conf.Conf("fake_conf.yaml") - test_tensorflow_prune = """ - model: - name: vit - framework: tensorflow - - pruning: - train: - epoch: 15 - optimizer: - AdamW: - learning_rate: 0.001 - weight_decay: 0.0001 - criterion: - CrossEntropyLoss: - reduction: sum_over_batch_size - from_logits: True - approach: - weight_compression: - initial_sparsity: 0.0 - target_sparsity: 0.7 - start_epoch: 0 - end_epoch: 9 - pruners: - - !Pruner - start_epoch: 0 - end_epoch: 9 - prune_type: basic_magnitude - - evaluation: - accuracy: - metric: - topk: 1 - - tuning: - accuracy_criterion: - relative: 0.01 - exit_policy: - timeout: 0 - random_seed: 9527 - """ - helper(test_tensorflow_prune) - config = conf.Conf("fake_conf.yaml") - - def test_data_type(self): - test = """ - model: - name: test - framework: tensorflow - - quantization: - calibration: - sampling_size: 20 - dataloader: - batch_size: 1 - dataset: - dummy: - shape: [[224,224], [256,256]] - high: [128., 127] - low: 1, 0 - dtype: ['float32', 'int8'] - """ - helper(test) - cfg = conf.Conf("fake_conf.yaml").usr_cfg - dataset = cfg["quantization"]["calibration"]["dataloader"]["dataset"]["dummy"] - self.assertTrue(isinstance(dataset["shape"][0], tuple)) - self.assertTrue(isinstance(dataset["shape"], list)) - self.assertTrue(isinstance(dataset["high"][1], float)) - self.assertTrue(isinstance(dataset["high"][0], float)) - self.assertTrue(isinstance(dataset["low"][0], float)) - - test = """ - model: - name: test - framework: tensorflow - - quantization: - calibration: - sampling_size: 20 - dataloader: - batch_size: 1 - dataset: - dummy: - shape: [224,224] - high: 128 - low: 0.1 - dtype: ['float32', 'int8'] - """ - helper(test) - cfg = conf.Conf("fake_conf.yaml").usr_cfg - dataset = cfg["quantization"]["calibration"]["dataloader"]["dataset"]["dummy"] - self.assertTrue(isinstance(dataset["shape"], tuple)) - self.assertTrue(isinstance(dataset["high"], float)) - - test = """ - model: - name: test - framework: tensorflow - - quantization: - calibration: - sampling_size: 20 - dataloader: - batch_size: 1 - dataset: - style_transfer: - content_folder: test - style_folder: test - crop_ratio: 0.5 - resize_shape: 10,10 - transform: - RandomResizedCrop: - size: 10 - scale: [0.07, 0.99] - ratio: [0.6, 0.8] - """ - helper(test) - cfg = conf.Conf("fake_conf.yaml").usr_cfg - shape_cfg = cfg["quantization"]["calibration"]["dataloader"]["dataset"]["style_transfer"]["resize_shape"] - self.assertTrue(isinstance(shape_cfg, list)) - transform_cfg = cfg["quantization"]["calibration"]["dataloader"]["transform"]["RandomResizedCrop"] - self.assertTrue(isinstance(transform_cfg["scale"], list)) - self.assertTrue(isinstance(transform_cfg["ratio"], list)) - - test = """ - model: - name: test - framework: tensorflow - - quantization: - calibration: - sampling_size: 20 - dataloader: - batch_size: 1 - dataset: - style_transfer: - content_folder: test - style_folder: test - crop_ratio: 0.5 - resize_shape: [10,10] - """ - helper(test) - cfg = conf.Conf("fake_conf.yaml").usr_cfg - shape_cfg = cfg["quantization"]["calibration"]["dataloader"]["dataset"]["style_transfer"]["resize_shape"] - self.assertTrue(isinstance(shape_cfg, list)) - - test = """ - model: - name: test - framework: tensorflow - - quantization: - calibration: - sampling_size: 20 - dataloader: - batch_size: 1 - dataset: - dummy: - shape: 224,224 - transform: - BilinearImagenet: - height: 224 - width: 224 - mean_value: 123.68 116.78 103.94 - """ - helper(test) - cfg = conf.Conf("fake_conf.yaml").usr_cfg - shape_cfg = cfg["quantization"]["calibration"]["dataloader"]["dataset"]["dummy"]["shape"] - self.assertTrue(isinstance(shape_cfg, tuple)) - transform_cfg = cfg["quantization"]["calibration"]["dataloader"]["transform"]["BilinearImagenet"] - self.assertTrue(isinstance(transform_cfg["mean_value"], list)) - - def test_yaml_detection(self): - try: - cfg = conf.Conf("not_exist.yaml").usr_cfg - except: - pass - - def test_deep_set(self): - from neural_compressor.conf.dotdict import DotDict, deep_set - - cfg = {"evaluation": {"accuracy": {}}} - dot_cfg = DotDict(cfg) - deep_set(dot_cfg, "evaluation.accuracy.metric", "iou") - deep_set(dot_cfg, "evaluation.accuracy.multi_metrics.weight", [0.1, 0, 9]) - deep_set(dot_cfg, "evaluation.accuracy.multi_metrics.mAP.anno_path", "anno_path_test") - self.assertTrue(dot_cfg.evaluation == dot_cfg["evaluation"]) - self.assertTrue(dot_cfg.evaluation.accuracy == dot_cfg["evaluation"]["accuracy"]) - self.assertTrue(dot_cfg.evaluation.accuracy.metric == dot_cfg["evaluation"]["accuracy"]["metric"]) - self.assertTrue(dot_cfg.evaluation.accuracy.multi_metrics == dot_cfg["evaluation"]["accuracy"]["multi_metrics"]) - self.assertTrue(dot_cfg.evaluation.accuracy.multi_metrics.weight == [0.1, 0, 9]) - self.assertTrue(dot_cfg.evaluation.accuracy.multi_metrics.mAP.anno_path == "anno_path_test") - multi_metrics1 = dot_cfg.evaluation.accuracy.multi_metrics - multi_metrics2 = dot_cfg["evaluation"]["accuracy"]["multi_metrics"] - self.assertTrue(multi_metrics1 == multi_metrics2) - self.assertTrue(list(multi_metrics1.keys()) == ["weight", "mAP"]) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/config/test_config_regex.py b/test/config/test_config_regex.py deleted file mode 100644 index 8001aa89778..00000000000 --- a/test/config/test_config_regex.py +++ /dev/null @@ -1,210 +0,0 @@ -# -# -*- coding: utf-8 -*- -# -import os -import unittest - -import tensorflow as tf -from tensorflow.compat.v1 import graph_util - -from neural_compressor.adaptor.tf_utils.util import disable_random - - -def build_fake_yaml(): - fake_yaml = """ - model: - name: fake_yaml - framework: tensorflow - inputs: input - outputs: op_to_store - device: cpu - quantization: - op_wise: { - \"conv1_[1-2]\": { - \"activation\": {\"dtype\": [\"fp32\"]}, - }, - } - evaluation: - accuracy: - metric: - topk: 1 - tuning: - strategy: - name: basic - exit_policy: - timeout: 0 - accuracy_criterion: - relative: 0.05 - exit_policy: - performance_only: True - workspace: - path: saved - """ - with open("fake_yaml.yaml", "w", encoding="utf-8") as f: - f.write(fake_yaml) - f.close() - - -def build_fake_yaml_invalid_model_wise(): - fake_yaml = """ - model: - name: fake_yaml - framework: tensorflow - inputs: input - outputs: op_to_store - device: cpu - quantization: - op_wise: { - \"conv1_[1-2]\": { - \"activation\": {\"dtype\": [\"fp32\"]}, - }, - } - model_wise: - weight: - granularity: per_channel - scheme: sym - dtype: int8 - algorithm: minmax - evaluation: - accuracy: - metric: - topk: 1 - tuning: - strategy: - name: basic - exit_policy: - timeout: 0 - accuracy_criterion: - relative: 0.05 - workspace: - path: saved - """ - with open("fake_yaml_with_invalid_cfg.yaml", "w", encoding="utf-8") as f: - f.write(fake_yaml) - f.close() - - -class TestConfigRegex(unittest.TestCase): - @classmethod - def setUpClass(self): - build_fake_yaml() - build_fake_yaml_invalid_model_wise() - - @classmethod - def tearDownClass(self): - os.remove("fake_yaml.yaml") - os.remove("fake_yaml_with_invalid_cfg.yaml") - - @disable_random() - def test_config_regex(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - top_relu = tf.nn.relu(x) - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(top_relu, paddings, "CONSTANT") - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv_weights_2 = tf.compat.v1.get_variable( - "weight_2", [3, 8, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv2d(x_pad, conv_weights, strides=[1, 2, 2, 1], padding="VALID", name="conv1_1") - normed1 = tf.compat.v1.layers.batch_normalization(conv) - - relu = tf.nn.relu(normed1) - max_pool = tf.nn.max_pool(relu, ksize=1, strides=[1, 2, 2, 1], padding="SAME") - conv_bias = tf.compat.v1.get_variable("bias", [16], initializer=tf.compat.v1.random_normal_initializer()) - conv_1 = tf.nn.conv2d(max_pool, conv_weights_2, strides=[1, 2, 2, 1], padding="VALID", name="conv1_3") - conv_bias = tf.math.add(conv_1, conv_bias) - relu6 = tf.nn.relu6(conv_bias, name="op_to_store") - - out_name = relu6.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - - for i in output_graph_def.node: - if i.op.find("Add") != -1: - i.op = "Add" - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - - found_fp32_conv = False - found_quantized_conv = False - for i in output_graph.graph_def.node: - if i.op == "Conv2D" and i.name == "conv1_1": - found_fp32_conv = True - - if i.op.find("QuantizedConv2D") != -1 and i.name == "conv1_3_eightbit_requantize": - found_quantized_conv = True - - self.assertEqual(found_fp32_conv, True) - self.assertEqual(found_quantized_conv, True) - - def test_config_regex_with_invalid_cfg(self): - tf.compat.v1.disable_eager_execution() - tf.compat.v1.reset_default_graph() - tf.compat.v1.set_random_seed(1) - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - top_relu = tf.nn.relu(x) - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(top_relu, paddings, "CONSTANT") - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv_weights_2 = tf.compat.v1.get_variable( - "weight_2", [3, 8, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv2d(x_pad, conv_weights, strides=[1, 2, 2, 1], padding="VALID", name="conv1_1") - normed1 = tf.compat.v1.layers.batch_normalization(conv) - - relu = tf.nn.relu(normed1) - max_pool = tf.nn.max_pool(relu, ksize=1, strides=[1, 2, 2, 1], padding="SAME") - conv_bias = tf.compat.v1.get_variable("bias", [16], initializer=tf.compat.v1.random_normal_initializer()) - conv_1 = tf.nn.conv2d(max_pool, conv_weights_2, strides=[1, 2, 2, 1], padding="VALID", name="conv1_3") - conv_bias = tf.math.add(conv_1, conv_bias) - relu6 = tf.nn.relu6(conv_bias, name="op_to_store") - - out_name = relu6.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - - for i in output_graph_def.node: - if i.op.find("Add") != -1: - i.op = "Add" - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml_with_invalid_cfg.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - - found_fp32_conv = False - found_quantized_conv = False - for i in output_graph.graph_def.node: - if i.op == "Conv2D" and i.name == "conv1_1": - found_fp32_conv = True - - if i.op.find("QuantizedConv2D") != -1 and i.name == "conv1_3_eightbit_requantize": - found_quantized_conv = True - - self.assertEqual(found_fp32_conv, True) - self.assertEqual(found_quantized_conv, True) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/config/test_pythonic_config.py b/test/config/test_pythonic_config.py deleted file mode 100644 index 6bfa55288b2..00000000000 --- a/test/config/test_pythonic_config.py +++ /dev/null @@ -1,390 +0,0 @@ -"""Tests for pythonic config file.""" - -import copy -import os -import shutil -import unittest - -import numpy as np -import onnx -import onnxruntime as ort -import tensorflow as tf -import torch -from onnx import TensorProto, helper -from tensorflow.core.framework import attr_value_pb2, graph_pb2, node_def_pb2 -from tensorflow.python.framework import dtypes, tensor_util -from torch import nn - -from neural_compressor.adaptor import FRAMEWORKS -from neural_compressor.adaptor.torch_utils.bf16_convert import BF16ModuleWrapper -from neural_compressor.conf.pythonic_config import ActivationConf, OpQuantConf, WeightConf, config -from neural_compressor.data import Datasets -from neural_compressor.experimental import NAS, Distillation, Quantization, common -from neural_compressor.experimental.data.dataloaders.pytorch_dataloader import PyTorchDataLoader -from neural_compressor.experimental.pruning_v2 import Pruning - - -def build_matmul_model(): - A = helper.make_tensor_value_info("A", TensorProto.FLOAT, [1, 1, 5, 5]) - B_init = helper.make_tensor( - "B", TensorProto.FLOAT, [1, 1, 5, 1], np.random.random([1, 1, 5, 1]).reshape(5).tolist() - ) - C = helper.make_tensor_value_info("C", TensorProto.FLOAT, [1, 1, 5, 1]) - D = helper.make_tensor_value_info("D", TensorProto.FLOAT, [1, 1, 5, 1]) - H = helper.make_tensor_value_info("H", TensorProto.FLOAT, [1, 1, 5, 1]) - - matmul_node = onnx.helper.make_node("MatMul", ["A", "B"], ["C"], name="Matmul") - e_value = np.random.randint(2, size=(5)).astype(np.float32) - E_init = helper.make_tensor("E", TensorProto.FLOAT, [1, 1, 5, 1], e_value.reshape(5).tolist()) - add = onnx.helper.make_node("Add", ["C", "E"], ["D"], name="add") - f_value = np.random.randint(2, size=(5)).astype(np.float32) - F_init = helper.make_tensor("F", TensorProto.FLOAT, [1, 1, 5, 1], e_value.reshape(5).tolist()) - add2 = onnx.helper.make_node("Add", ["D", "F"], ["H"], name="add2") - graph = helper.make_graph([matmul_node, add, add2], "test_graph_1", [A], [H], [E_init, F_init, B_init]) - model = helper.make_model(graph) - model = helper.make_model(graph, **{"opset_imports": [helper.make_opsetid("", 13)]}) - return model - - -def build_conv2d_model(): - input_node = node_def_pb2.NodeDef() - input_node.name = "input" - input_node.op = "Placeholder" - input_node.attr["dtype"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - - conv1_weight_node = node_def_pb2.NodeDef() - conv1_weight_node.name = "conv1_weights" - conv1_weight_node.op = "Const" - conv1_weight_value = np.float32(np.abs(np.random.randn(3, 3, 3, 32))) - conv1_weight_node.attr["dtype"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - conv1_weight_node.attr["value"].CopyFrom( - attr_value_pb2.AttrValue( - tensor=tensor_util.make_tensor_proto( - conv1_weight_value, conv1_weight_value.dtype.type, conv1_weight_value.shape - ) - ) - ) - - conv1_node = node_def_pb2.NodeDef() - conv1_node.name = "conv1" - conv1_node.op = "Conv2D" - conv1_node.attr["T"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - conv1_node.input.extend([input_node.name, conv1_weight_node.name]) - conv1_node.attr["strides"].CopyFrom( - attr_value_pb2.AttrValue(list=attr_value_pb2.AttrValue.ListValue(i=[1, 1, 1, 1])) - ) - conv1_node.attr["dilations"].CopyFrom( - attr_value_pb2.AttrValue(list=attr_value_pb2.AttrValue.ListValue(i=[1, 1, 1, 1])) - ) - conv1_node.attr["padding"].CopyFrom(attr_value_pb2.AttrValue(s=b"SAME")) - conv1_node.attr["data_format"].CopyFrom(attr_value_pb2.AttrValue(s=b"NHWC")) - - bias_node = node_def_pb2.NodeDef() - bias_node.name = "conv1_bias" - bias_node.op = "Const" - bias_value = np.float32(np.abs(np.random.randn(32))) - bias_node.attr["dtype"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - bias_node.attr["value"].CopyFrom( - attr_value_pb2.AttrValue( - tensor=tensor_util.make_tensor_proto(bias_value, bias_value.dtype.type, bias_value.shape) - ) - ) - - bias_add_node = node_def_pb2.NodeDef() - bias_add_node.name = "out" - bias_add_node.op = "BiasAdd" - bias_add_node.attr["T"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - bias_add_node.input.extend([conv1_node.name, bias_node.name]) - bias_add_node.attr["data_format"].CopyFrom(attr_value_pb2.AttrValue(s=b"NHWC")) - - test_graph = graph_pb2.GraphDef() - test_graph.node.extend( - [ - input_node, - conv1_weight_node, - conv1_node, - bias_node, - bias_add_node, - ] - ) - return test_graph - - -class ConvNet(torch.nn.Module): - def __init__(self, channels, dimensions): - super().__init__() - self.conv = torch.nn.Conv2d(3, channels, (3, 3), padding=1) - self.avg_pooling = torch.nn.AvgPool2d((64, 64)) - self.dense = torch.nn.Linear(channels, dimensions) - self.out = torch.nn.Linear(dimensions, 2) - self.activation = torch.nn.Softmax() - - def forward(self, inputs): - outputs = self.conv(inputs) - outputs = self.avg_pooling(outputs).squeeze() - outputs = self.dense(outputs) - outputs = self.out(outputs) - outputs = self.activation(outputs) - return outputs - - -def model_builder(model_arch_params): - channels = model_arch_params["channels"] - dimensions = model_arch_params["dimensions"] - return ConvNet(channels, dimensions) - - -class torch_model(torch.nn.Module): - def __init__(self): - super().__init__() - self.conv = nn.Conv2d(3, 1, 1) - self.linear = nn.Linear(224 * 224, 5) - - def forward(self, x): - x = self.conv(x) - x = x.view(1, -1) - x = self.linear(x) - return x - - -class TestPythonicConf(unittest.TestCase): - @classmethod - def tearDownClass(self): - shutil.rmtree("./nc_workspace", ignore_errors=True) - - def test_config_setting(self): - config.quantization.inputs = ["image"] - config.quantization.outputs = ["out"] - config.quantization.approach = "post_training_dynamic_quant" - config.quantization.device = "gpu" - config.quantization.op_type_dict = {"Conv": {"weight": {"dtype": ["fp32"]}, "activation": {"dtype": ["fp32"]}}} - config.quantization.op_name_dict = { - "layer1.0.conv1": {"activation": {"dtype": ["fp32"]}, "weight": {"dtype": ["fp32"]}} - } - config.quantization.strategy = "mse" - config.quantization.objective = "accuracy" - config.quantization.timeout = 100 - config.quantization.max_trials = 100 - config.quantization.accuracy_criterion.relative = 0.5 - config.quantization.reduce_range = False - config.quantization.use_bf16 = False - config.benchmark.cores_per_instance = 10 - - self.assertEqual(config.quantization.inputs, ["image"]) - self.assertEqual(config.quantization.outputs, ["out"]) - self.assertEqual(config.quantization.approach, "post_training_dynamic_quant") - self.assertEqual(config.quantization.device, "gpu") - self.assertEqual( - config.quantization.op_type_dict, - {"Conv": {"weight": {"dtype": ["fp32"]}, "activation": {"dtype": ["fp32"]}}}, - ) - self.assertEqual( - config.quantization.op_name_dict, - {"layer1.0.conv1": {"activation": {"dtype": ["fp32"]}, "weight": {"dtype": ["fp32"]}}}, - ) - self.assertEqual(config.quantization.strategy, "mse") - self.assertEqual(config.quantization.objective, "accuracy") - self.assertEqual(config.quantization.timeout, 100) - self.assertEqual(config.quantization.max_trials, 100) - self.assertEqual(config.quantization.accuracy_criterion.relative, 0.5) - self.assertEqual(config.benchmark.cores_per_instance, 10) - - config.quantization.accuracy_criterion.absolute = 0.4 - self.assertEqual(config.quantization.accuracy_criterion.absolute, 0.4) - self.assertEqual(config.quantization.accuracy_criterion.relative, None) - - config.onnxruntime.precisions = ["int8", "uint8"] - config.onnxruntime.graph_optimization_level = "DISABLE_ALL" - q = Quantization(config) - q.model = build_matmul_model() - self.assertEqual(q.conf.usr_cfg.reduce_range, False) - self.assertEqual(q.conf.usr_cfg.use_bf16, False) - q.pre_process() - self.assertEqual(q.strategy.adaptor.query_handler.get_precisions(), ["int8", "uint8"]) - self.assertNotEqual(config.mxnet, None) - self.assertNotEqual(config.tensorflow, None) - self.assertNotEqual(config.pytorch, None) - self.assertNotEqual(config.keras, None) - - def test_weight_activation_op(self): - opconf = OpQuantConf() - self.assertEqual(opconf.op_type, None) - - opconf = OpQuantConf("MatMul") - self.assertEqual(opconf.op_type, "MatMul") - self.assertNotEqual(opconf.weight, None) - self.assertNotEqual(opconf.activation, None) - - opconf.weight.datatype = ["int8"] - opconf.activation.datatype = ["uint8"] - opconf.weight.scheme = ["asym"] - opconf.activation.scheme = ["sym"] - opconf.weight.granularity = ["per_channel"] - opconf.activation.granularity = ["per_tensor"] - opconf.weight.algorithm = ["minmax"] - opconf.activation.algorithm = ["minmax"] - self.assertEqual(opconf.weight.datatype, ["int8"]) - self.assertEqual(opconf.activation.datatype, ["uint8"]) - self.assertEqual(opconf.weight.scheme, ["asym"]) - self.assertEqual(opconf.activation.scheme, ["sym"]) - self.assertEqual(opconf.weight.granularity, ["per_channel"]) - self.assertEqual(opconf.activation.granularity, ["per_tensor"]) - self.assertEqual(opconf.weight.algorithm, ["minmax"]) - self.assertEqual(opconf.activation.algorithm, ["minmax"]) - - def test_quantization(self): - q = Quantization(config) - q.model = build_matmul_model() - q_model = q() - self.assertTrue(any([i.name.endswith("_quant") for i in q_model.nodes()])) - - config.onnxruntime.precisions = ["fp32"] - q = Quantization(config) - q.model = build_matmul_model() - q_model = q() - self.assertTrue(all([not i.name.endswith("_quant") for i in q_model.nodes()])) - - def test_distillation(self): - config.quantization.device = "cpu" - distiller = Distillation(config) - model = ConvNet(16, 32) - origin_weight = copy.deepcopy(model.out.weight) - distiller.model = model - distiller.teacher_model = ConvNet(16, 32) - - # Customized train, evaluation - datasets = Datasets("pytorch") - dummy_dataset = datasets["dummy"](shape=(32, 3, 64, 64), low=0.0, high=1.0, label=True) - dummy_dataloader = PyTorchDataLoader(dummy_dataset) - - def train_func(model): - epochs = 3 - iters = 10 - criterion = torch.nn.CrossEntropyLoss() - optimizer = torch.optim.SGD(model.parameters(), lr=0.0001) - for nepoch in range(epochs): - model.train() - cnt = 0 - for image, target in dummy_dataloader: - print(".", end="") - cnt += 1 - output = model(image).unsqueeze(dim=0) - loss = criterion(output, target) - loss = distiller.on_after_compute_loss(image, output, loss) - optimizer.zero_grad() - loss.backward() - optimizer.step() - if cnt >= iters: - break - - def eval_func(model): - model.eval() - acc = 0 - for image, target in dummy_dataloader: - output = model(image).cpu().detach().numpy() - acc += np.sum(output == target) - return {"acc": acc / len(dummy_dataset)} - - distiller.train_func = train_func - distiller.eval_func = eval_func - model = distiller() - weight = model.model.out.weight - self.assertTrue(torch.any(weight != origin_weight)) - - def test_pruning(self): - prune = Pruning(config) - model = ConvNet(16, 32) - origin_weight = copy.deepcopy(model.out.weight) - prune.model = model - - # Customized train, evaluation - datasets = Datasets("pytorch") - dummy_dataset = datasets["dummy"](shape=(32, 3, 64, 64), low=0.0, high=1.0, label=True) - dummy_dataloader = PyTorchDataLoader(dummy_dataset) - - def train_func(model): - epochs = 3 - iters = 10 - criterion = torch.nn.CrossEntropyLoss() - optimizer = torch.optim.SGD(model.parameters(), lr=0.0001) - for nepoch in range(epochs): - model.train() - cnt = 0 - prune.on_epoch_begin(nepoch) - for image, target in dummy_dataloader: - print(".", end="") - cnt += 1 - prune.on_step_begin(cnt) - output = model(image).unsqueeze(dim=0) - loss = criterion(output, target) - optimizer.zero_grad() - loss.backward() - optimizer.step() - prune.on_step_end() - if cnt >= iters: - break - prune.on_epoch_end() - - def eval_func(model): - model.eval() - acc = 0 - for image, target in dummy_dataloader: - output = model(image).cpu().detach().numpy() - acc += np.sum(output == target) - return {"acc": acc / len(dummy_dataset)} - - prune.train_func = train_func - prune.eval_func = eval_func - model = prune() - weight = model.model.out.weight - self.assertTrue(torch.any(weight != origin_weight)) - - def test_use_bf16(self): - config.quantization.device = "cpu" - config.quantization.approach = "post_training_dynamic_quant" - config.quantization.use_bf16 = False - q = Quantization(config) - q.model = torch_model() - os.environ["FORCE_BF16"] = "1" - q_model = q() - del os.environ["FORCE_BF16"] - self.assertEqual(isinstance(q_model.model.linear, BF16ModuleWrapper), False) - - def test_quantization_pytorch(self): - config.quantization.device = "cpu" - config.quantization.backend = "default" - config.quantization.approach = "post_training_dynamic_quant" - config.quantization.use_bf16 = False - q = Quantization(config) - q.model = torch_model() - q_model = q() - self.assertEqual(isinstance(q_model.model.linear, torch.nn.quantized.dynamic.modules.linear.Linear), True) - - -class TestTFPyhonicConf(unittest.TestCase): - @classmethod - def tearDownClass(self): - shutil.rmtree("./nc_workspace", ignore_errors=True) - - def test_tf_quantization(self): - config.quantization.inputs = ["input"] - config.quantization.outputs = ["out"] - config.quantization.approach = "post_training_static_quant" - config.quantization.device = "cpu" - config.quantization.strategy = "basic" - config.quantization.objective = "accuracy" - config.quantization.timeout = 100 - config.quantization.accuracy_criterion.relative = 0.5 - config.quantization.reduce_range = False - - q = Quantization(config) - q.model = build_conv2d_model() - dataset = q.dataset("dummy", shape=(1, 224, 224, 3), label=True) - q.calib_dataloader = common.DataLoader(dataset) - q_model = q() - - self.assertTrue(any([i.name.endswith("_requantize") for i in q_model.graph_def.node])) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/data/test_exp_dataloader.py b/test/data/test_exp_dataloader.py deleted file mode 100644 index 52c094aa987..00000000000 --- a/test/data/test_exp_dataloader.py +++ /dev/null @@ -1,239 +0,0 @@ -"""Tests for the dataloader module.""" - -import os -import platform -import shutil -import unittest - -import numpy as np -from PIL import Image - -from neural_compressor.experimental.data import DATALOADERS, TRANSFORMS, Datasets -from neural_compressor.utils.create_obj_from_config import create_dataloader, create_dataset - - -class TestDataloader(unittest.TestCase): - def test_iterable_dataset(self): - class iter_dataset(object): - def __iter__(self): - for i in range(100): - yield np.zeros([256, 256, 3]) - - dataset = iter_dataset() - data_loader = DATALOADERS["tensorflow"](dataset) - iterator = iter(data_loader) - data = next(iterator) - self.assertEqual(data.shape, (1, 256, 256, 3)) - - def test_tensorflow_dummy(self): - datasets = Datasets("tensorflow") - dataset = datasets["dummy"](shape=(4, 256, 256, 3)) - - data_loader = DATALOADERS["tensorflow"](dataset) - iterator = iter(data_loader) - data = next(iterator) - self.assertEqual(data[0].shape, (1, 256, 256, 3)) - # dynamic batching - data_loader.batch(batch_size=2, last_batch="rollover") - iterator = iter(data_loader) - data = next(iterator) - self.assertEqual(data[0].shape, (2, 256, 256, 3)) - - with self.assertRaises(AssertionError): - dataset = datasets["dummy"](shape=[(4, 256, 256, 3), (256, 256, 3)]) - with self.assertRaises(AssertionError): - dataset = datasets["dummy"](shape=(4, 256, 256, 3), low=[1.0, 0.0]) - with self.assertRaises(AssertionError): - dataset = datasets["dummy"](shape=(4, 256, 256, 3), high=[128.0, 127.0]) - with self.assertRaises(AssertionError): - dataset = datasets["dummy"](shape=(4, 256, 256, 3), dtype=["float32", "int8"]) - - def test_tensorflow_dummy_v2(self): - datasets = Datasets("tensorflow") - # test with label - dataset = datasets["dummy_v2"](input_shape=(256, 256, 3), label_shape=(1,)) - data_loader = DATALOADERS["tensorflow"](dataset) - iterator = iter(data_loader) - data = next(iterator) - self.assertEqual(data[0].shape, (1, 256, 256, 3)) - self.assertEqual(data[1].shape, (1, 1)) - # dynamic batching - data_loader.batch(batch_size=2, last_batch="rollover") - iterator = iter(data_loader) - data = next(iterator) - self.assertEqual(data[0].shape, (2, 256, 256, 3)) - self.assertEqual(data[1].shape, (2, 1)) - - # test without label - dataset = datasets["dummy_v2"](input_shape=(256, 256, 3)) - data_loader = DATALOADERS["tensorflow"](dataset) - iterator = iter(data_loader) - data = next(iterator) - self.assertEqual(data.shape, (1, 256, 256, 3)) - # dynamic batching - data_loader.batch(batch_size=2, last_batch="rollover") - iterator = iter(data_loader) - data = next(iterator) - self.assertEqual(data.shape, (2, 256, 256, 3)) - - with self.assertRaises(AssertionError): - dataset = datasets["dummy_v2"](input_shape=(256, 256, 3), low=[1.0, 0.0]) - with self.assertRaises(AssertionError): - dataset = datasets["dummy_v2"](input_shape=(256, 256, 3), high=[128.0, 127.0]) - with self.assertRaises(AssertionError): - dataset = datasets["dummy_v2"](input_shape=(256, 256, 3), dtype=["float32", "int8"]) - - def test_tensorflow_sparse_dummy_v2(self): - datasets = Datasets("tensorflow") - # test with label - dataset = datasets["sparse_dummy_v2"]( - dense_shape=[[10, 20], [5, 3]], label_shape=[[1]], sparse_ratio=[0.98, 0.8] - ) - data_loader = DATALOADERS["tensorflow"](dataset) - iterator = iter(data_loader) - data = next(iterator) - self.assertEqual(data[0][0][0].shape, (1, 4, 2)) - self.assertEqual(data[0][0][1].shape, (1, 4)) - self.assertEqual(data[0][1].shape, (1, 1)) - - # test without label - dataset = datasets["sparse_dummy_v2"](dense_shape=(256, 256, 3), sparse_ratio=0.3) - data_loader = DATALOADERS["tensorflow"](dataset) - iterator = iter(data_loader) - data = next(iterator) - self.assertEqual(data[0][0].shape, (1, 137626, 3)) - self.assertEqual(data[0][1].shape, (1, 137626)) - - with self.assertRaises(AssertionError): - dataset = datasets["sparse_dummy_v2"](dense_shape=(256, 256, 3), low=[1.0, 0.0]) - with self.assertRaises(AssertionError): - dataset = datasets["sparse_dummy_v2"](dense_shape=(256, 256, 3), high=[128.0, 127.0]) - with self.assertRaises(AssertionError): - dataset = datasets["sparse_dummy_v2"](dense_shape=(256, 256, 3), dtype=["float32", "int8"]) - with self.assertRaises(AssertionError): - dataset = datasets["sparse_dummy_v2"](dense_shape=(256, 256, 3), dtype=["0.3", "0.5"]) - with self.assertRaises(AssertionError): - dataset = datasets["sparse_dummy_v2"](dense_shape=(256, 256, 3), label_shape=[[1], [2], [3]]) - - def test_tensorflow_dataloader_multi_input(self): - import tensorflow as tf - - x = tf.data.Dataset.from_tensor_slices((np.random.random(20), np.random.random(20))) - y = tf.data.Dataset.from_tensor_slices(np.random.random(20)) - dataset = tf.data.Dataset.zip((x, y)) - dataloader = DATALOADERS["tensorflow"](dataset) - for i, (x, y) in enumerate(dataloader): - self.assertIsNotNone(x) - self.assertIsNotNone(y) - break - - def test_style_transfer_dataset(self): - random_array = np.random.random_sample([100, 100, 3]) * 255 - random_array = random_array.astype(np.uint8) - im = Image.fromarray(random_array) - im.save("test.jpg") - - datasets = Datasets("tensorflow") - dataset = datasets["style_transfer"](content_folder="./", style_folder="./") - length = len(dataset) - image, label = dataset[0] - self.assertEqual(image[0].shape, (256, 256, 3)) - self.assertEqual(image[1].shape, (256, 256, 3)) - os.remove("test.jpg") - - def test_tensorflow_list_dict(self): - dataset = [{"a": 1, "b": 2, "c": 3, "d": 4}, {"a": 5, "b": 6, "c": 7, "d": 8}] - data_loader = DATALOADERS["tensorflow"](dataset) - - iterator = iter(data_loader) - data = next(iterator) - self.assertEqual(data, {"a": [1], "b": [2], "c": [3], "d": [4]}) - - # test iterable consistent - iterator = iter(data_loader) - data = next(iterator) - self.assertEqual(data, {"a": [1], "b": [2], "c": [3], "d": [4]}) - - # dynamic batching - data_loader.batch(batch_size=2, last_batch="rollover") - iterator = iter(data_loader) - data = next(iterator) - self.assertEqual(data, {"a": [1, 5], "b": [2, 6], "c": [3, 7], "d": [4, 8]}) - - def test_pytorch_dummy(self): - datasets = Datasets("pytorch") - transform = TRANSFORMS("pytorch", "preprocess")["Resize"](**{"size": 100}) - dataset = datasets["dummy"]( - shape=[(4, 256, 256, 3), (4, 1)], high=[10.0, 10.0], low=[0.0, 0.0], transform=transform - ) - - data_loader = DATALOADERS["pytorch"](dataset) - iterator = iter(data_loader) - data, label = next(iterator) - self.assertEqual(data[0].shape, (1, 256, 256, 3)) - # dynamic batching - data_loader.batch(batch_size=2, last_batch="rollover") - iterator = iter(data_loader) - data, label = next(iterator) - self.assertEqual(data[0].shape, (2, 256, 256, 3)) - - @unittest.skipIf(platform.system().lower() == "windows", "not support mxnet on windows yet") - def test_mxnet_dummy(self): - datasets = Datasets("mxnet") - transform = TRANSFORMS("mxnet", "preprocess")["Resize"](**{"size": 100}) - dataset = datasets["dummy"](shape=(4, 256, 256, 3), transform=transform) - - data_loader = DATALOADERS["mxnet"](dataset) - iterator = iter(data_loader) - data = next(iterator) - self.assertEqual(data[0].shape, (1, 256, 256, 3)) - # dynamic batching - data_loader.batch(batch_size=2, last_batch="rollover") - iterator = iter(data_loader) - data = next(iterator) - self.assertEqual(data[0].shape, (2, 256, 256, 3)) - - dataset = datasets["dummy"](shape=(4, 256, 256, 3), label=True) - self.assertEqual(dataset[0][1], 0) - - def test_onnxrt_qlinear_dummy(self): - datasets = Datasets("onnxrt_qlinearops") - transform = TRANSFORMS("onnxrt_qlinearops", "preprocess")["Resize"](**{"size": 100}) - dataset = datasets["dummy"](shape=(4, 256, 256, 3), transform=transform) - - data_loader = DATALOADERS["onnxrt_qlinearops"](dataset) - iterator = iter(data_loader) - data = next(iterator) - self.assertEqual(data[0].shape, (1, 256, 256, 3)) - # dynamic batching - data_loader.batch(batch_size=2, last_batch="rollover") - iterator = iter(data_loader) - data = next(iterator) - self.assertEqual(data[0].shape, (2, 256, 256, 3)) - - dataset = datasets["dummy"](shape=(4, 256, 256, 3), label=False) - data_loader = DATALOADERS["onnxrt_qlinearops"](dataset) - iterator = iter(data_loader) - data = next(iterator) - self.assertEqual(len(data), 1) - - with self.assertRaises(AssertionError): - dataset = datasets["dummy"](shape=[(4, 256, 256, 3), (4, 256, 256, 3)], dtype=["float32", "int8", "int8"]) - - def test_onnx_integer_dummy(self): - datasets = Datasets("onnxrt_integerops") - dataset = datasets["dummy"](shape=(4, 256, 256, 3)) - - data_loader = DATALOADERS["onnxrt_integerops"](dataset) - iterator = iter(data_loader) - data = next(iterator) - self.assertEqual(data[0].shape, (1, 256, 256, 3)) - # dynamic batching - data_loader.batch(batch_size=2, last_batch="rollover") - iterator = iter(data_loader) - data = next(iterator) - self.assertEqual(data[0].shape, (2, 256, 256, 3)) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/data/test_exp_transformers.py b/test/data/test_exp_transformers.py deleted file mode 100644 index c2265b2ff1b..00000000000 --- a/test/data/test_exp_transformers.py +++ /dev/null @@ -1,1207 +0,0 @@ -"""Tests for the transform module.""" - -import os -import platform -import random -import unittest - -import numpy as np -from PIL import Image - -from neural_compressor.experimental.data import DATALOADERS, TRANSFORMS -from neural_compressor.utils.create_obj_from_config import create_dataset, get_postprocess -from neural_compressor.utils.utility import LazyImport - -mx = LazyImport("mxnet") -tf = LazyImport("tensorflow") -torch = LazyImport("torch") -torchvision = LazyImport("torchvision") - -random.seed(1) -np.random.seed(1) - - -class TestMetrics(unittest.TestCase): - def test_tensorflow_2(self): - image = np.ones([256, 256, 1]) - resize_kwargs = {"size": [224, 224]} - transforms = TRANSFORMS(framework="tensorflow", process="preprocess") - resize = transforms["Resize"](**resize_kwargs) - random_crop_kwargs = {"size": 128} - random_crop = transforms["RandomCrop"](**random_crop_kwargs) - transform_list = [resize, random_crop] - compose = transforms["Compose"](transform_list) - image_result = compose((image, None)) - self.assertEqual(image_result[0].shape, (128, 128)) - - -class TestONNXQLImagenetTransform(unittest.TestCase): - @classmethod - def setUpClass(cls): - cls.img = np.random.random_sample([600, 600]) * 255 - - def testResizeCropImagenetTransform(self): - transforms = TRANSFORMS("onnxrt_qlinearops", "preprocess") - transform = transforms["ResizeCropImagenet"](height=224, width=224, random_crop=True) - sample = (self.img, 0) - result = transform(sample) - resized_input = result[0] - self.assertEqual(len(resized_input), 3) - self.assertEqual(len(resized_input[0]), 224) - self.assertEqual(len(resized_input[0][0]), 224) - - -class TestONNXITImagenetTransform(unittest.TestCase): - @classmethod - def setUpClass(cls): - cls.img = np.random.random_sample([600, 600, 3]) * 255 - - def testResizeCropImagenetTransform(self): - transforms = TRANSFORMS("onnxrt_integerops", "preprocess") - transform = transforms["ResizeCropImagenet"](height=224, width=224) - sample = (self.img, 0) - result = transform(sample) - resized_input = result[0] - self.assertEqual(len(resized_input), 3) - self.assertEqual(len(resized_input[0]), 224) - self.assertEqual(len(resized_input[0][0]), 224) - - def testResizeWithAspectRatio(self): - transforms = TRANSFORMS("onnxrt_integerops", "preprocess") - transform = transforms["ResizeWithAspectRatio"](height=224, width=224) - sample = (self.img, 0) - result = transform(sample) - resized_input = result[0] - self.assertEqual(len(resized_input), 256) - self.assertEqual(len(resized_input[0]), 256) - self.assertEqual(len(resized_input[0][0]), 3) - - -class TestTensorflowImagenetTransform(unittest.TestCase): - tf.compat.v1.disable_v2_behavior() - - def testBilinearImagenetTransform(self): - transforms = TRANSFORMS("tensorflow", "preprocess") - transform = transforms["BilinearImagenet"](height=224, width=224) - rand_input = np.random.random_sample([600, 600, 3]).astype(np.float32) - sample = (rand_input, 0) - result = transform(sample) - resized_input = result[0].eval(session=tf.compat.v1.Session()) - self.assertEqual(len(resized_input), 224) - self.assertEqual(len(resized_input[0]), 224) - self.assertEqual(len(resized_input[0][0]), 3) - - transforms = TRANSFORMS("onnxrt_qlinearops", "preprocess") - transform = transforms["BilinearImagenet"](height=224, width=224) - rand_input = np.random.random_sample([600, 600, 3]).astype(np.float32) - sample = (rand_input, 0) - result = transform(sample) - self.assertEqual(len(resized_input), 224) - self.assertEqual(len(resized_input[0]), 224) - self.assertEqual(len(resized_input[0][0]), 3) - - def testResizeCropImagenetTransform1(self): - transforms = TRANSFORMS("tensorflow", "preprocess") - rand_input = np.random.random_sample([600, 600, 3]).astype(np.float32) - sample = (rand_input, 0) - transform = transforms["ResizeCropImagenet"]( - height=224, width=224, random_crop=True, random_flip_left_right=True - ) - result = transform(sample) - resized_input = result[0].eval(session=tf.compat.v1.Session()) - self.assertEqual(len(resized_input), 224) - self.assertEqual(len(resized_input[0]), 224) - self.assertEqual(len(resized_input[0][0]), 3) - - @unittest.skipIf(tf.version.VERSION < "2.5.0", "Skip tf.experimental.numpy.moveaxis") - def testResizeCropImagenetTransform2(self): - transforms = TRANSFORMS("tensorflow", "preprocess") - rand_input = np.random.random_sample([600, 600, 3]).astype(np.float32) - sample = (rand_input, 0) - transform = transforms["ResizeCropImagenet"]( - height=224, - width=224, - random_crop=False, - random_flip_left_right=False, - data_format="channels_last", - subpixels="RGB", - ) - result = transform(sample) - resized_input1 = result[0].eval(session=tf.compat.v1.Session()) - transform = transforms["ResizeCropImagenet"]( - height=224, - width=224, - random_crop=False, - random_flip_left_right=False, - data_format="channels_last", - subpixels="BGR", - ) - result = transform(sample) - resized_input2 = result[0].eval(session=tf.compat.v1.Session()) - self.assertTrue((resized_input1[..., 0] == resized_input2[..., -1]).all()) - - transform = transforms["ResizeCropImagenet"]( - height=224, - width=224, - random_crop=False, - random_flip_left_right=False, - data_format="channels_first", - subpixels="BGR", - ) - rand_input = np.moveaxis(rand_input, -1, 0) - sample = (rand_input, 0) - result = transform(sample) - resized_input3 = result[0].eval(session=tf.compat.v1.Session()) - self.assertTrue((resized_input1[..., 0] == resized_input3[..., -1]).all()) - - def testLabelShift(self): - transforms = TRANSFORMS("tensorflow", "postprocess") - transform = transforms["LabelShift"](label_shift=1) - rand_input = np.random.random_sample([600, 600, 3]).astype(np.float32) - sample = (rand_input, 1001) - label = transform(sample)[1] - self.assertEqual(label, 1000) - if platform.architecture()[0] == "64bit": - self.assertTrue(isinstance(label, np.int64) or isinstance(label, np.int32)) - else: - self.assertTrue(isinstance(label, np.int32)) - - label = transform((rand_input, [(1, 2, 3)]))[1] - self.assertTrue(isinstance(label, list)) - self.assertTrue(isinstance(label[0], tuple)) - - label = transform((rand_input, [[1, 2, 3]]))[1] - self.assertTrue(isinstance(label, list)) - self.assertTrue(isinstance(label[0], list)) - - label = transform((rand_input, [np.array([1, 2, 3])]))[1] - self.assertTrue(isinstance(label, list)) - self.assertTrue(isinstance(label[0], np.ndarray)) - - def testQuantizedInput(self): - transforms = TRANSFORMS("tensorflow", "preprocess") - transform = transforms["QuantizedInput"](dtype="uint8", scale=100) - rand_input = np.random.random_sample([600, 600, 3]).astype(np.float32) - sample = (rand_input, 1001) - result = transform(sample) - quantized_input = result[0].eval(session=tf.compat.v1.Session()) - self.assertLessEqual(quantized_input.max(), 255) - self.assertGreaterEqual(quantized_input.min(), 0) - - transform = transforms["QuantizedInput"](dtype="uint8") - sample = (rand_input, 1001) - result = transform(sample) - quantized_input = result[0] - self.assertLessEqual(quantized_input.max(), 1) - self.assertGreaterEqual(quantized_input.min(), 0) - - -class TestDataConversion(unittest.TestCase): - @classmethod - def setUpClass(cls): - if platform.system().lower() == "windows": - cls.skipTest(cls, "not support mxnet on windows yet") - cls.img = np.random.random_sample([10, 10, 3]) * 255 - cls.mx_trans = TRANSFORMS("mxnet", "preprocess") - cls.pt_trans = TRANSFORMS("pytorch", "preprocess") - - def testToPILImage(self): - trans = TestDataConversion.pt_trans["ToPILImage"]() - image, _ = trans((TestDataConversion.img.astype(np.uint8), None)) - self.assertTrue(isinstance(image, Image.Image)) - - def testToTensor(self): - trans = TestDataConversion.pt_trans["ToTensor"]() - image, _ = trans((TestDataConversion.img.astype(np.uint8), None)) - self.assertTrue(isinstance(image, torch.Tensor)) - - trans = TestDataConversion.mx_trans["ToTensor"]() - image, _ = trans((mx.nd.array(TestDataConversion.img), None)) - self.assertTrue(isinstance(image, mx.ndarray.NDArray)) # pylint: disable=no-member - - def testToNDArray(self): - trans = TestDataConversion.mx_trans["ToNDArray"]() - image, _ = trans((TestDataConversion.img.astype(np.uint8), None)) - self.assertTrue(isinstance(image, mx.ndarray.NDArray)) - - -class TestSameTransfoms(unittest.TestCase): - @classmethod - def setUpClass(cls): - if platform.system().lower() == "windows": - cls.skipTest(cls, "not support mxnet on windows yet") - cls.img = np.random.random_sample([10, 10, 3]) * 255 - cls.tf_trans = TRANSFORMS("tensorflow", "preprocess") - cls.pt_trans = TRANSFORMS("pytorch", "preprocess") - cls.mx_trans = TRANSFORMS("mxnet", "preprocess") - cls.ox_trans = TRANSFORMS("onnxrt_qlinearops", "preprocess") - cls.mx_img = mx.nd.array(cls.img.astype(np.uint8)) - cls.pt_img = Image.fromarray(cls.img.astype(np.uint8)) - cls.tf_img = tf.constant(cls.img) - _ = TRANSFORMS("tensorflow", "postprocess") - _ = TRANSFORMS("pytorch", "postprocess") - _ = TRANSFORMS("mxnet", "postprocess") - _ = TRANSFORMS("onnxrt_qlinearops", "postprocess") - _ = TRANSFORMS("onnxrt_integerops", "postprocess") - - def testCast(self): - args = {"dtype": "int64"} - tf_func = TestSameTransfoms.tf_trans["Cast"](**args) - tf_result = tf_func((TestSameTransfoms.img, None))[0] - self.assertEqual(tf_result[0][0][0].dtype, "int64") - tf_result = tf_func((TestSameTransfoms.tf_img, None))[0] - tf_result = tf_result.eval(session=tf.compat.v1.Session()) - self.assertEqual(tf_result[0][0][0].dtype, "int64") - mx_func = TestSameTransfoms.mx_trans["Cast"](**args) - mx_result = mx_func((TestSameTransfoms.mx_img, None)) - self.assertEqual(mx_result[0][0][0].dtype, np.int64) - ox_func = TestSameTransfoms.ox_trans["Cast"](**args) - ox_result = ox_func((TestSameTransfoms.img, None)) - self.assertEqual(ox_result[0][0][0].dtype, "int64") - - totensor = TestSameTransfoms.pt_trans["ToTensor"]() - cast = TestSameTransfoms.pt_trans["Cast"](**args) - pt_func = TestSameTransfoms.pt_trans["Compose"]([totensor, cast]) - pt_result = pt_func((TestSameTransfoms.pt_img, None)) - self.assertEqual(pt_result[0][0][0].dtype, torch.int64) - - def testCropToBoundingBox(self): - args = {"offset_height": 2, "offset_width": 2, "target_height": 5, "target_width": 5} - pt_func = TestSameTransfoms.pt_trans["CropToBoundingBox"](**args) - pt_result = pt_func((TestSameTransfoms.pt_img, None))[0] - self.assertEqual(pt_result.size, (5, 5)) - - ox_func = TestSameTransfoms.ox_trans["CropToBoundingBox"](**args) - ox_result = ox_func((TestSameTransfoms.img, None))[0] - self.assertEqual(ox_result.shape, (5, 5, 3)) - - mx_func = TestSameTransfoms.mx_trans["CropToBoundingBox"](**args) - mx_result = mx_func((TestSameTransfoms.mx_img, None))[0] - self.assertEqual(mx_result.shape, (5, 5, 3)) - - tf_func = TestSameTransfoms.tf_trans["CropToBoundingBox"](**args) - tf_result = tf_func((TestSameTransfoms.img, None))[0] - self.assertEqual(tf_result.shape, (5, 5, 3)) - tf_result = tf_func((TestSameTransfoms.tf_img, None))[0] - tf_result = tf_result.eval(session=tf.compat.v1.Session()) - self.assertEqual(tf_result.shape, (5, 5, 3)) - - def testNormalize(self): - args = {} - normalize = TestSameTransfoms.pt_trans["Normalize"](**args) - totensor = TestSameTransfoms.pt_trans["ToTensor"]() - pt_func = TestSameTransfoms.pt_trans["Compose"]([totensor, normalize]) - pt_result = pt_func((TestSameTransfoms.pt_img, None))[0] - self.assertEqual(TestSameTransfoms.img.astype(np.uint8)[0][0][0] / 255.0, pt_result[0][0][0]) - args = {"std": [0.0]} - with self.assertRaises(ValueError): - TestSameTransfoms.pt_trans["Normalize"](**args) - - def testRescale(self): - ox_func = TestSameTransfoms.ox_trans["Rescale"]() - ox_result = ox_func((TestSameTransfoms.img, None))[0] - self.assertAlmostEqual(ox_result[1][2][0], TestSameTransfoms.img[1][2][0] / 255.0) - - def testTranspose(self): - args = {"perm": [2, 0, 1]} - tf_func = TestSameTransfoms.tf_trans["Transpose"](**args) - tf_result = tf_func((TestSameTransfoms.img, None))[0] - ox_func = TestSameTransfoms.ox_trans["Transpose"](**args) - ox_result = ox_func((TestSameTransfoms.img, None))[0] - mx_func = TestSameTransfoms.mx_trans["Transpose"](**args) - mx_result = mx_func((TestSameTransfoms.mx_img, None))[0] - pt_transpose = TestSameTransfoms.pt_trans["Transpose"](**args) - pt_totensor = TestSameTransfoms.pt_trans["ToTensor"]() - pt_compose = TestSameTransfoms.pt_trans["Compose"]([pt_totensor, pt_transpose]) - pt_result = pt_compose((TestSameTransfoms.pt_img, None))[0] - - self.assertEqual(tf_result.shape, (3, 10, 10)) - self.assertEqual(ox_result.shape, (3, 10, 10)) - self.assertEqual(mx_result.shape, (3, 10, 10)) - self.assertEqual(pt_result.shape, (10, 3, 10)) - - tf_result = tf_func((TestSameTransfoms.tf_img, None))[0] - tf_result = tf_result.eval(session=tf.compat.v1.Session()) - self.assertEqual(tf_result.shape, (3, 10, 10)) - - def testCenterCrop(self): - args = {"size": [4, 4]} - tf_func = TestSameTransfoms.tf_trans["CenterCrop"](**args) - tf_result = tf_func((TestSameTransfoms.img, None))[0] - pt_func = TestSameTransfoms.pt_trans["CenterCrop"](**args) - pt_result = pt_func((TestSameTransfoms.pt_img, None))[0] - mx_func = TestSameTransfoms.mx_trans["CenterCrop"](**args) - mx_result = mx_func((TestSameTransfoms.mx_img, None))[0] - self.assertEqual(tf_result.shape, (4, 4, 3)) - self.assertEqual(pt_result.size, (4, 4)) - self.assertEqual(mx_result.shape, (4, 4, 3)) - self.assertEqual(np.array(pt_result)[0][0][0], mx_result.asnumpy()[0][0][0]) - self.assertEqual(np.array(pt_result)[0][0][0], int(tf_result[0][0][0])) - - tf_result = tf_func((TestSameTransfoms.tf_img, None))[0] - tf_result = tf_result.eval(session=tf.compat.v1.Session()) - self.assertEqual(tf_result.shape, (4, 4, 3)) - - tf_result = tf_func((tf.constant(TestSameTransfoms.img.reshape((1, 10, 10, 3))), None))[0] - tf_result = tf_result.eval(session=tf.compat.v1.Session()) - self.assertEqual(tf_result.shape, (1, 4, 4, 3)) - - args = {"size": 4} - tf_func = TestSameTransfoms.tf_trans["CenterCrop"](**args) - tf_result = tf_func((TestSameTransfoms.img, None))[0] - pt_func = TestSameTransfoms.pt_trans["CenterCrop"](**args) - pt_result = pt_func((TestSameTransfoms.pt_img, None))[0] - mx_func = TestSameTransfoms.mx_trans["CenterCrop"](**args) - mx_result = mx_func((TestSameTransfoms.mx_img, None))[0] - self.assertEqual(tf_result.shape, (4, 4, 3)) - self.assertEqual(pt_result.size, (4, 4)) - self.assertEqual(mx_result.shape, (4, 4, 3)) - self.assertEqual(np.array(pt_result)[0][0][0], mx_result.asnumpy()[0][0][0]) - self.assertEqual(np.array(pt_result)[0][0][0], int(tf_result[0][0][0])) - - args = {"size": [4]} - tf_func = TestSameTransfoms.tf_trans["CenterCrop"](**args) - tf_result = tf_func((TestSameTransfoms.img, None))[0] - self.assertEqual(tf_result.shape, (4, 4, 3)) - - with self.assertRaises(ValueError): - tf_func = TestSameTransfoms.tf_trans["CenterCrop"](**args) - tf_result = tf_func((np.array([[TestSameTransfoms.img]]), None)) - with self.assertRaises(ValueError): - tf_func = TestSameTransfoms.tf_trans["CenterCrop"](**args) - tf_result = tf_func((tf.constant(TestSameTransfoms.img.reshape((1, 1, 10, 10, 3))), None)) - - args = {"size": [20]} - with self.assertRaises(ValueError): - tf_func = TestSameTransfoms.tf_trans["CenterCrop"](**args) - tf_result = tf_func((TestSameTransfoms.img, None)) - with self.assertRaises(ValueError): - tf_func = TestSameTransfoms.tf_trans["CenterCrop"](**args) - tf_result = tf_func((TestSameTransfoms.tf_img, None)) - - def testResizeWithRatio(self): - args = {"padding": True} - label = [[0.1, 0.1, 0.5, 0.5], [], [], []] - tf_func = TestSameTransfoms.tf_trans["ResizeWithRatio"](**args) - tf_result = tf_func((TestSameTransfoms.img, label))[0] - self.assertEqual(tf_result.shape, (1365, 1365, 3)) - - args = {"padding": False} - tf_func = TestSameTransfoms.tf_trans["ResizeWithRatio"](**args) - tf_result = tf_func((TestSameTransfoms.img, label))[0] - self.assertTrue((tf_result.shape[0] == 800 or tf_result.shape[1] == 1365)) - - def testResize(self): - tf_func = TestSameTransfoms.tf_trans["Resize"](**{"size": [4, 5]}) - tf_result = tf_func((TestSameTransfoms.img, None))[0] - pt_func = TestSameTransfoms.pt_trans["Resize"](**{"size": [4, 5]}) - pt_result = pt_func((TestSameTransfoms.pt_img, None))[0] - mx_func = TestSameTransfoms.mx_trans["Resize"](**{"size": [4, 5]}) - mx_result = mx_func((TestSameTransfoms.mx_img, None))[0] - self.assertEqual(tf_result.shape, (5, 4, 3)) - self.assertEqual(pt_result.size, (5, 4)) - self.assertEqual(mx_result.shape, (4, 5, 3)) - - tf_result = tf_func((TestSameTransfoms.tf_img, None))[0] - tf_result = tf_result.eval(session=tf.compat.v1.Session()) - self.assertEqual(tf_result.shape, (4, 5, 3)) - - args = {"size": 4} - tf_func = TestSameTransfoms.tf_trans["Resize"](**args) - tf_result = tf_func((TestSameTransfoms.img, None))[0] - pt_func = TestSameTransfoms.pt_trans["Resize"](**args) - pt_result = pt_func((TestSameTransfoms.pt_img, None))[0] - mx_func = TestSameTransfoms.mx_trans["Resize"](**args) - mx_result = mx_func((TestSameTransfoms.mx_img, None))[0] - self.assertEqual(tf_result.shape, (4, 4, 3)) - self.assertEqual(pt_result.size, (4, 4)) - self.assertEqual(mx_result.shape, (4, 4, 3)) - - args = {"size": [4]} - tf_func = TestSameTransfoms.tf_trans["Resize"](**args) - tf_result = tf_func((TestSameTransfoms.img, None))[0] - mx_func = TestSameTransfoms.mx_trans["Resize"](**args) - mx_result = mx_func((TestSameTransfoms.mx_img, None))[0] - self.assertEqual(tf_result.shape, (4, 4, 3)) - self.assertEqual(mx_result.shape, (4, 4, 3)) - - args = {"size": 4, "interpolation": "test"} - with self.assertRaises(ValueError): - TestSameTransfoms.tf_trans["Resize"](**args) - with self.assertRaises(ValueError): - TestSameTransfoms.pt_trans["Resize"](**args) - with self.assertRaises(ValueError): - TestSameTransfoms.mx_trans["Resize"](**args) - - def testRandomResizedCrop(self): - tf_func = TestSameTransfoms.tf_trans["RandomResizedCrop"](**{"size": [4, 5]}) - tf_result = tf_func((TestSameTransfoms.img, None))[0] - pt_func = TestSameTransfoms.pt_trans["RandomResizedCrop"](**{"size": [4, 5]}) - pt_result = pt_func((TestSameTransfoms.pt_img, None))[0] - mx_func = TestSameTransfoms.mx_trans["RandomResizedCrop"](**{"size": [4, 5]}) - mx_result = mx_func((TestSameTransfoms.mx_img, None))[0] - self.assertEqual(tf_result.shape, (5, 4, 3)) - self.assertEqual(pt_result.size, (5, 4)) - self.assertEqual(mx_result.shape, (4, 5, 3)) - - tf_result = tf_func((TestSameTransfoms.tf_img, None))[0] - tf_result = tf_result.eval(session=tf.compat.v1.Session()) - self.assertEqual(tf_result.shape, (4, 5, 3)) - - args = {"size": [4]} - tf_func = TestSameTransfoms.tf_trans["RandomResizedCrop"](**args) - tf_result = tf_func((TestSameTransfoms.img, None))[0] - self.assertEqual(tf_result.shape, (4, 4, 3)) - mx_func = TestSameTransfoms.mx_trans["RandomResizedCrop"](**args) - mx_result = mx_func((TestSameTransfoms.mx_img, None))[0] - self.assertEqual(mx_result.shape, (4, 4, 3)) - - args = {"size": 4} - tf_func = TestSameTransfoms.tf_trans["RandomResizedCrop"](**args) - tf_result = tf_func((TestSameTransfoms.img, None))[0] - pt_func = TestSameTransfoms.pt_trans["RandomResizedCrop"](**args) - pt_result = pt_func((TestSameTransfoms.pt_img, None))[0] - mx_func = TestSameTransfoms.mx_trans["RandomResizedCrop"](**args) - mx_result = mx_func((TestSameTransfoms.mx_img, None))[0] - self.assertEqual(tf_result.shape, (4, 4, 3)) - self.assertEqual(pt_result.size, (4, 4)) - self.assertEqual(mx_result.shape, (4, 4, 3)) - - args = {"size": 4, "scale": (0.8, 0.2)} - with self.assertRaises(ValueError): - TestSameTransfoms.tf_trans["RandomResizedCrop"](**args) - with self.assertRaises(ValueError): - TestSameTransfoms.pt_trans["RandomResizedCrop"](**args) - with self.assertRaises(ValueError): - TestSameTransfoms.mx_trans["RandomResizedCrop"](**args) - - args = {"size": 4, "interpolation": "test"} - with self.assertRaises(ValueError): - TestSameTransfoms.tf_trans["RandomResizedCrop"](**args) - with self.assertRaises(ValueError): - TestSameTransfoms.pt_trans["RandomResizedCrop"](**args) - with self.assertRaises(ValueError): - TestSameTransfoms.mx_trans["RandomResizedCrop"](**args) - - def testCropResize(self): - args = {"x": 0, "y": 0, "width": 10, "height": 10, "size": [5, 5]} - tf_func = TestSameTransfoms.tf_trans["CropResize"](**args) - tf_result = tf_func((TestSameTransfoms.img, None))[0] - mx_func = TestSameTransfoms.mx_trans["CropResize"](**args) - mx_result = mx_func((TestSameTransfoms.mx_img, None))[0] - ox_func = TestSameTransfoms.ox_trans["CropResize"](**args) - ox_result = ox_func((TestSameTransfoms.img, None))[0] - pt_func = TestSameTransfoms.pt_trans["CropResize"](**args) - pt_result = pt_func((TestSameTransfoms.pt_img, None))[0] - self.assertEqual(tf_result.shape, (5, 5, 3)) - self.assertEqual(mx_result.shape, (5, 5, 3)) - self.assertEqual(ox_result.shape, (5, 5, 3)) - self.assertEqual(pt_result.size, (5, 5)) - - tf_result = tf_func((TestSameTransfoms.tf_img, None))[0] - tf_result = tf_result.eval(session=tf.compat.v1.Session()) - self.assertEqual(tf_result.shape, (5, 5, 3)) - - args = {"x": 0, "y": 0, "width": 10, "height": 10, "size": 5} - tf_func = TestSameTransfoms.tf_trans["CropResize"](**args) - tf_result = tf_func((TestSameTransfoms.img, None))[0] - mx_func = TestSameTransfoms.mx_trans["CropResize"](**args) - mx_result = mx_func((TestSameTransfoms.mx_img, None))[0] - ox_func = TestSameTransfoms.ox_trans["CropResize"](**args) - ox_result = ox_func((TestSameTransfoms.img, None))[0] - self.assertEqual(tf_result.shape, (5, 5, 3)) - self.assertEqual(mx_result.shape, (5, 5, 3)) - self.assertEqual(ox_result.shape, (5, 5, 3)) - - args = {"x": 0, "y": 0, "width": 10, "height": 10, "size": [5]} - tf_func = TestSameTransfoms.tf_trans["CropResize"](**args) - tf_result = tf_func((TestSameTransfoms.img, None))[0] - mx_func = TestSameTransfoms.mx_trans["CropResize"](**args) - mx_result = mx_func((TestSameTransfoms.mx_img, None))[0] - ox_func = TestSameTransfoms.ox_trans["CropResize"](**args) - ox_result = ox_func((TestSameTransfoms.img, None))[0] - self.assertEqual(tf_result.shape, (5, 5, 3)) - self.assertEqual(mx_result.shape, (5, 5, 3)) - self.assertEqual(ox_result.shape, (5, 5, 3)) - - args = {"x": 0, "y": 0, "width": 10, "height": 10, "size": [5, 5]} - tf_func = TestSameTransfoms.tf_trans["CropResize"](**args) - tf_result = tf_func((TestSameTransfoms.img, None))[0] - mx_func = TestSameTransfoms.mx_trans["CropResize"](**args) - mx_result = mx_func((TestSameTransfoms.mx_img, None))[0] - ox_func = TestSameTransfoms.ox_trans["CropResize"](**args) - ox_result = ox_func((TestSameTransfoms.img, None))[0] - self.assertEqual(tf_result.shape, (5, 5, 3)) - self.assertEqual(mx_result.shape, (5, 5, 3)) - self.assertEqual(ox_result.shape, (5, 5, 3)) - - args = {"x": 0, "y": 0, "width": 10, "height": 10, "size": 5, "interpolation": "test"} - with self.assertRaises(ValueError): - TestSameTransfoms.ox_trans["CropResize"](**args) - with self.assertRaises(ValueError): - TestSameTransfoms.mx_trans["CropResize"](**args) - with self.assertRaises(ValueError): - TestSameTransfoms.tf_trans["CropResize"](**args) - with self.assertRaises(ValueError): - TestSameTransfoms.pt_trans["CropResize"](**args) - - def testRandomHorizontalFlip(self): - tf_func = TestSameTransfoms.tf_trans["RandomHorizontalFlip"]() - tf_result = tf_func((TestSameTransfoms.img, None))[0] - ox_func = TestSameTransfoms.ox_trans["RandomHorizontalFlip"]() - ox_result = ox_func((TestSameTransfoms.img, None))[0] - pt_func = TestSameTransfoms.pt_trans["RandomHorizontalFlip"]() - pt_result = pt_func((TestSameTransfoms.pt_img, None))[0] - mx_func = TestSameTransfoms.mx_trans["RandomHorizontalFlip"]() - mx_result = mx_func((TestSameTransfoms.mx_img, None))[0] - self.assertTrue( - (np.array(TestSameTransfoms.pt_img) == np.array(pt_result)).all() - or (np.fliplr(np.array(TestSameTransfoms.pt_img)) == np.array(pt_result)).all() - ) - self.assertTrue( - (TestSameTransfoms.img == tf_result).all() or (np.fliplr(TestSameTransfoms.img) == tf_result).all() - ) - self.assertTrue( - (TestSameTransfoms.img == ox_result).all() or (np.fliplr(TestSameTransfoms.img) == ox_result).all() - ) - self.assertTrue( - (TestSameTransfoms.mx_img.asnumpy() == mx_result.asnumpy()).all() - or (np.fliplr(TestSameTransfoms.mx_img.asnumpy()) == mx_result.asnumpy()).all() - ) - - tf_result = tf_func((TestSameTransfoms.tf_img, None))[0] - tf_result = tf_result.eval(session=tf.compat.v1.Session()) - self.assertTrue( - (TestSameTransfoms.img == tf_result).all() or (np.fliplr(TestSameTransfoms.img) == tf_result).all() - ) - - def testRandomVerticalFlip(self): - tf_func = TestSameTransfoms.tf_trans["RandomVerticalFlip"]() - tf_result = tf_func((TestSameTransfoms.img, None))[0] - ox_func = TestSameTransfoms.ox_trans["RandomVerticalFlip"]() - ox_result = ox_func((TestSameTransfoms.img, None))[0] - pt_func = TestSameTransfoms.pt_trans["RandomVerticalFlip"]() - pt_result = pt_func((TestSameTransfoms.pt_img, None))[0] - mx_func = TestSameTransfoms.mx_trans["RandomVerticalFlip"]() - mx_result = mx_func((TestSameTransfoms.mx_img, None))[0] - self.assertTrue( - (np.array(TestSameTransfoms.pt_img) == np.array(pt_result)).all() - or (np.flipud(np.array(TestSameTransfoms.pt_img)) == np.array(pt_result)).all() - ) - self.assertTrue( - (TestSameTransfoms.img == tf_result).all() or (np.flipud(TestSameTransfoms.img) == tf_result).all() - ) - self.assertTrue( - (TestSameTransfoms.img == ox_result).all() or (np.flipud(TestSameTransfoms.img) == ox_result).all() - ) - self.assertTrue( - (TestSameTransfoms.mx_img.asnumpy() == mx_result.asnumpy()).all() - or (np.flipud(TestSameTransfoms.mx_img.asnumpy()) == mx_result.asnumpy()).all() - ) - - tf_result = tf_func((TestSameTransfoms.tf_img, None))[0] - tf_result = tf_result.eval(session=tf.compat.v1.Session()) - self.assertTrue( - (TestSameTransfoms.img == tf_result).all() or (np.flipud(TestSameTransfoms.img) == tf_result).all() - ) - - -class TestTFTransorm(unittest.TestCase): - @classmethod - def setUpClass(cls): - cls.img = np.ones([10, 10, 3]) - cls.tf_img = tf.constant(cls.img) - cls.transforms = TRANSFORMS("tensorflow", "preprocess") - cls.tf_img = tf.constant(cls.img) - - def testRandomCrop(self): - args = {"size": [50]} - transform = TestTFTransorm.transforms["RandomCrop"](**args) - self.assertRaises(ValueError, transform, (TestTFTransorm.img, None)) - self.assertRaises(ValueError, transform, (TestTFTransorm.tf_img, None)) - - args = {"size": [5, 5]} - transform = TestTFTransorm.transforms["RandomCrop"](**args) - img_result = transform((TestTFTransorm.img, None))[0] - self.assertEqual(img_result.shape, (5, 5, 3)) - tf_result = transform((tf.constant(TestTFTransorm.img.reshape((1, 10, 10, 3))), None))[0] - tf_result = tf_result.eval(session=tf.compat.v1.Session()) - self.assertEqual(tf_result.shape, (1, 5, 5, 3)) - - args = {"size": [10, 10]} - transform = TestTFTransorm.transforms["RandomCrop"](**args) - img_result = transform((TestTFTransorm.img, None))[0] - self.assertEqual(img_result.shape, (10, 10, 3)) - tf_result = transform((TestTFTransorm.tf_img, None))[0] - tf_result = tf_result.eval(session=tf.compat.v1.Session()) - self.assertEqual(tf_result.shape, (10, 10, 3)) - - def testPaddedCenterCrop(self): - args = {"size": [4, 4]} - tf_func = TestTFTransorm.transforms["PaddedCenterCrop"](**args) - tf_result = tf_func((TestTFTransorm.img, None))[0] - self.assertEqual(tf_result.shape, (10, 10, 3)) - - args = {"size": [4, 4], "crop_padding": 4} - tf_func = TestTFTransorm.transforms["PaddedCenterCrop"](**args) - tf_result = tf_func((TestTFTransorm.img, None))[0] - self.assertEqual(tf_result.shape, (5, 5, 3)) - - args = {"size": 4} - tf_func = TestTFTransorm.transforms["PaddedCenterCrop"](**args) - tf_result = tf_func((TestTFTransorm.img, None))[0] - self.assertEqual(tf_result.shape, (10, 10, 3)) - - args = {"size": 4, "crop_padding": 4} - tf_func = TestTFTransorm.transforms["PaddedCenterCrop"](**args) - tf_result = tf_func((TestTFTransorm.img, None))[0] - self.assertEqual(tf_result.shape, (5, 5, 3)) - - args = {"size": [4]} - tf_func = TestTFTransorm.transforms["PaddedCenterCrop"](**args) - tf_result = tf_func((TestTFTransorm.img, None))[0] - self.assertEqual(tf_result.shape, (10, 10, 3)) - - args = {"size": [4], "crop_padding": 4} - tf_func = TestTFTransorm.transforms["PaddedCenterCrop"](**args) - tf_result = tf_func((TestTFTransorm.img, None))[0] - self.assertEqual(tf_result.shape, (5, 5, 3)) - - args = {"size": [4, 5], "crop_padding": 4} - with self.assertRaises(ValueError): - tf_func = TestTFTransorm.transforms["PaddedCenterCrop"](**args) - tf_result = tf_func((TestTFTransorm.img, None)) - - def testRescale(self): - transform = TestTFTransorm.transforms["Rescale"]() - img_result = transform((TestTFTransorm.img, None))[0] - comp_result = np.array(TestTFTransorm.img) / 255.0 - self.assertAlmostEqual(img_result[0][0][0], comp_result[0][0][0], places=5) - - tf_result = transform((TestTFTransorm.tf_img, None))[0] - tf_result = tf_result.eval(session=tf.compat.v1.Session()) - self.assertAlmostEqual(tf_result[0][0][0], comp_result[0][0][0], places=5) - - def testNormalize(self): - args = {"mean": [0.0, 0.0, 0.0], "std": [0.2, 0.5, 0.1]} - normalize = TestTFTransorm.transforms["Normalize"](**args) - img_result = normalize((TestTFTransorm.img, None))[0] - comp_result = np.array(TestTFTransorm.img) / [0.2, 0.5, 0.1] - self.assertAlmostEqual(img_result[0][0][0], comp_result[0][0][0], places=5) - self.assertAlmostEqual(img_result[0][0][1], comp_result[0][0][1], places=5) - self.assertAlmostEqual(img_result[0][0][2], comp_result[0][0][2], places=5) - - tf_result = normalize((TestTFTransorm.tf_img, None))[0] - tf_result = tf_result.eval(session=tf.compat.v1.Session()) - self.assertAlmostEqual(tf_result[0][0][0], comp_result[0][0][0], places=5) - - args = {"mean": [0.0, 0.0, 0.0], "std": [0, 0, 0]} - with self.assertRaises(ValueError): - TestTFTransorm.transforms["Normalize"](**args) - - def testRandomResizedCrop(self): - args = {"size": [50]} - randomresizedcrop = TestTFTransorm.transforms["RandomResizedCrop"](**args) - compose = TestTFTransorm.transforms["Compose"]([randomresizedcrop]) - image_result = compose((TestTFTransorm.img, None))[0] - self.assertEqual(image_result.shape, (50, 50, 3)) - args = {"size": [100, 100]} - randomresizedcrop = TestTFTransorm.transforms["RandomResizedCrop"](**args) - compose = TestTFTransorm.transforms["Compose"]([randomresizedcrop]) - image_result = compose((TestTFTransorm.img, None))[0] - self.assertEqual(image_result.shape, (100, 100, 3)) - tf_result = randomresizedcrop((TestTFTransorm.tf_img, None))[0] - tf_result = tf_result.eval(session=tf.compat.v1.Session()) - self.assertEqual(tf_result.shape, (100, 100, 3)) - args = {"size": [100, 100], "scale": (0.8, 0.1)} - with self.assertRaises(ValueError): - TestTFTransorm.transforms["RandomResizedCrop"](**args) - - def testSquadV1(self): - import json - import ssl - import urllib - - ssl._create_default_https_context = ssl._create_unverified_context - - vocab_url = ( - "https://raw.githubusercontent.com/microsoft/SDNet/master/bert_vocab_files/bert-large-uncased-vocab.txt" - ) - urllib.request.urlretrieve(vocab_url, "./vocab.txt") - label = [ - { - "paragraphs": [ - { - "context": "Super Bowl 50 was an American football game to determine the champion of the National Football League (NFL) for the 2015 season.", - "qas": [ - { - "answers": [ - {"answer_start": 177, "text": "Denver Broncos"}, - {"answer_start": 177, "text": "Denver Broncos"}, - {"answer_start": 177, "text": "Denver Broncos"}, - ], - "question": "Which NFL team represented the AFC at Super Bowl 50?", - "id": "56be4db0acb8001400a502ec", - } - ], - } - ] - } - ] - fake_json = json.dumps({"data": label}) - with open("dev.json", "w") as f: - f.write(fake_json) - args = {"label_file": "./dev.json", "vocab_file": "./vocab.txt"} - post_transforms = TRANSFORMS("tensorflow", "postprocess") - squadv1 = post_transforms["SquadV1"](**args) - - preds_0 = np.array([1000000000]) - preds_1 = np.random.uniform(low=-12.3, high=6.8, size=(1, 384)) - preds_2 = np.random.uniform(low=-10.8, high=7.4, size=(1, 384)) - preds = [preds_0, preds_1, preds_2] - result = squadv1((preds, label)) - self.assertTrue(result[1][0]["paragraphs"][0]["qas"][0]["id"] in result[0]) - os.remove("dev.json") - os.remove("vocab.txt") - - -class TestAlignImageChannel(unittest.TestCase): - @classmethod - def setUpClass(cls): - cls.img1 = np.random.random_sample([100, 100, 3]) * 255 - cls.img2 = np.random.random_sample([100, 100]) * 255 - cls.img3 = np.random.random_sample([100, 100, 4]) * 255 - cls.pt_img1 = Image.fromarray(cls.img1.astype(np.uint8)) - cls.pt_img2 = Image.fromarray(cls.img2.astype(np.uint8)) - cls.pt_img3 = Image.fromarray(cls.img3.astype(np.uint8)) - - def testTensorflow(self): - transforms = TRANSFORMS("tensorflow", "preprocess") - align = transforms["AlignImageChannel"](**{"dim": 1}) - image, _ = align((TestAlignImageChannel.img1.astype(np.uint8), None)) - self.assertEqual(image.shape[-1], 1) - - align = transforms["AlignImageChannel"](**{"dim": 1}) - image, _ = align((TestAlignImageChannel.img2.astype(np.uint8), None)) - self.assertEqual(image.shape[-1], 1) - - align = transforms["AlignImageChannel"](**{"dim": 3}) - image, _ = align((TestAlignImageChannel.img3.astype(np.uint8), None)) - self.assertEqual(image.shape[-1], 3) - - align = transforms["AlignImageChannel"](**{"dim": 2}) - self.assertRaises(ValueError, align, (TestAlignImageChannel.img1.astype(np.uint8), None)) - - with self.assertRaises(ValueError): - transforms["AlignImageChannel"](**{"dim": 5}) - - def testONNX(self): - transforms = TRANSFORMS("onnxrt_qlinearops", "preprocess") - align = transforms["AlignImageChannel"](**{"dim": 1}) - image, _ = align((TestAlignImageChannel.img1.astype(np.uint8), None)) - self.assertEqual(image.shape[-1], 1) - - align = transforms["AlignImageChannel"](**{"dim": 1}) - image, _ = align((TestAlignImageChannel.img2.astype(np.uint8), None)) - self.assertEqual(image.shape[-1], 1) - - align = transforms["AlignImageChannel"](**{"dim": 3}) - image, _ = align((TestAlignImageChannel.img3.astype(np.uint8), None)) - self.assertEqual(image.shape[-1], 3) - - align = transforms["AlignImageChannel"](**{"dim": 2}) - self.assertRaises(ValueError, align, (TestAlignImageChannel.img1.astype(np.uint8), None)) - - with self.assertRaises(ValueError): - transforms["AlignImageChannel"](**{"dim": 5}) - - def testPyTorch(self): - transforms = TRANSFORMS("pytorch", "preprocess") - align = transforms["AlignImageChannel"](**{"dim": 1}) - image, _ = align((TestAlignImageChannel.pt_img1, None)) - self.assertEqual(image.mode, "L") - - align = transforms["AlignImageChannel"](**{"dim": 1}) - image, _ = align((TestAlignImageChannel.pt_img2, None)) - self.assertEqual(image.mode, "L") - - align = transforms["AlignImageChannel"](**{"dim": 3}) - image, _ = align((TestAlignImageChannel.pt_img3, None)) - self.assertEqual(image.mode, "RGB") - - with self.assertRaises(ValueError): - align = transforms["AlignImageChannel"](**{"dim": 2}) - - with self.assertRaises(ValueError): - transforms["AlignImageChannel"](**{"dim": 5}) - - @unittest.skipIf(platform.system().lower() == "windows", "not support mxnet on windows yet") - def testMXNet(self): - transforms = TRANSFORMS("mxnet", "preprocess") - align = transforms["AlignImageChannel"](**{"dim": 1}) - image, _ = align((TestAlignImageChannel.img1.astype(np.uint8), None)) - self.assertEqual(image.shape[-1], 1) - - align = transforms["AlignImageChannel"](**{"dim": 1}) - image, _ = align((TestAlignImageChannel.img2.astype(np.uint8), None)) - self.assertEqual(image.shape[-1], 1) - - align = transforms["AlignImageChannel"](**{"dim": 3}) - image, _ = align((TestAlignImageChannel.img3.astype(np.uint8), None)) - self.assertEqual(image.shape[-1], 3) - - align = transforms["AlignImageChannel"](**{"dim": 2}) - self.assertRaises(ValueError, align, (TestAlignImageChannel.img1.astype(np.uint8), None)) - - with self.assertRaises(ValueError): - transforms["AlignImageChannel"](**{"dim": 5}) - - -class TestToArray(unittest.TestCase): - @unittest.skipIf(platform.system().lower() == "windows", "not support mxnet on windows yet") - def testParse(self): - random_array = np.random.random_sample([10, 10, 3]) * 255 - random_array = random_array.astype(np.uint8) - img1 = Image.fromarray(random_array) - onnx_transforms = TRANSFORMS("onnxrt_qlinearops", "preprocess") - onnx_parse = onnx_transforms["ToArray"]() - img, _ = onnx_parse((img1, None)) - self.assertTrue(isinstance(img, np.ndarray)) - - mxnet_transforms = TRANSFORMS("mxnet", "preprocess") - mxnet_parse = mxnet_transforms["ToArray"]() - img, _ = mxnet_parse((mx.nd.array(random_array), None)) - self.assertTrue(isinstance(img, np.ndarray)) - self.assertRaises(ValueError, mxnet_parse, ([1, 2], None)) - - -class TestMXNetTransform(unittest.TestCase): - @classmethod - def setUpClass(cls): - if platform.system().lower() == "windows": - cls.skipTest(cls, "not support mxnet on windows yet") - array = np.random.random_sample([100, 100, 3]) * 255 - cls.img = mx.nd.array(array) - cls.transforms = TRANSFORMS("mxnet", "preprocess") - - def testRandomCrop(self): - args = {"size": [50]} - randomcrop = TestMXNetTransform.transforms["RandomCrop"](**args) - compose = TestMXNetTransform.transforms["Compose"]([randomcrop]) - image_result = compose((TestMXNetTransform.img, None)) - self.assertEqual(image_result[0].shape, (50, 50, 3)) - - def testNormalize(self): - args = {"mean": [0.0, 0.0, 0.0], "std": [0.29, 0.24, 0.25]} - normalize = TestMXNetTransform.transforms["Normalize"](**args) - image_result = normalize((TestMXNetTransform.img, None)) - self.assertAlmostEqual( - image_result[0].asnumpy()[0][0][0], (TestMXNetTransform.img.asnumpy() / [0.29])[0][0][0], places=3 - ) - - -class TestONNXTransfrom(unittest.TestCase): - @classmethod - def setUpClass(cls): - cls.img = np.random.random_sample([100, 100, 3]) * 255 - cls.transforms = TRANSFORMS("onnxrt_qlinearops", "preprocess") - - def testResize(self): - args = {"size": [224]} - resize = TestONNXTransfrom.transforms["Resize"](**args) - compose = TestONNXTransfrom.transforms["Compose"]([resize]) - image_result = compose((self.img, None)) - self.assertEqual(image_result[0].shape, (224, 224, 3)) - args = {"size": [100, 100], "interpolation": "test"} - with self.assertRaises(ValueError): - TestONNXTransfrom.transforms["Resize"](**args) - - args = {"size": 224} - resize = TestONNXTransfrom.transforms["Resize"](**args) - compose = TestONNXTransfrom.transforms["Compose"]([resize]) - image_result = compose((self.img, None)) - self.assertEqual(image_result[0].shape, (224, 224, 3)) - - args = {"size": [224, 224]} - resize = TestONNXTransfrom.transforms["Resize"](**args) - compose = TestONNXTransfrom.transforms["Compose"]([resize]) - image_result = compose((self.img, None)) - self.assertEqual(image_result[0].shape, (224, 224, 3)) - - def testNormalize(self): - args = {"mean": [0.0, 0.0, 0.0], "std": [0.29, 0.24, 0.25]} - normalize = TestONNXTransfrom.transforms["Normalize"](**args) - compose = TestONNXTransfrom.transforms["Compose"]([normalize]) - image_result = compose((TestONNXTransfrom.img, None)) - self.assertTrue((image_result[0] == np.array(TestONNXTransfrom.img) / [0.29, 0.24, 0.25]).all()) - - args = {"mean": [0.0, 0.0, 0.0], "std": [0, 0, 0]} - with self.assertRaises(ValueError): - TestONNXTransfrom.transforms["Normalize"](**args) - - def testRandomCrop(self): - args = {"size": [50]} - randomcrop = TestONNXTransfrom.transforms["RandomCrop"](**args) - compose = TestONNXTransfrom.transforms["Compose"]([randomcrop]) - image_result = compose((TestONNXTransfrom.img, None)) - self.assertEqual(image_result[0].shape, (50, 50, 3)) - args = {"size": [1000, 1000]} - with self.assertRaises(ValueError): - trans = TestONNXTransfrom.transforms["RandomCrop"](**args) - trans((TestONNXTransfrom.img, None)) - - args = {"size": 50} - randomcrop = TestONNXTransfrom.transforms["RandomCrop"](**args) - compose = TestONNXTransfrom.transforms["Compose"]([randomcrop]) - image_result = compose((TestONNXTransfrom.img, None)) - self.assertEqual(image_result[0].shape, (50, 50, 3)) - - args = {"size": [100, 100]} - randomcrop = TestONNXTransfrom.transforms["RandomCrop"](**args) - compose = TestONNXTransfrom.transforms["Compose"]([randomcrop]) - image_result = compose((TestONNXTransfrom.img, None)) - self.assertEqual(image_result[0].shape, (100, 100, 3)) - - def testCenterCrop(self): - args = {"size": [100]} - centercrop = TestONNXTransfrom.transforms["CenterCrop"](**args) - compose = TestONNXTransfrom.transforms["Compose"]([centercrop]) - image_result = compose((TestONNXTransfrom.img, None)) - self.assertEqual(image_result[0].shape, (100, 100, 3)) - args = {"size": 5} - centercrop = TestONNXTransfrom.transforms["CenterCrop"](**args) - image_result = centercrop((TestONNXTransfrom.img, None)) - self.assertEqual(image_result[0].shape, (5, 5, 3)) - args = {"size": [5, 6]} - centercrop = TestONNXTransfrom.transforms["CenterCrop"](**args) - image_result = centercrop((TestONNXTransfrom.img, None)) - self.assertEqual(image_result[0].shape, (5, 6, 3)) - args = {"size": [150]} - centercrop = TestONNXTransfrom.transforms["CenterCrop"](**args) - with self.assertRaises(ValueError): - centercrop((TestONNXTransfrom.img, None)) - - def testRandomResizedCrop(self): - args = {"size": [150]} - randomresizedcrop = TestONNXTransfrom.transforms["RandomResizedCrop"](**args) - compose = TestONNXTransfrom.transforms["Compose"]([randomresizedcrop]) - image_result = compose((TestONNXTransfrom.img, None)) - self.assertEqual(image_result[0].shape, (150, 150, 3)) - args = {"size": [150, 150], "scale": (0.9, 0.3)} - with self.assertRaises(ValueError): - TestONNXTransfrom.transforms["RandomResizedCrop"](**args) - - args = {"size": 150, "interpolation": "test"} - with self.assertRaises(ValueError): - TestONNXTransfrom.transforms["RandomResizedCrop"](**args) - - -class TestImagenetTransform(unittest.TestCase): - def testParseDecodeImagenet(self): - random_array = np.random.random_sample([100, 100, 3]) * 255 - random_array = random_array.astype(np.uint8) - im = Image.fromarray(random_array) - im.save("test.jpeg") - - image = tf.compat.v1.gfile.FastGFile("test.jpeg", "rb").read() - label = 10 - example = tf.train.Example( - features=tf.train.Features( - feature={ - "image/encoded": tf.train.Feature(bytes_list=tf.train.BytesList(value=[image])), - "image/class/label": tf.train.Feature(int64_list=tf.train.Int64List(value=[label])), - "image/object/bbox/xmin": tf.train.Feature(float_list=tf.train.FloatList(value=[10])), - "image/object/bbox/ymin": tf.train.Feature(float_list=tf.train.FloatList(value=[20])), - "image/object/bbox/xmax": tf.train.Feature(float_list=tf.train.FloatList(value=[100])), - "image/object/bbox/ymax": tf.train.Feature(float_list=tf.train.FloatList(value=[200])), - } - ) - ) - with tf.io.TFRecordWriter("test-0-of-0") as writer: - writer.write(example.SerializeToString()) - eval_dataset = create_dataset("tensorflow", {"ImageRecord": {"root": "./"}}, {"ParseDecodeImagenet": {}}, None) - dataloader = DATALOADERS["tensorflow"](dataset=eval_dataset, batch_size=1) - for inputs, labels in dataloader: - self.assertEqual(inputs.shape, (1, 100, 100, 3)) - self.assertEqual(labels[0][0], 10) - break - - from neural_compressor.experimental.data.transforms.imagenet_transform import ParseDecodeImagenet - - func = ParseDecodeImagenet() - out = func(example.SerializeToString()) - self.assertEqual(out[0].eval(session=tf.compat.v1.Session()).shape, (100, 100, 3)) - - from neural_compressor.experimental.data.datasets.dataset import TensorflowTFRecordDataset - - ds = TensorflowTFRecordDataset("test-0-of-0", func) - dataloader = DATALOADERS["tensorflow"](dataset=ds, batch_size=1) - for inputs, labels in dataloader: - self.assertEqual(inputs.shape, (1, 100, 100, 3)) - self.assertEqual(labels[0][0], 10) - break - - os.remove("test-0-of-0") - os.remove("test.jpeg") - - -class TestCOCOTransform(unittest.TestCase): - def testCOCODecode(self): - tf.compat.v1.disable_eager_execution() - - random_array = np.random.random_sample([100, 100, 3]) * 255 - random_array = random_array.astype(np.uint8) - im = Image.fromarray(random_array) - im.save("test.jpeg") - - image = tf.compat.v1.gfile.FastGFile("test.jpeg", "rb").read() - source_id = "000000397133.jpg".encode("utf-8") - label = "person".encode("utf-8") - example = tf.train.Example( - features=tf.train.Features( - feature={ - "image/encoded": tf.train.Feature(bytes_list=tf.train.BytesList(value=[image])), - "image/object/class/text": tf.train.Feature(bytes_list=tf.train.BytesList(value=[label])), - "image/source_id": tf.train.Feature(bytes_list=tf.train.BytesList(value=[source_id])), - "image/object/bbox/xmin": tf.train.Feature(float_list=tf.train.FloatList(value=[10])), - "image/object/bbox/ymin": tf.train.Feature(float_list=tf.train.FloatList(value=[10])), - "image/object/bbox/xmax": tf.train.Feature(float_list=tf.train.FloatList(value=[100])), - "image/object/bbox/ymax": tf.train.Feature(float_list=tf.train.FloatList(value=[100])), - } - ) - ) - - with tf.io.TFRecordWriter("test.record") as writer: - writer.write(example.SerializeToString()) - eval_dataset = create_dataset( - "tensorflow", - {"COCORecord": {"root": "test.record"}}, - { - "ParseDecodeCoco": {}, - "Resize": {"size": 50}, - "Cast": {"dtype": "int64"}, - "CropToBoundingBox": {"offset_height": 2, "offset_width": 2, "target_height": 5, "target_width": 5}, - "CenterCrop": {"size": [4, 4]}, - "RandomResizedCrop": {"size": [4, 5]}, - }, - None, - ) - dataloader = DATALOADERS["tensorflow"](dataset=eval_dataset, batch_size=1) - for inputs, labels in dataloader: - self.assertEqual(inputs.shape, (1, 4, 5, 3)) - self.assertEqual(labels[0].shape, (1, 1, 4)) - - from neural_compressor.experimental.data.datasets.coco_dataset import ParseDecodeCoco - from neural_compressor.experimental.data.transforms.transform import TensorflowResizeWithRatio - - func = ParseDecodeCoco() - out = func(example.SerializeToString()) - self.assertEqual(out[0].eval(session=tf.compat.v1.Session()).shape, (100, 100, 3)) - - func = ParseDecodeCoco() - out = func(example.SerializeToString()) - self.assertEqual(out[0].eval(session=tf.compat.v1.Session()).shape, (100, 100, 3)) - - func = TensorflowResizeWithRatio(**{"padding": True}) - out = func(out) - self.assertEqual(out[0].eval(session=tf.compat.v1.Session()).shape, (1365, 1365, 3)) - - example = tf.train.Example( - features=tf.train.Features( - feature={ - "image/encoded": tf.train.Feature(bytes_list=tf.train.BytesList(value=[image])), - "image/source_id": tf.train.Feature(bytes_list=tf.train.BytesList(value=[source_id])), - "image/object/bbox/xmin": tf.train.Feature(float_list=tf.train.FloatList(value=[10])), - "image/object/bbox/ymin": tf.train.Feature(float_list=tf.train.FloatList(value=[10])), - "image/object/bbox/xmax": tf.train.Feature(float_list=tf.train.FloatList(value=[100])), - "image/object/bbox/ymax": tf.train.Feature(float_list=tf.train.FloatList(value=[100])), - } - ) - ) - - with tf.io.TFRecordWriter("test2.record") as writer: - writer.write(example.SerializeToString()) - self.assertRaises( - ValueError, create_dataset, "tensorflow", {"COCORecord": {"root": "test2.record"}}, None, None - ) - - os.remove("test2.record") - os.remove("test.record") - os.remove("test.jpeg") - - -class TestVOCTransform(unittest.TestCase): - def testVOCDecode(self): - import shutil - - tf.compat.v1.disable_eager_execution() - - def _bytes_list_feature(values): - import six - - def norm2bytes(value): - return value.encode() if isinstance(value, str) and six.PY3 else value - - return tf.train.Feature(bytes_list=tf.train.BytesList(value=[norm2bytes(values)])) - - def _int64_list_feature(values): - import collections - import collections.abc - - if not isinstance(values, collections.abc.Iterable): - values = [values] - return tf.train.Feature(int64_list=tf.train.Int64List(value=values)) - - random_array = np.random.random_sample([100, 100, 3]) * 255 - random_array = random_array.astype(np.uint8) - im = Image.fromarray(random_array) - im.save("test.jpg") - random_array = np.random.random_sample([100, 100, 3]) * 0 - random_array = random_array.astype(np.uint8) - im = Image.fromarray(random_array) - im.save("test.png") - image_data = tf.compat.v1.gfile.GFile("test.jpg", "rb").read() - seg_data = tf.compat.v1.gfile.GFile("test.png", "rb").read() - filename = "test" - - example = tf.train.Example( - features=tf.train.Features( - feature={ - "image/encoded": _bytes_list_feature(image_data), - "image/filename": _bytes_list_feature(filename), - "image/format": _bytes_list_feature("png"), - "image/height": _int64_list_feature(100), - "image/width": _int64_list_feature(100), - "image/channels": _int64_list_feature(3), - "image/segmentation/class/encoded": (_bytes_list_feature(seg_data)), - "image/segmentation/class/format": _bytes_list_feature("png"), - } - ) - ) - - if not os.path.exists("./test_record"): - os.mkdir("./test_record") - with tf.io.TFRecordWriter("./test_record/val-test.record") as writer: - writer.write(example.SerializeToString()) - eval_dataset = create_dataset( - "tensorflow", {"VOCRecord": {"root": "./test_record"}}, {"ParseDecodeVoc": {}}, None - ) - dataloader = DATALOADERS["tensorflow"](dataset=eval_dataset, batch_size=1) - for inputs, labels in dataloader: - self.assertEqual(inputs.shape, (1, 100, 100, 3)) - self.assertEqual(labels[0].shape, (100, 100, 1)) - - from neural_compressor.experimental.data.transforms.transform import ParseDecodeVocTransform - - func = ParseDecodeVocTransform() - out = func(example.SerializeToString()) - self.assertEqual(out[0].eval(session=tf.compat.v1.Session()).shape, (100, 100, 3)) - - os.remove("./test_record/val-test.record") - os.remove("test.jpg") - os.remove("test.png") - shutil.rmtree("./test_record") - - -if __name__ == "__main__": - unittest.main() diff --git a/test/data/test_tokenization.py b/test/data/test_tokenization.py deleted file mode 100644 index 0a8e1018cdc..00000000000 --- a/test/data/test_tokenization.py +++ /dev/null @@ -1,43 +0,0 @@ -import os -import shutil -import unittest - -from neural_compressor.experimental.data.transforms.tokenization import FullTokenizer -from neural_compressor.utils.utility import LazyImport - -tf = LazyImport("tensorflow") - -basic_text = ["un", "##aff", "##able"] - - -class TestFullTokenizer(unittest.TestCase): - @classmethod - def setUpClass(cls): - os.makedirs("val", exist_ok=True) - vocab_file = "val/temp.txt" - with tf.io.gfile.GFile(vocab_file, "w+") as f: - for vocab in basic_text: - f.write(vocab + "\n") - f.close() - - @classmethod - def tearDownClass(cls): - if os.path.exists("val"): - shutil.rmtree("val") - - def test_tokenizer(self): - tokenizer = FullTokenizer("val/temp.txt") - ids = [2, 1, 0] - tokens = basic_text[::-1] - tokens_to_ids = tokenizer.convert_tokens_to_ids(tokens) - self.assertEqual(tokens_to_ids, ids) - ids_to_tokens = tokenizer.convert_ids_to_tokens(ids) - self.assertEqual(ids_to_tokens, tokens) - split_tokens = tokenizer.tokenize("unaffable") - self.assertEqual(split_tokens, basic_text) - split_tokens = tokenizer.tokenize("example") - self.assertEqual(split_tokens, ["[UNK]"]) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/data/test_transform.py b/test/data/test_transform.py index 9775187e9ae..02798127008 100644 --- a/test/data/test_transform.py +++ b/test/data/test_transform.py @@ -996,212 +996,5 @@ def testRandomResizedCrop(self): TestONNXTransfrom.transforms["RandomResizedCrop"](**args) -class TestImagenetTransform(unittest.TestCase): - def testParseDecodeImagenet(self): - random_array = np.random.random_sample([100, 100, 3]) * 255 - random_array = random_array.astype(np.uint8) - im = Image.fromarray(random_array) - im.save("test.jpeg") - - image = tf.compat.v1.gfile.FastGFile("test.jpeg", "rb").read() - label = 10 - example = tf.train.Example( - features=tf.train.Features( - feature={ - "image/encoded": tf.train.Feature(bytes_list=tf.train.BytesList(value=[image])), - "image/class/label": tf.train.Feature(int64_list=tf.train.Int64List(value=[label])), - "image/object/bbox/xmin": tf.train.Feature(float_list=tf.train.FloatList(value=[10])), - "image/object/bbox/ymin": tf.train.Feature(float_list=tf.train.FloatList(value=[20])), - "image/object/bbox/xmax": tf.train.Feature(float_list=tf.train.FloatList(value=[100])), - "image/object/bbox/ymax": tf.train.Feature(float_list=tf.train.FloatList(value=[200])), - } - ) - ) - with tf.io.TFRecordWriter("test-0-of-0") as writer: - writer.write(example.SerializeToString()) - eval_dataset = create_dataset("tensorflow", {"ImageRecord": {"root": "./"}}, {"ParseDecodeImagenet": {}}, None) - dataloader = DATALOADERS["tensorflow"](dataset=eval_dataset, batch_size=1) - for inputs, labels in dataloader: - self.assertEqual(inputs.shape, (1, 100, 100, 3)) - self.assertEqual(labels[0][0], 10) - break - - from neural_compressor.experimental.data.transforms.imagenet_transform import ParseDecodeImagenet - - func = ParseDecodeImagenet() - out = func(example.SerializeToString()) - self.assertEqual(out[0].eval(session=tf.compat.v1.Session()).shape, (100, 100, 3)) - - from neural_compressor.experimental.data.datasets.dataset import TensorflowTFRecordDataset - - ds = TensorflowTFRecordDataset("test-0-of-0", func) - dataloader = DATALOADERS["tensorflow"](dataset=ds, batch_size=1) - for inputs, labels in dataloader: - self.assertEqual(inputs.shape, (1, 100, 100, 3)) - self.assertEqual(labels[0][0], 10) - break - - os.remove("test-0-of-0") - os.remove("test.jpeg") - - -class TestCOCOTransform(unittest.TestCase): - def testCOCODecode(self): - tf.compat.v1.disable_eager_execution() - - random_array = np.random.random_sample([100, 100, 3]) * 255 - random_array = random_array.astype(np.uint8) - im = Image.fromarray(random_array) - im.save("test.jpeg") - - image = tf.compat.v1.gfile.FastGFile("test.jpeg", "rb").read() - source_id = "000000397133.jpg".encode("utf-8") - label = "person".encode("utf-8") - example = tf.train.Example( - features=tf.train.Features( - feature={ - "image/encoded": tf.train.Feature(bytes_list=tf.train.BytesList(value=[image])), - "image/object/class/text": tf.train.Feature(bytes_list=tf.train.BytesList(value=[label])), - "image/source_id": tf.train.Feature(bytes_list=tf.train.BytesList(value=[source_id])), - "image/object/bbox/xmin": tf.train.Feature(float_list=tf.train.FloatList(value=[10])), - "image/object/bbox/ymin": tf.train.Feature(float_list=tf.train.FloatList(value=[10])), - "image/object/bbox/xmax": tf.train.Feature(float_list=tf.train.FloatList(value=[100])), - "image/object/bbox/ymax": tf.train.Feature(float_list=tf.train.FloatList(value=[100])), - } - ) - ) - - with tf.io.TFRecordWriter("test.record") as writer: - writer.write(example.SerializeToString()) - eval_dataset = create_dataset( - "tensorflow", - {"COCORecord": {"root": "test.record"}}, - { - "ParseDecodeCoco": {}, - "Resize": {"size": 50}, - "Cast": {"dtype": "int64"}, - "CropToBoundingBox": {"offset_height": 2, "offset_width": 2, "target_height": 5, "target_width": 5}, - "CenterCrop": {"size": [4, 4]}, - "RandomResizedCrop": {"size": [4, 5]}, - }, - None, - ) - dataloader = DATALOADERS["tensorflow"](dataset=eval_dataset, batch_size=1) - for inputs, labels in dataloader: - self.assertEqual(inputs.shape, (1, 4, 5, 3)) - self.assertEqual(labels[0].shape, (1, 1, 4)) - - from neural_compressor.experimental.data.datasets.coco_dataset import ParseDecodeCoco - from neural_compressor.experimental.data.transforms.transform import TensorflowResizeWithRatio - - func = ParseDecodeCoco() - out = func(example.SerializeToString()) - self.assertEqual(out[0].eval(session=tf.compat.v1.Session()).shape, (100, 100, 3)) - - func = ParseDecodeCoco() - out = func(example.SerializeToString()) - self.assertEqual(out[0].eval(session=tf.compat.v1.Session()).shape, (100, 100, 3)) - - func = TensorflowResizeWithRatio(**{"padding": True}) - out = func(out) - self.assertEqual(out[0].eval(session=tf.compat.v1.Session()).shape, (1365, 1365, 3)) - - example = tf.train.Example( - features=tf.train.Features( - feature={ - "image/encoded": tf.train.Feature(bytes_list=tf.train.BytesList(value=[image])), - "image/source_id": tf.train.Feature(bytes_list=tf.train.BytesList(value=[source_id])), - "image/object/bbox/xmin": tf.train.Feature(float_list=tf.train.FloatList(value=[10])), - "image/object/bbox/ymin": tf.train.Feature(float_list=tf.train.FloatList(value=[10])), - "image/object/bbox/xmax": tf.train.Feature(float_list=tf.train.FloatList(value=[100])), - "image/object/bbox/ymax": tf.train.Feature(float_list=tf.train.FloatList(value=[100])), - } - ) - ) - - with tf.io.TFRecordWriter("test2.record") as writer: - writer.write(example.SerializeToString()) - self.assertRaises( - ValueError, create_dataset, "tensorflow", {"COCORecord": {"root": "test2.record"}}, None, None - ) - - os.remove("test2.record") - os.remove("test.record") - os.remove("test.jpeg") - - -class TestVOCTransform(unittest.TestCase): - def testVOCDecode(self): - import shutil - - tf.compat.v1.disable_eager_execution() - - def _bytes_list_feature(values): - import six - - def norm2bytes(value): - return value.encode() if isinstance(value, str) and six.PY3 else value - - return tf.train.Feature(bytes_list=tf.train.BytesList(value=[norm2bytes(values)])) - - def _int64_list_feature(values): - import collections - import collections.abc - - if not isinstance(values, collections.abc.Iterable): - values = [values] - return tf.train.Feature(int64_list=tf.train.Int64List(value=values)) - - random_array = np.random.random_sample([100, 100, 3]) * 255 - random_array = random_array.astype(np.uint8) - im = Image.fromarray(random_array) - im.save("test.jpg") - random_array = np.random.random_sample([100, 100, 3]) * 0 - random_array = random_array.astype(np.uint8) - im = Image.fromarray(random_array) - im.save("test.png") - image_data = tf.compat.v1.gfile.GFile("test.jpg", "rb").read() - seg_data = tf.compat.v1.gfile.GFile("test.png", "rb").read() - filename = "test" - - example = tf.train.Example( - features=tf.train.Features( - feature={ - "image/encoded": _bytes_list_feature(image_data), - "image/filename": _bytes_list_feature(filename), - "image/format": _bytes_list_feature("png"), - "image/height": _int64_list_feature(100), - "image/width": _int64_list_feature(100), - "image/channels": _int64_list_feature(3), - "image/segmentation/class/encoded": (_bytes_list_feature(seg_data)), - "image/segmentation/class/format": _bytes_list_feature("png"), - } - ) - ) - - if not os.path.exists("./test_record"): - os.mkdir("./test_record") - with tf.io.TFRecordWriter("./test_record/val-test.record") as writer: - writer.write(example.SerializeToString()) - eval_dataset = create_dataset( - "tensorflow", {"VOCRecord": {"root": "./test_record"}}, {"ParseDecodeVoc": {}}, None - ) - dataloader = DATALOADERS["tensorflow"](dataset=eval_dataset, batch_size=1) - for inputs, labels in dataloader: - self.assertEqual(inputs.shape, (1, 100, 100, 3)) - self.assertEqual(labels[0].shape, (100, 100, 1)) - - from neural_compressor.experimental.data.transforms.transform import ParseDecodeVocTransform - - func = ParseDecodeVocTransform() - out = func(example.SerializeToString()) - self.assertEqual(out[0].eval(session=tf.compat.v1.Session()).shape, (100, 100, 3)) - - os.remove("./test_record/val-test.record") - os.remove("test.jpg") - os.remove("test.png") - shutil.rmtree("./test_record") - - if __name__ == "__main__": unittest.main() diff --git a/test/distillation/test_distillation_1.x.py b/test/distillation/test_distillation_1.x.py deleted file mode 100644 index 802f81148a5..00000000000 --- a/test/distillation/test_distillation_1.x.py +++ /dev/null @@ -1,266 +0,0 @@ -import copy -import os -import shutil -import unittest - -import tensorflow as tf -import torch -import torch.nn as nn -import torchvision - -from neural_compressor.adaptor.tf_utils.util import version1_lt_version2 -from neural_compressor.config import DistillationConfig, KnowledgeDistillationLossConfig -from neural_compressor.data import Datasets -from neural_compressor.experimental.data.dataloaders.pytorch_dataloader import PyTorchDataLoader - - -def build_fake_yaml(): - fake_yaml = """ - model: - name: imagenet_distillation - framework: pytorch - - distillation: - train: - start_epoch: 0 - end_epoch: 3 - iteration: 10 - frequency: 1 - optimizer: - SGD: - learning_rate: 0.001 - momentum: 0.1 - nesterov: True - weight_decay: 0.001 - criterion: - KnowledgeDistillationLoss: - temperature: 1.0 - loss_types: ['CE', 'KL'] - loss_weights: [0.5, 0.5] - dataloader: - batch_size: 30 - dataset: - dummy: - shape: [128, 3, 224, 224] - label: True - evaluation: - accuracy: - metric: - topk: 1 - dataloader: - batch_size: 30 - dataset: - dummy: - shape: [128, 3, 224, 224] - label: True - """ - with open("fake.yaml", "w", encoding="utf-8") as f: - f.write(fake_yaml) - - -def build_fake_yaml_1(): - fake_yaml = """ - model: - name: imagenet_distillation - framework: tensorflow - - distillation: - train: - start_epoch: 0 - end_epoch: 3 - iteration: 10 - frequency: 1 - optimizer: - SGD: - learning_rate: 0.001 - momentum: 0.1 - nesterov: True - weight_decay: 0.001 - criterion: - KnowledgeDistillationLoss: - temperature: 1.0 - loss_types: ['CE', 'CE'] - loss_weights: [0.5, 0.5] - dataloader: - batch_size: 30 - dataset: - dummy: - shape: [128, 224, 224, 3] - label: True - evaluation: - accuracy: - metric: - topk: 1 - dataloader: - batch_size: 30 - dataset: - dummy: - shape: [128, 224, 224, 3] - label: True - """ - with open("fake_1.yaml", "w", encoding="utf-8") as f: - f.write(fake_yaml) - - -def build_fake_yaml_2(): - fake_yaml = """ - model: - name: imagenet_distillation - framework: pytorch - - distillation: - train: - start_epoch: 0 - end_epoch: 3 - iteration: 10 - optimizer: - SGD: - learning_rate: 0.001 - momentum: 0.1 - nesterov: True - weight_decay: 0.001 - criterion: - IntermediateLayersKnowledgeDistillationLoss: - layer_mappings: [ - ['layer1.0', ], - [['layer1.1.conv1', ''], ['layer1.1.conv1', '0']], - ] - loss_types: ['KL', 'MSE'] - loss_weights: [0.5, 0.5] - dataloader: - batch_size: 30 - dataset: - dummy: - shape: [128, 3, 224, 224] - label: True - evaluation: - accuracy: - metric: - topk: 1 - dataloader: - batch_size: 30 - dataset: - dummy: - shape: [128, 3, 224, 224] - label: True - """ - with open("fake_2.yaml", "w", encoding="utf-8") as f: - f.write(fake_yaml) - - -class TestDistillation(unittest.TestCase): - student_model = torchvision.models.resnet18() - teacher_model = torchvision.models.resnet34() - - student_model_tf = tf.keras.applications.mobilenet.MobileNet() - teacher_model_tf = tf.keras.applications.mobilenet_v2.MobileNetV2() - - @classmethod - def setUpClass(cls): - build_fake_yaml() - build_fake_yaml_1() - build_fake_yaml_2() - - @classmethod - def tearDownClass(cls): - os.remove("fake.yaml") - os.remove("fake_1.yaml") - os.remove("fake_2.yaml") - shutil.rmtree("./saved", ignore_errors=True) - shutil.rmtree("runs", ignore_errors=True) - - def test_distillation(self): - from neural_compressor.conf.config import DistillationConf - from neural_compressor.experimental import Distillation - - conf = DistillationConf("fake.yaml") - distiller = Distillation(conf) - distiller = Distillation() - - from neural_compressor.conf.config import conf - - conf.model.framework = "pytorch" - conf.distillation.train.end_epoch = 3 - conf.distillation.train.iteration = 10 - conf.distillation.train.optimizer = { - "SGD": {"learning_rate": 0.001, "momentum": 0.1, "nesterov": True, "weight_decay": 0.001} - } - conf.distillation.train.dataloader.batch_size = 30 - conf.distillation.train.dataloader.dataset = {"dummy": {"shape": [128, 3, 224, 224], "label": True}} - conf.evaluation.accuracy.dataloader.batch_size = 30 - conf.evaluation.accuracy.dataloader.dataset = {"dummy": {"shape": [128, 3, 224, 224], "label": True}} - distiller = Distillation(conf) - distiller.student_model = self.student_model - distiller.teacher_model = self.teacher_model - print("student model: {}".format(distiller.student_model)) - distilled_model = distiller.fit() - distilled_model.save("./saved") - stat = torch.load("./saved/best_model.pt") - self.student_model.load_state_dict(stat) - - def test_distillation_intermediate_layers(self): - from neural_compressor.conf.config import DistillationConf - from neural_compressor.experimental import Distillation, common - - conf = DistillationConf("fake_2.yaml") - conf.usr_cfg.distillation.train.criterion.IntermediateLayersKnowledgeDistillationLoss.layer_mappings[1][1][ - -1 - ] = lambda x: x[:, :2, ...] - distiller = Distillation(conf) - distiller.student_model = common.Model(self.student_model) - distiller.teacher_model = common.Model(self.teacher_model) - print("student model: {}".format(distiller.student_model)) - _ = distiller.fit() - - def test_distillation_external(self): - from neural_compressor.experimental.common.criterion import TensorflowKnowledgeDistillationLossExternal - - criterion = TensorflowKnowledgeDistillationLossExternal() - criterion.teacher_model_forward(None) - y_true = [[0, 1, 0]] - y_pred = [[0.05, 0.95, 0]] - criterion.teacher_student_loss_cal(y_pred, y_true) - criterion.student_targets_loss_cal(y_pred, y_true) - - def test_distillation_external_new_API(self): - from neural_compressor.training import prepare_compression - - datasets = Datasets("pytorch") - dummy_dataset = datasets["dummy"](shape=(100, 3, 224, 224), low=0.0, high=1.0, label=True) - dummy_dataloader = PyTorchDataLoader(dummy_dataset) - - criterion = nn.CrossEntropyLoss() - distillation_criterion = KnowledgeDistillationLossConfig(loss_types=["CE", "KL"]) - optimizer = torch.optim.SGD(self.student_model.parameters(), lr=0.0001) - conf = DistillationConfig(self.teacher_model, distillation_criterion) - compression_manager = prepare_compression(copy.deepcopy(self.student_model), conf) - model = compression_manager.model - - epochs = 3 - iters = 10 - for nepoch in range(epochs): - model.train() - cnt = 0 - compression_manager.callbacks.on_epoch_begin(nepoch) - for image, target in dummy_dataloader: - compression_manager.callbacks.on_step_begin(cnt) - print(".", end="") - cnt += 1 - output = model(image) - loss = criterion(output, target) - loss = compression_manager.callbacks.on_after_compute_loss(image, output, loss) - optimizer.zero_grad() - loss.backward() - optimizer.step() - compression_manager.callbacks.on_step_end() - if cnt >= iters: - break - compression_manager.callbacks.on_epoch_end() - - model.save("./saved") - stat = torch.load("./saved/best_model.pt") - opt_model = self.student_model.load_state_dict(stat) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/distillation/test_distillation_2.x.py b/test/distillation/test_distillation_2.x.py index 3d433a5930c..51d57dea8be 100644 --- a/test/distillation/test_distillation_2.x.py +++ b/test/distillation/test_distillation_2.x.py @@ -11,7 +11,6 @@ from neural_compressor.adaptor import FRAMEWORKS from neural_compressor.adaptor.tf_utils.util import version1_lt_version2 -from neural_compressor.conf.dotdict import DotDict from neural_compressor.config import ( DistillationConfig, IntermediateLayersKnowledgeDistillationLossConfig, @@ -22,6 +21,7 @@ from neural_compressor.data.dataloaders.tensorflow_dataloader import TensorflowDataLoader from neural_compressor.training import prepare_compression from neural_compressor.utils import create_obj_from_config +from neural_compressor.utils.utility import DotDict class TestDistillation(unittest.TestCase): diff --git a/test/distillation/test_self_distillation_2.x.py b/test/distillation/test_self_distillation_2.x.py index d36a986734e..37dbab9f00d 100644 --- a/test/distillation/test_self_distillation_2.x.py +++ b/test/distillation/test_self_distillation_2.x.py @@ -7,76 +7,12 @@ import torchvision from neural_compressor.data import Datasets -from neural_compressor.experimental.data.dataloaders.pytorch_dataloader import PyTorchDataLoader - - -def build_fake_yaml(): - fake_yaml = """ - model: - name: self_distillation - framework: pytorch - - distillation: - train: - start_epoch: 0 - end_epoch: 3 - iteration: 10 - frequency: 1 - optimizer: - SGD: - learning_rate: 0.001 - momentum: 0.1 - nesterov: True - weight_decay: 0.001 - criterion: - SelfKnowledgeDistillationLoss: - layer_mappings: [ - [['resblock.1.feature.output', 'resblock.deepst.feature.output'], - ['resblock.2.feature.output','resblock.deepst.feature.output']], - [['resblock.2.fc','resblock.deepst.fc'], - ['resblock.3.fc','resblock.deepst.fc']], - [['resblock.1.fc','resblock.deepst.fc'], - ['resblock.2.fc','resblock.deepst.fc'], - ['resblock.3.fc','resblock.deepst.fc']] - ] - temperature: 3.0 - loss_types: ['L2', 'KL', 'CE'] - loss_weights: [0.5, 0.05, 0.02] - add_origin_loss: True - dataloader: - batch_size: 30 - dataset: - dummy: - shape: [128, 3, 224, 224] - label: True - evaluation: - accuracy: - metric: - topk: 1 - dataloader: - batch_size: 30 - dataset: - dummy: - shape: [128, 3, 224, 224] - label: True - """ - with open("fake.yaml", "w", encoding="utf-8") as f: - f.write(fake_yaml) +from neural_compressor.data.dataloaders.pytorch_dataloader import PyTorchDataLoader class TestSelfDistillation(unittest.TestCase): model = torchvision.models.resnet50() - @classmethod - def setUpClass(cls): - build_fake_yaml() - - @classmethod - def tearDownClass(cls): - os.remove("fake.yaml") - shutil.rmtree("./saved", ignore_errors=True) - shutil.rmtree("runs", ignore_errors=True) - def test_self_distillation(self): import copy diff --git a/test/distributed/test_distributed_pt_train.py b/test/distributed/test_distributed_pt_train.py deleted file mode 100644 index 598b7ffae8a..00000000000 --- a/test/distributed/test_distributed_pt_train.py +++ /dev/null @@ -1,149 +0,0 @@ -import os -import shutil -import signal -import subprocess -import unittest - -import torchvision - - -def build_fake_py(): - fake_py = """ -import os -import shutil -import unittest - -import torch -import torchvision -import torch.nn as nn -import horovod.torch as hvd - -from neural_compressor.data import Datasets -from neural_compressor.experimental.data.dataloaders.pytorch_dataloader import PyTorchDataLoader - - - -class TestPruning(unittest.TestCase): - - model = torchvision.models.resnet18() - - def test_pruning_internal(self): - from neural_compressor.experimental import Pruning, common - prune = Pruning('fake.yaml') - - prune.model = self.model - _ = prune() - print('rank {} in size {}'.format(hvd.rank(), hvd.size())) - -if __name__ == "__main__": - unittest.main() - """ - with open("fake.py", "w", encoding="utf-8") as f: - f.write(fake_py) - - -def build_fake_yaml(): - fake_yaml = """ - model: - name: imagenet_prune - framework: pytorch - - pruning: - train: - start_epoch: 0 - end_epoch: 4 - iteration: 10 - dataloader: - batch_size: 30 - distributed: True - dataset: - dummy: - shape: [128, 3, 224, 224] - label: True - optimizer: - SGD: - learning_rate: 0.1 - momentum: 0.1 - nesterov: True - weight_decay: 0.1 - criterion: - CrossEntropyLoss: - reduction: sum - approach: - weight_compression: - initial_sparsity: 0.0 - target_sparsity: 0.97 - start_epoch: 0 - end_epoch: 4 - pruners: - - !Pruner - start_epoch: 1 - end_epoch: 3 - prune_type: basic_magnitude - names: ['layer1.0.conv1.weight'] - - - !Pruner - target_sparsity: 0.6 - prune_type: gradient_sensitivity - update_frequency: 2 - names: ['layer1.0.conv2.weight'] - evaluation: - accuracy: - metric: - topk: 1 - dataloader: - distributed: True - batch_size: 30 - dataset: - dummy: - shape: [128, 3, 224, 224] - label: True - """ - with open("fake.yaml", "w", encoding="utf-8") as f: - f.write(fake_yaml) - - -class TestDistributed(unittest.TestCase): - model = torchvision.models.resnet18() - - @classmethod - def setUpClass(cls): - build_fake_yaml() - build_fake_py() - - @classmethod - def tearDownClass(cls): - os.remove("fake.yaml") - os.remove("fake.py") - shutil.rmtree("./saved", ignore_errors=True) - shutil.rmtree("runs", ignore_errors=True) - - def test_distributed(self): - distributed_cmd = "horovodrun -np 2 python fake.py" - p = subprocess.Popen( - distributed_cmd, preexec_fn=os.setsid, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True - ) # nosec - try: - out, error = p.communicate() - import re - - matches = re.findall(r".*rank ([01]) in size 2.*", out.decode("utf-8")) - assert "0" in matches - assert "1" in matches - except KeyboardInterrupt: - os.killpg(os.getpgid(p.pid), signal.SIGKILL) - assert 0 - - def test_single_node(self): - from neural_compressor.experimental import Pruning, common - - prune = Pruning("fake.yaml") - - prune.model = self.model - _ = prune() - # assert hvd hook is registered. pruner has 2 on_train_begin hooks: hvd and prune - assert len(prune.hooks_dict["on_train_begin"]) == 2 - - -if __name__ == "__main__": - unittest.main() diff --git a/test/graph_optimization/test_graph_optimization.py b/test/graph_optimization/test_graph_optimization.py deleted file mode 100644 index 072b90f8281..00000000000 --- a/test/graph_optimization/test_graph_optimization.py +++ /dev/null @@ -1,1011 +0,0 @@ -# -# -*- coding: utf-8 -*- -# -import os -import platform -import unittest - -import numpy as np -import tensorflow as tf -import yaml -from packaging.version import Version -from tensorflow.compat.v1 import graph_util -from tensorflow.core.framework import graph_pb2 -from tensorflow.python.framework import dtypes - -from neural_compressor.adaptor.tf_utils.graph_util import GraphRewriterHelper as Helper -from neural_compressor.adaptor.tf_utils.util import disable_random -from neural_compressor.utils.utility import CpuInfo - - -def build_fake_yaml(): - fake_yaml = """ - model: - name: fake_yaml - framework: tensorflow - inputs: input - outputs: op_to_store - device: cpu - graph_optimization: - precisions: [bf16] - evaluation: - accuracy: - metric: - topk: 1 - tuning: - accuracy_criterion: - relative: 0.0001 - workspace: - path: saved - """ - y = yaml.load(fake_yaml, Loader=yaml.SafeLoader) - with open("fake_yaml.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - f.close() - - -def build_fake_yaml_2(): - fake_yaml_2 = """ - model: - name: fake_yaml_2 - framework: tensorflow - inputs: input - outputs: op_to_store - graph_optimization: - precisions: [bf16] - """ - y = yaml.load(fake_yaml_2, Loader=yaml.SafeLoader) - with open("fake_yaml_2.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - f.close() - - -def build_fake_yaml_3(): - fake_yaml_3 = """ - model: - name: fake_yaml_3 - framework: tensorflow - inputs: input - outputs: op_to_store - graph_optimization: - precisions: - - bf16 - - fp32 - """ - y = yaml.load(fake_yaml_3, Loader=yaml.SafeLoader) - with open("fake_yaml_3.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - f.close() - - -def build_fake_yaml_4(): - fake_yaml_4 = """ - model: - name: fake_yaml_4 - framework: pytorch - inputs: input - outputs: op_to_store - graph_optimization: - precisions: [bf16] - """ - y = yaml.load(fake_yaml_4, Loader=yaml.SafeLoader) - with open("fake_yaml_4.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - f.close() - - -def build_fake_yaml_5(): - fake_yaml = """ - model: - name: fake_yaml_5 - framework: tensorflow - inputs: input - outputs: op_to_store - device: cpu - graph_optimization: - precisions: [bf16] - evaluation: - accuracy: - multi_metrics: - topk: 1 - MSE: - compare_label: False - weight: [1, 0] - tuning: - accuracy_criterion: - relative: 0.0001 - workspace: - path: saved - exit_policy: - max_trials: 3 - timeout: 50 - """ - y = yaml.load(fake_yaml, Loader=yaml.SafeLoader) - with open("fake_yaml_5.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - f.close() - - -def build_fake_yaml_6(): - fake_yaml = """ - model: - name: fake_yaml_6 - framework: tensorflow - inputs: input - outputs: op_to_store - device: cpu - graph_optimization: - precisions: [bf16] - evaluation: - accuracy: - multi_metrics: - topk: 1 - MSE: - compare_label: False - weight: [1, 0] - tuning: - accuracy_criterion: - relative: 0.0001 - workspace: - path: saved - exit_policy: - max_trials: 3 - timeout: 50 - """ - y = yaml.load(fake_yaml, Loader=yaml.SafeLoader) - with open("fake_yaml_6.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - f.close() - - -class MyMetric(object): - def __init__(self, *args): - self.pred_list = [] - self.label_list = [] - self.samples = 0 - - def update(self, predict, label): - self.pred_list.extend(predict) - self.label_list.extend(label) - self.samples += len(label) - - def reset(self): - self.pred_list = [] - self.label_list = [] - self.samples = 0 - - def result(self): - pred = np.array(self.pred_list) - label = np.array(self.label_list) - ones = np.ones(pred.ndim, dtype=np.int32) - ones[0] = label.shape[0] - label = np.array(self.label_list).reshape(ones) - correct_num = np.sum(pred == label) - return correct_num / self.samples - - -class TestGraphOptimizationOnNonBF16Host(unittest.TestCase): - @classmethod - def setUpClass(self): - build_fake_yaml() - - @classmethod - def tearDownClass(self): - os.remove("fake_yaml.yaml") - - @disable_random() - @unittest.skipIf(tf.__version__ < "2.0", "does not support on 1.15up3") - def test_bf16_cfg_on_non_bf16_enabled_host(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 300, 300, 16], name="input") - top_relu = tf.nn.relu(x) - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(top_relu, paddings, "CONSTANT") - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv_weights_2 = tf.compat.v1.get_variable( - "weight_2", [3, 8, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv2d(x_pad, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - relu = tf.nn.relu(conv) - - max_pool = tf.nn.max_pool(relu, ksize=1, strides=[1, 2, 2, 1], padding="SAME") - conv_bias = tf.compat.v1.get_variable("bias", [16], initializer=tf.compat.v1.random_normal_initializer()) - conv_1 = tf.nn.conv2d(max_pool, conv_weights_2, strides=[1, 2, 2, 1], padding="VALID", name="conv1_3") - conv_bias = tf.math.add(conv_1, conv_bias) - relu6 = tf.nn.relu6(conv_bias, name="op_to_store") - out_name = relu6.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.conf.config import GraphOptConf - from neural_compressor.experimental import GraphOptimization, common - - conf = GraphOptConf("fake_yaml.yaml") - graph_optimizer = GraphOptimization(conf) - dataset = graph_optimizer.dataset("dummy", shape=(100, 300, 300, 16), label=True) - graph_optimizer.eval_dataloader = common.DataLoader(dataset) - graph_optimizer.model = output_graph_def - output_graph = graph_optimizer.fit() - found_cast_op = False - - for i in output_graph.graph_def.node: - if i.op == "Cast": - found_cast_op = True - break - - if CpuInfo().bf16: - self.assertEqual(found_cast_op, True) - else: - self.assertEqual(found_cast_op, False) - - -@unittest.skipIf(tf.__version__ < "2.0", "does not support on 1.15up3") -class TestGraphOptimization(unittest.TestCase): - @classmethod - def setUpClass(self): - os.environ["FORCE_BF16"] = "1" - if platform.system().lower() == "windows": - self.skipTest(self, "Graph Optimization NOT Support Windows Yet") - build_fake_yaml() - build_fake_yaml_2() - build_fake_yaml_3() - build_fake_yaml_4() - build_fake_yaml_5() - build_fake_yaml_6() - - @classmethod - def tearDownClass(self): - del os.environ["FORCE_BF16"] - os.remove("fake_yaml.yaml") - os.remove("fake_yaml_2.yaml") - os.remove("fake_yaml_3.yaml") - os.remove("fake_yaml_4.yaml") - os.remove("fake_yaml_5.yaml") - os.remove("fake_yaml_6.yaml") - - def test_not_supported_model(self): - import neural_compressor.adaptor.pytorch as nc_torch - - PT_VERSION = nc_torch.get_torch_version() - if PT_VERSION > Version("1.8.0-rc1") and PT_VERSION < Version("1.9.0-rc1"): - pass - else: - import torchvision - - model = torchvision.models.resnet18() - from neural_compressor.experimental import Graph_Optimization - - graph_optimizer = Graph_Optimization("fake_yaml_4.yaml") - graph_optimizer.input = "input" - graph_optimizer.output = "op_to_store" - graph_optimizer.model = model - try: - output_graph = graph_optimizer.fit() - except SystemExit: - pass - - def test_not_supported_model_without_yaml(self): - import neural_compressor.adaptor.pytorch as nc_torch - - PT_VERSION = nc_torch.get_torch_version() - if PT_VERSION > Version("1.8.0-rc1") and PT_VERSION < Version("1.9.0-rc1"): - pass - else: - import torchvision - - model = torchvision.models.resnet18() - from neural_compressor.experimental import Graph_Optimization - - graph_optimizer = Graph_Optimization() - graph_optimizer.input = "input" - graph_optimizer.output = "op_to_store" - try: - graph_optimizer.model = model - except SystemExit: - pass - - def test_not_supported_model_with_conf(self): - import neural_compressor.adaptor.pytorch as nc_torch - - PT_VERSION = nc_torch.get_torch_version() - if PT_VERSION > Version("1.8.0-rc1") and PT_VERSION < Version("1.9.0-rc1"): - pass - else: - import torchvision - - from neural_compressor.conf.config import conf - from neural_compressor.experimental import Graph_Optimization - - model = torchvision.models.resnet18() - - conf.model.inputs = "input" - conf.model.outputs = "op_to_store" - conf.graph_optimization.precisions = "bf16" - graph_optimizer = Graph_Optimization(conf) - try: - graph_optimizer.model = model - except SystemExit: - pass - - @disable_random() - def test_graph_optimization_with_evaluation(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 300, 300, 16], name="input") - top_relu = tf.nn.relu(x) - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(top_relu, paddings, "CONSTANT") - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv_weights_2 = tf.compat.v1.get_variable( - "weight_2", [3, 8, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv2d(x_pad, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - relu = tf.nn.relu(conv) - - max_pool = tf.nn.max_pool(relu, ksize=1, strides=[1, 2, 2, 1], padding="SAME") - conv_bias = tf.compat.v1.get_variable("bias", [16], initializer=tf.compat.v1.random_normal_initializer()) - conv_1 = tf.nn.conv2d(max_pool, conv_weights_2, strides=[1, 2, 2, 1], padding="VALID", name="conv1_3") - conv_bias = tf.math.add(conv_1, conv_bias) - relu6 = tf.nn.relu6(conv_bias, name="op_to_store") - out_name = relu6.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import GraphOptimization, common - - graph_optimizer = GraphOptimization("fake_yaml.yaml") - dataset = graph_optimizer.dataset("dummy", shape=(100, 300, 300, 16), label=True) - graph_optimizer.eval_dataloader = common.DataLoader(dataset) - graph_optimizer.model = output_graph_def - output_graph = graph_optimizer.fit() - found_cast_op = False - - for i in output_graph.graph_def.node: - if i.op == "Cast": - found_cast_op = True - break - - self.assertEqual(found_cast_op, True) - - @disable_random() - def test_graph_optimization_without_evaluation(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - top_relu = tf.nn.relu(x) - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(top_relu, paddings, "CONSTANT") - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv_weights_2 = tf.compat.v1.get_variable( - "weight_2", [3, 8, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv2d(x_pad, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - relu = tf.nn.relu(conv) - - max_pool = tf.nn.max_pool(relu, ksize=1, strides=[1, 2, 2, 1], padding="SAME") - conv_bias = tf.compat.v1.get_variable("bias", [16], initializer=tf.compat.v1.random_normal_initializer()) - conv_1 = tf.nn.conv2d(max_pool, conv_weights_2, strides=[1, 2, 2, 1], padding="VALID", name="conv1_3") - conv_bias = tf.math.add(conv_1, conv_bias) - relu6 = tf.nn.relu6(conv_bias, name="op_to_store") - - out_name = relu6.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Graph_Optimization, common - - graph_optimizer = Graph_Optimization("fake_yaml_2.yaml") - graph_optimizer.model = output_graph_def - output_graph = graph_optimizer.fit() - found_cast_op = False - - for i in output_graph.graph_def.node: - if i.op == "Cast": - found_cast_op = True - break - - self.assertEqual(found_cast_op, True) - - @disable_random() - def test_graph_optimization_without_yaml(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - top_relu = tf.nn.relu(x) - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(top_relu, paddings, "CONSTANT") - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv_weights_2 = tf.compat.v1.get_variable( - "weight_2", [3, 8, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv2d(x_pad, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - relu = tf.nn.relu(conv) - - max_pool = tf.nn.max_pool(relu, ksize=1, strides=[1, 2, 2, 1], padding="SAME") - conv_bias = tf.compat.v1.get_variable("bias", [16], initializer=tf.compat.v1.random_normal_initializer()) - conv_1 = tf.nn.conv2d(max_pool, conv_weights_2, strides=[1, 2, 2, 1], padding="VALID", name="conv1_3") - conv_bias = tf.math.add(conv_1, conv_bias) - relu6 = tf.nn.relu6(conv_bias, name="op_to_store") - relu62 = tf.nn.relu6(conv_bias, name="op2_to_store") - - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, - input_graph_def=sess.graph_def, - output_node_names=[relu6.name.split(":")[0], relu62.name.split(":")[0]], - ) - - from neural_compressor.experimental import Graph_Optimization - - graph_optimizer = Graph_Optimization() - graph_optimizer.precisions = "fp32" - graph_optimizer.input = "input" - graph_optimizer.output = "op_to_store, op2_to_store" - - graph_optimizer.model = output_graph_def - output_graph = graph_optimizer.fit() - found_cast_op = False - for i in output_graph.graph_def.node: - if i.op == "Cast": - found_cast_op = True - break - input_name = graph_optimizer.input - output_name = graph_optimizer.output - self.assertEqual(found_cast_op, False) - self.assertEqual(input_name, "input") - self.assertEqual(output_name, "op_to_store, op2_to_store") - - @disable_random() - def test_graph_optimization_with_yaml(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - top_relu = tf.nn.relu(x) - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(top_relu, paddings, "CONSTANT") - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv_weights_2 = tf.compat.v1.get_variable( - "weight_2", [3, 8, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv2d(x_pad, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - relu = tf.nn.relu(conv) - - max_pool = tf.nn.max_pool(relu, ksize=1, strides=[1, 2, 2, 1], padding="SAME") - conv_bias = tf.compat.v1.get_variable("bias", [16], initializer=tf.compat.v1.random_normal_initializer()) - conv_1 = tf.nn.conv2d(max_pool, conv_weights_2, strides=[1, 2, 2, 1], padding="VALID", name="conv1_3") - conv_bias = tf.math.add(conv_1, conv_bias) - relu6 = tf.nn.relu6(conv_bias, name="op_to_store") - - out_name = relu6.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Graph_Optimization - - graph_optimizer = Graph_Optimization("fake_yaml_3.yaml") - - graph_optimizer.model = output_graph_def - output_graph = graph_optimizer.fit() - found_cast_op = False - - for i in output_graph.graph_def.node: - if i.op == "Cast": - found_cast_op = True - break - - self.assertEqual(found_cast_op, True) - - @disable_random() - def test_graph_optimization_with_custom_metric_without_postprocess(self): - os.environ["FORCE_BF16"] = "1" - - x = tf.compat.v1.placeholder(tf.float32, [1, 300, 300, 16], name="input") - top_relu = tf.nn.relu(x) - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(top_relu, paddings, "CONSTANT") - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv_weights_2 = tf.compat.v1.get_variable( - "weight_2", [3, 8, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv2d(x_pad, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - relu = tf.nn.relu(conv) - - max_pool = tf.nn.max_pool(relu, ksize=1, strides=[1, 2, 2, 1], padding="SAME") - conv_bias = tf.compat.v1.get_variable("bias", [16], initializer=tf.compat.v1.random_normal_initializer()) - conv_1 = tf.nn.conv2d(max_pool, conv_weights_2, strides=[1, 2, 2, 1], padding="VALID", name="conv1_3") - conv_bias = tf.math.add(conv_1, conv_bias) - relu6 = tf.nn.relu6(conv_bias, name="op_to_store") - out_name = relu6.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Graph_Optimization, common - - graph_optimizer = Graph_Optimization("fake_yaml_3.yaml") - graph_optimizer.metric = common.Metric(MyMetric) - dataset = graph_optimizer.dataset("dummy", shape=(100, 300, 300, 16), label=True) - graph_optimizer.precisions = ["fp32", "bf16"] - graph_optimizer.eval_dataloader = common.DataLoader(dataset) - graph_optimizer.model = output_graph_def - output_graph = graph_optimizer.fit() - found_cast_op = False - - self.assertIsNotNone(output_graph.graph_def) - - for i in output_graph.graph_def.node: - if i.op == "Cast": - found_cast_op = True - break - - self.assertEqual(found_cast_op, True) - - @disable_random() - def test_graph_optimization_without_custom_metric_with_postprocess(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 300, 300, 16], name="input") - top_relu = tf.nn.relu(x) - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(top_relu, paddings, "CONSTANT") - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv_weights_2 = tf.compat.v1.get_variable( - "weight_2", [3, 8, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv2d(x_pad, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - relu = tf.nn.relu(conv) - - max_pool = tf.nn.max_pool(relu, ksize=1, strides=[1, 2, 2, 1], padding="SAME") - conv_bias = tf.compat.v1.get_variable("bias", [16], initializer=tf.compat.v1.random_normal_initializer()) - conv_1 = tf.nn.conv2d(max_pool, conv_weights_2, strides=[1, 2, 2, 1], padding="VALID", name="conv1_3") - conv_bias = tf.math.add(conv_1, conv_bias) - relu6 = tf.nn.relu6(conv_bias, name="op_to_store") - out_name = relu6.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.data import Postprocess - from neural_compressor.experimental import Graph_Optimization, common, data - - graph_optimizer = Graph_Optimization("fake_yaml.yaml") - dataset = graph_optimizer.dataset("dummy", shape=(100, 300, 300, 16), label=True) - graph_optimizer.eval_dataloader = common.DataLoader(dataset) - graph_optimizer.postprocess = Postprocess(data.transforms.transform.TensorflowWrapFunction(np.array)) - graph_optimizer.model = output_graph_def - output_graph = graph_optimizer.fit() - found_cast_op = False - - for i in output_graph.graph_def.node: - if i.op == "Cast": - found_cast_op = True - break - - self.assertEqual(found_cast_op, True) - - @disable_random() - def test_graph_optimization_with_eval_func(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 300, 300, 16], name="input") - top_relu = tf.nn.relu(x) - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(top_relu, paddings, "CONSTANT") - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv_weights_2 = tf.compat.v1.get_variable( - "weight_2", [3, 8, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv2d(x_pad, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - relu = tf.nn.relu(conv) - - max_pool = tf.nn.max_pool(relu, ksize=1, strides=[1, 2, 2, 1], padding="SAME") - conv_bias = tf.compat.v1.get_variable("bias", [16], initializer=tf.compat.v1.random_normal_initializer()) - conv_1 = tf.nn.conv2d(max_pool, conv_weights_2, strides=[1, 2, 2, 1], padding="VALID", name="conv1_3") - conv_bias = tf.math.add(conv_1, conv_bias) - relu6 = tf.nn.relu6(conv_bias, name="op_to_store") - - out_name = relu6.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Graph_Optimization, common - - graph_optimizer = Graph_Optimization("fake_yaml.yaml") - - dataset = graph_optimizer.dataset("dummy", shape=(100, 300, 300, 16), label=True) - graph_optimizer.eval_dataloader = common.DataLoader(dataset) - graph_optimizer.model = output_graph_def - graph_optimizer.eval_func = None - - output_graph = graph_optimizer.fit() - found_cast_op = False - - for i in output_graph.graph_def.node: - if i.op == "Cast": - found_cast_op = True - break - self.assertEqual(found_cast_op, True) - - @disable_random() - def test_graph_optimization_multimetric_noweight(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 300, 300, 16], name="input") - top_relu = tf.nn.relu(x) - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(top_relu, paddings, "CONSTANT") - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv_weights_2 = tf.compat.v1.get_variable( - "weight_2", [3, 8, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv2d(x_pad, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - relu = tf.nn.relu(conv) - - max_pool = tf.nn.max_pool(relu, ksize=1, strides=[1, 2, 2, 1], padding="SAME") - conv_bias = tf.compat.v1.get_variable("bias", [16], initializer=tf.compat.v1.random_normal_initializer()) - conv_1 = tf.nn.conv2d(max_pool, conv_weights_2, strides=[1, 2, 2, 1], padding="VALID", name="conv1_3") - conv_bias = tf.math.add(conv_1, conv_bias) - relu6 = tf.nn.relu6(conv_bias, name="op_to_store") - - out_name = relu6.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Graph_Optimization, common - - graph_optimizer = Graph_Optimization("fake_yaml_5.yaml") - - dataset = graph_optimizer.dataset("dummy", shape=(100, 300, 300, 16), label=True) - graph_optimizer.eval_dataloader = common.DataLoader(dataset) - graph_optimizer.model = output_graph_def - graph_optimizer.eval_func = None - - output_graph = graph_optimizer.fit() - found_cast_op = False - - for i in output_graph.graph_def.node: - if i.op == "Cast": - found_cast_op = True - break - self.assertEqual(found_cast_op, True) - - @disable_random() - def test_graph_optimization_multimetric_weight(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 300, 300, 16], name="input") - top_relu = tf.nn.relu(x) - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(top_relu, paddings, "CONSTANT") - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv_weights_2 = tf.compat.v1.get_variable( - "weight_2", [3, 8, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv2d(x_pad, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - relu = tf.nn.relu(conv) - - max_pool = tf.nn.max_pool(relu, ksize=1, strides=[1, 2, 2, 1], padding="SAME") - conv_bias = tf.compat.v1.get_variable("bias", [16], initializer=tf.compat.v1.random_normal_initializer()) - conv_1 = tf.nn.conv2d(max_pool, conv_weights_2, strides=[1, 2, 2, 1], padding="VALID", name="conv1_3") - conv_bias = tf.math.add(conv_1, conv_bias) - relu6 = tf.nn.relu6(conv_bias, name="op_to_store") - - out_name = relu6.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Graph_Optimization, common - - graph_optimizer = Graph_Optimization("fake_yaml_6.yaml") - - dataset = graph_optimizer.dataset("dummy", shape=(100, 300, 300, 16), label=True) - graph_optimizer.eval_dataloader = common.DataLoader(dataset) - graph_optimizer.model = output_graph_def - graph_optimizer.eval_func = None - - output_graph = graph_optimizer.fit() - found_cast_op = False - - for i in output_graph.graph_def.node: - if i.op == "Cast": - found_cast_op = True - break - self.assertEqual(found_cast_op, True) - - @disable_random() - def test_graph_optimization_with_force_bf16(self): - os.environ["FORCE_BF16"] = "1" - - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - top_relu = tf.nn.relu(x) - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(top_relu, paddings, "CONSTANT") - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv_weights_2 = tf.compat.v1.get_variable( - "weight_2", [3, 8, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv2d(x_pad, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - relu = tf.nn.relu(conv) - - max_pool = tf.nn.max_pool(relu, ksize=1, strides=[1, 2, 2, 1], padding="SAME") - conv_bias = tf.compat.v1.get_variable("bias", [16], initializer=tf.compat.v1.random_normal_initializer()) - conv_1 = tf.nn.conv2d(max_pool, conv_weights_2, strides=[1, 2, 2, 1], padding="VALID", name="conv1_3") - conv_bias = tf.math.add(conv_1, conv_bias) - relu6 = tf.nn.relu6(conv_bias, name="op_to_store") - - out_name = relu6.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Graph_Optimization - - graph_optimizer = Graph_Optimization() - graph_optimizer.input = "input" - graph_optimizer.output = "op_to_store" - - graph_optimizer.precisions = "bf16" - graph_optimizer.model = output_graph_def - output_graph = graph_optimizer.fit() - found_cast_op = False - - for i in output_graph.graph_def.node: - if i.op == "Cast": - found_cast_op = True - break - - self.assertEqual(found_cast_op, True) - - @disable_random() - def test_graph_optimization_with_bn(self): - input_constant_name = "input_constant" - relu_name = "relu" - float_graph_def = graph_pb2.GraphDef() - input_constant = Helper.create_constant_node( - input_constant_name, value=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], dtype=dtypes.float32, shape=[1, 2, 6, 6] - ) - float_graph_def.node.extend([input_constant]) - relu_node = Helper.create_node("Relu", relu_name, [input_constant_name]) - Helper.set_attr_dtype(relu_node, "T", dtypes.float32) - float_graph_def.node.extend([relu_node]) - - b_constant_name = "b_constant" - conv2d_name = "conv2d_1" - b_constant = Helper.create_constant_node( - b_constant_name, value=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], dtype=dtypes.float32, shape=[1, 2, 6, 6] - ) - float_graph_def.node.extend([b_constant]) - - conv2d_node = Helper.create_node("Conv2D", conv2d_name, [relu_name, b_constant_name]) - Helper.set_attr_dtype(conv2d_node, "T", dtypes.float32) - Helper.set_attr_string(conv2d_node, "padding", b"SAME") - Helper.set_attr_int_list(conv2d_node, "strides", [1, 1, 1, 1]) - - float_graph_def.node.extend([conv2d_node]) - - bias_add_name = "bias_add" - offset_constant_name = "offset_constant" - - offset_constant = Helper.create_constant_node( - offset_constant_name, value=[1, 2, 3, 4, 5, 6], dtype=dtypes.float32, shape=[6] - ) - float_graph_def.node.extend([offset_constant]) - - bias_add_node = Helper.create_node("BiasAdd", bias_add_name, [conv2d_name, offset_constant_name]) - Helper.set_attr_dtype(bias_add_node, "T", dtypes.float32) - float_graph_def.node.extend([bias_add_node]) - - bn_scale_name = "bn_scale" - bn_scale_node = Helper.create_constant_node( - bn_scale_name, value=[1, 2, 3, 4, 5, 6], dtype=dtypes.float32, shape=[6] - ) - bn_offset_name = "bn_offset" - bn_offset_node = Helper.create_constant_node( - bn_offset_name, value=[1, 2, 3, 4, 5, 6], dtype=dtypes.float32, shape=[6] - ) - bn_mean_name = "bn_mean" - bn_mean_node = Helper.create_constant_node( - bn_mean_name, - value=[ - 1, - 2, - ], - dtype=dtypes.float32, - shape=[ - 2, - ], - ) - bn_var_name = "bn_var" - bn_var_node = Helper.create_constant_node(bn_var_name, value=[], dtype=dtypes.float32, shape=[0]) - fused_bn_node_name = "bn" - fused_bn_node = Helper.create_node( - "FusedBatchNormV3", - fused_bn_node_name, - [bias_add_name, bn_scale_name, bn_offset_name, bn_mean_name, bn_var_name], - ) - Helper.set_attr_dtype(fused_bn_node, "T", dtypes.float32) - Helper.set_attr_dtype(fused_bn_node, "U", dtypes.float32) - float_graph_def.node.extend([fused_bn_node, bn_scale_node, bn_offset_node, bn_mean_node, bn_var_node]) - - post_relu_name = "post_relu" - post_relu_node = Helper.create_node("Relu", post_relu_name, [fused_bn_node_name]) - Helper.set_attr_dtype(post_relu_node, "T", dtypes.float32) - - float_graph_def.node.extend([post_relu_node]) - - from neural_compressor.experimental import Graph_Optimization - - graph_optimizer = Graph_Optimization() - - graph_optimizer.precisions = "bf16" - graph_optimizer.model = float_graph_def - output_graph = graph_optimizer.fit() - bn_bf16 = False - for i in output_graph.graph_def.node: - if i.op == "FusedBatchNormV3" and i.attr["T"].type == dtypes.bfloat16: - bn_bf16 = True - if i.op == "Conv2D" and i.attr["T"].type == dtypes.bfloat16: - bn_bf16 = True - self.assertEqual(bn_bf16, True) - - -class TestGraphOptmizationFP32(unittest.TestCase): - @disable_random() - def test_graph_optimization_without_yaml_without_precisions(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - top_relu = tf.nn.relu(x) - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(top_relu, paddings, "CONSTANT") - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv_weights_2 = tf.compat.v1.get_variable( - "weight_2", [3, 8, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv2d(x_pad, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - relu = tf.nn.relu(conv) - - max_pool = tf.nn.max_pool(relu, ksize=1, strides=[1, 2, 2, 1], padding="SAME") - conv_bias = tf.compat.v1.get_variable("bias", [16], initializer=tf.compat.v1.random_normal_initializer()) - conv_1 = tf.nn.conv2d(max_pool, conv_weights_2, strides=[1, 2, 2, 1], padding="VALID", name="conv1_3") - conv_bias = tf.math.add(conv_1, conv_bias) - relu6 = tf.nn.relu6(conv_bias, name="op_to_store") - - out_name = relu6.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Graph_Optimization - - graph_optimizer = Graph_Optimization() - graph_optimizer.input = "input" - graph_optimizer.output = "op_to_store" - - graph_optimizer.model = output_graph_def - output_graph = graph_optimizer.fit() - found_cast_op = False - - for i in output_graph.graph_def.node: - if i.op == "Cast": - found_cast_op = True - break - precision = graph_optimizer.precisions - self.assertEqual(found_cast_op, False) - self.assertEqual(precision, "fp32") - - @disable_random() - def test_graph_optimization_without_yaml_with_precisions(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - top_relu = tf.nn.relu(x) - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(top_relu, paddings, "CONSTANT") - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv_weights_2 = tf.compat.v1.get_variable( - "weight_2", [3, 8, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv2d(x_pad, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - relu = tf.nn.relu(conv) - - max_pool = tf.nn.max_pool(relu, ksize=1, strides=[1, 2, 2, 1], padding="SAME") - conv_bias = tf.compat.v1.get_variable("bias", [16], initializer=tf.compat.v1.random_normal_initializer()) - conv_1 = tf.nn.conv2d(max_pool, conv_weights_2, strides=[1, 2, 2, 1], padding="VALID", name="conv1_3") - conv_bias = tf.math.add(conv_1, conv_bias) - relu6 = tf.nn.relu6(conv_bias, name="op_to_store") - - out_name = relu6.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Graph_Optimization - - graph_optimizer = Graph_Optimization() - graph_optimizer.precisions = "fp32" - - graph_optimizer.model = output_graph_def - output_graph = graph_optimizer.fit() - found_cast_op = False - - for i in output_graph.graph_def.node: - if i.op == "Cast": - found_cast_op = True - break - - self.assertEqual(found_cast_op, False) - - @disable_random() - def test_graph_optimization_fp32_only_with_force_bf16(self): - os.environ["FORCE_BF16"] = "1" - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - top_relu = tf.nn.relu(x) - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(top_relu, paddings, "CONSTANT") - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv_weights_2 = tf.compat.v1.get_variable( - "weight_2", [3, 8, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv2d(x_pad, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - relu = tf.nn.relu(conv) - - max_pool = tf.nn.max_pool(relu, ksize=1, strides=[1, 2, 2, 1], padding="SAME") - conv_bias = tf.compat.v1.get_variable("bias", [16], initializer=tf.compat.v1.random_normal_initializer()) - conv_1 = tf.nn.conv2d(max_pool, conv_weights_2, strides=[1, 2, 2, 1], padding="VALID", name="conv1_3") - conv_bias = tf.math.add(conv_1, conv_bias) - relu6 = tf.nn.relu6(conv_bias, name="op_to_store") - - out_name = relu6.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Graph_Optimization - - graph_optimizer = Graph_Optimization() - graph_optimizer.input = "input" - graph_optimizer.output = "op_to_store" - - graph_optimizer.model = output_graph_def - output_graph = graph_optimizer.fit() - found_cast_op = False - - for i in output_graph.graph_def.node: - if i.op == "Cast": - found_cast_op = True - break - - self.assertEqual(found_cast_op, False) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/ipex/test_adaptor_ipex.py b/test/ipex/test_adaptor_ipex.py index 7cb3555e2e6..c40700ed45c 100644 --- a/test/ipex/test_adaptor_ipex.py +++ b/test/ipex/test_adaptor_ipex.py @@ -9,9 +9,7 @@ import neural_compressor.adaptor.pytorch as nc_torch from neural_compressor import mix_precision, set_workspace -from neural_compressor.conf.pythonic_config import config from neural_compressor.config import MixedPrecisionConfig -from neural_compressor.experimental import common from neural_compressor.utils.pytorch import load from neural_compressor.utils.utility import LazyImport @@ -95,57 +93,12 @@ def __iter__(self): yield torch.randn(1, 3, 224, 224).to(self.device) -@unittest.skipIf( - PT_VERSION >= Version("1.12.0").release or PT_VERSION < Version("1.10.0").release, - "Please use Intel extension for Pytorch version 1.10 or 1.11", -) -class TestPytorchIPEX_1_10_Adaptor(unittest.TestCase): - @classmethod - def setUpClass(self): - config.quantization.backend = "ipex" - config.quantization.approach = "post_training_static_quant" - config.quantization.use_bf16 = False - set_workspace("./saved") - - @classmethod - def tearDownClass(self): - shutil.rmtree("./saved", ignore_errors=True) - shutil.rmtree("runs", ignore_errors=True) - - def test_tuning_ipex(self): - from neural_compressor.experimental import Quantization - - model = M() - quantizer = Quantization(config) - quantizer.model = model - quantizer.conf.usr_cfg.tuning.exit_policy["performance_only"] = True - dataset = quantizer.dataset("dummy", (100, 3, 224, 224), label=True) - dataloader = torch.utils.data.DataLoader(dataset) - quantizer.calib_dataloader = dataloader - quantizer.eval_dataloader = dataloader - nc_model = quantizer.fit() - nc_model.save("./saved") - self.assertTrue(isinstance(nc_model._model, torch.jit.ScriptModule)) - q_model = load("./saved", model, dataloader=dataloader) - from neural_compressor.experimental import Benchmark - - evaluator = Benchmark(config) - evaluator.model = q_model - evaluator.b_dataloader = dataloader - evaluator.fit("accuracy") - - @unittest.skipIf( PT_VERSION < Version("1.12.0").release, "Please use Intel extension for Pytorch version higher or equal to 1.12" ) class TestPytorchIPEX_1_12_Adaptor(unittest.TestCase): @classmethod def setUpClass(self): - config.quantization.backend = "ipex" - config.quantization.accuracy_criterion.tolerable_loss = 0.0001 - config.quantization.accuracy_criterion.higher_is_better = False - config.quantization.approach = "post_training_static_quant" - config.quantization.use_bf16 = False set_workspace("./saved") @classmethod @@ -153,53 +106,6 @@ def tearDownClass(self): shutil.rmtree("./saved", ignore_errors=True) shutil.rmtree("runs", ignore_errors=True) - def test_tuning_ipex(self): - from neural_compressor.experimental import Quantization - - model = M() - quantizer = Quantization(config) - quantizer.model = model - quantizer.conf.usr_cfg.tuning.exit_policy["performance_only"] = False - dataset = quantizer.dataset("dummy", (100, 3, 224, 224), label=True) - dataloader = torch.utils.data.DataLoader(dataset) - quantizer.calib_dataloader = dataloader - quantizer.calib_func = calib_func - quantizer.eval_dataloader = dataloader - nc_model = quantizer.fit() - sparsity = nc_model.report_sparsity() - self.assertTrue(sparsity[-1] >= 0.0) - nc_model.save("./saved") - self.assertTrue(isinstance(nc_model._model, torch.jit.ScriptModule)) - q_model = load("./saved", model, dataloader=dataloader) - from neural_compressor.experimental import Benchmark - - evaluator = Benchmark(config) - evaluator.model = q_model - evaluator.b_dataloader = dataloader - evaluator.fit("accuracy") - - def test_tuning_ipex_for_ipex_autotune_func(self): - from neural_compressor.experimental import Quantization - - model = M() - if PT_VERSION < Version("2.1").release: - qconfig = ipex.quantization.default_static_qconfig - else: - qconfig = ipex.quantization.default_static_qconfig_mapping - prepared_model = ipex.quantization.prepare( - model, qconfig, example_inputs=torch.ones(1, 3, 224, 224), inplace=False - ) - quantizer = Quantization(config) - quantizer.model = prepared_model - quantizer.conf.usr_cfg.tuning.exit_policy["max_trials"] = 5 - quantizer.conf.usr_cfg.tuning.exit_policy["timeout"] = 100 - dataset = quantizer.dataset("dummy", (100, 3, 224, 224), label=True) - dataloader = torch.utils.data.DataLoader(dataset) - quantizer.calib_dataloader = dataloader - quantizer.eval_dataloader = dataloader - nc_model = quantizer.fit() - self.assertTrue(isinstance(nc_model._model, torch.jit.ScriptModule)) - def test_copy_prepared_model(self): model = M() if PT_VERSION < Version("2.1").release: @@ -212,42 +118,6 @@ def test_copy_prepared_model(self): copy_model = torch_utils.util.auto_copy(prepared_model) self.assertTrue(isinstance(copy_model, torch.nn.Module)) - def test_bf16(self): - from neural_compressor.experimental import Quantization - - model = M() - if PT_VERSION < Version("2.1").release: - qconfig = ipex.quantization.default_static_qconfig - else: - qconfig = ipex.quantization.default_static_qconfig_mapping - prepared_model = ipex.quantization.prepare( - model, qconfig, example_inputs=torch.ones(1, 3, 224, 224), inplace=False - ) - config.quantization.use_bf16 = True - config.quantization.performance_only = True - quantizer = Quantization(config) - quantizer.model = model - dataset = quantizer.dataset("dummy", (100, 3, 224, 224), label=True) - dataloader = torch.utils.data.DataLoader(dataset) - quantizer.calib_dataloader = dataloader - quantizer.eval_dataloader = dataloader - nc_model = quantizer.fit() - self.assertTrue(isinstance(nc_model._model, torch.jit.ScriptModule)) - - def test_example_inputs(self): - from neural_compressor.experimental import Quantization - - model = M() - config.quantization.example_inputs = torch.randn([1, 3, 224, 224]) - quantizer = Quantization(config) - quantizer.model = model - quantizer.conf.usr_cfg.tuning.exit_policy["performance_only"] = False - dataset = quantizer.dataset("dummy", (100, 3, 224, 224), label=True) - dataloader = torch.utils.data.DataLoader(dataset) - quantizer.calib_dataloader = dataloader - nc_model = quantizer.fit() - self.assertTrue(isinstance(nc_model._model, torch.jit.ScriptModule)) - def test_new_API(self): model = M() from neural_compressor import PostTrainingQuantConfig, quantization diff --git a/test/itex/test_tensorflow_itex_basic.py b/test/itex/test_tensorflow_itex_basic.py deleted file mode 100644 index 7705a4acb09..00000000000 --- a/test/itex/test_tensorflow_itex_basic.py +++ /dev/null @@ -1,496 +0,0 @@ -# -# -*- coding: utf-8 -*- -# -import os -import platform -import shutil -import unittest - -import tensorflow as tf -import yaml -from tensorflow.compat.v1 import graph_util -from tensorflow.python.platform import gfile - -from neural_compressor.adaptor.tf_utils.util import disable_random, version1_gte_version2, version1_lt_version2 -from neural_compressor.experimental import Benchmark, Quantization, common - - -def build_fake_yaml(fake_yaml, save_path, **kwargs): - y = yaml.load(fake_yaml, Loader=yaml.SafeLoader) - with open(file=save_path, mode=kwargs["mode"], encoding=kwargs["encoding"]) as f: - yaml.dump(y, f) - - -@unittest.skipIf(version1_lt_version2(tf.version.VERSION, "2.8.0"), "Only supports tf greater 2.7.0") -class TestItexEnabling(unittest.TestCase): - @classmethod - def setUpClass(self): - os.system("rm *.log") - fake_yaml_1 = """ - model: - name: fake_model_cpu - framework: tensorflow_itex - inputs: input - device: cpu - quantization: - model_wise: - weight: - granularity: per_tensor - scheme: sym - dtype: int8 - algorithm: minmax - evaluation: - accuracy: - metric: - topk: 1 - tuning: - strategy: - name: basic - accuracy_criterion: - relative: 0.1 - exit_policy: - performance_only: True - workspace: - path: workspace_1 - """ - - fake_yaml_2 = """ - model: - name: fake_model_gpu - framework: tensorflow_itex - inputs: input - device: gpu - quantization: - model_wise: - weight: - granularity: per_tensor - scheme: sym - dtype: int8 - algorithm: minmax - evaluation: - accuracy: - metric: - topk: 1 - performance: - warmup: 10 - iteration: 100 - configs: - cores_per_instance: 1 - num_of_instance: 1 - tuning: - strategy: - name: basic - accuracy_criterion: - relative: 0.1 - exit_policy: - performance_only: True - workspace: - path: workspace_2 - """ - - fake_yaml_3 = """ - model: - name: fake_model_default_device - framework: tensorflow_itex - inputs: input - quantization: - model_wise: - weight: - granularity: per_tensor - scheme: sym - dtype: int8 - algorithm: minmax - evaluation: - accuracy: - metric: - topk: 1 - tuning: - strategy: - name: basic - accuracy_criterion: - relative: 0.1 - exit_policy: - performance_only: True - workspace: - path: workspace_3 - """ - build_fake_yaml(fake_yaml_1, "fake_yaml_1.yaml", mode="w", encoding="utf-8") - build_fake_yaml(fake_yaml_2, "fake_yaml_2.yaml", mode="w", encoding="utf-8") - build_fake_yaml(fake_yaml_3, "fake_yaml_3.yaml", mode="w", encoding="utf-8") - - @classmethod - def tearDownClass(self): - os.remove("fake_yaml_1.yaml") - os.remove("fake_yaml_2.yaml") - os.remove("fake_yaml_3.yaml") - if version1_gte_version2(tf.version.VERSION, "2.8.0"): - shutil.rmtree("workspace_1") - shutil.rmtree("workspace_2") - shutil.rmtree("workspace_3") - - @disable_random() - def test_itex_convert_basic_default_device(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - top_relu = tf.nn.relu(x) - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(top_relu, paddings, "CONSTANT") - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv2d(x_pad, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - normed = tf.compat.v1.layers.batch_normalization(conv) - conv_weights2 = tf.compat.v1.get_variable( - "weight2", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv2 = tf.nn.conv2d(top_relu, conv_weights2, strides=[1, 2, 2, 1], padding="SAME") - normed2 = tf.compat.v1.layers.batch_normalization(conv2) - add = tf.raw_ops.Add(x=normed, y=normed2, name="addv2") - relu = tf.nn.relu(add) - relu6 = tf.nn.relu6(relu, name="op_to_store") - out_name = relu6.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - - quantizer = Quantization("fake_yaml_3.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - - dequant_count = 0 - quantize_count = 0 - for i in output_graph.graph_def.node: - if "min" in i.name or "max" in i.name: - self.assertEqual(i.op, "HostConst") - if i.op == "HostConst": - self.assertTrue("min" in i.name or "max" in i.name) - if i.op == "Dequantize": - dequant_count += 1 - if i.op == "QuantizeV2": - quantize_count += 1 - - self.assertEqual(dequant_count, 5) - self.assertEqual(quantize_count, 4) - - @disable_random() - @unittest.skipIf(version1_lt_version2(tf.version.VERSION, "2.8.0"), "Only supports tf greater 2.7.0") - def test_itex_convert_basic_cpu(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - top_relu = tf.nn.relu(x) - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(top_relu, paddings, "CONSTANT") - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv2d(x_pad, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - normed = tf.compat.v1.layers.batch_normalization(conv) - # relu = tf.nn.relu(normed) - - conv_weights2 = tf.compat.v1.get_variable( - "weight2", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv2 = tf.nn.conv2d(top_relu, conv_weights2, strides=[1, 2, 2, 1], padding="SAME") - normed2 = tf.compat.v1.layers.batch_normalization(conv2) - # relu2 = tf.nn.relu(normed2) - add = tf.raw_ops.Add(x=normed, y=normed2, name="addv2") - relu = tf.nn.relu(add) - relu6 = tf.nn.relu6(relu, name="op_to_store") - - out_name = relu6.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - - quantizer = Quantization("fake_yaml_1.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - - dequant_count = 0 - quantize_count = 0 - for i in output_graph.graph_def.node: - if i.op == "Dequantize": - dequant_count += 1 - if i.op == "QuantizeV2": - quantize_count += 1 - - self.assertEqual(dequant_count, 5) - self.assertEqual(quantize_count, 4) - - @disable_random() - @unittest.skipIf(version1_lt_version2(tf.version.VERSION, "2.8.0"), "Only supports tf greater 2.7.0") - def test_itex_convert_basic_gpu(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - top_relu = tf.nn.relu(x) - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(top_relu, paddings, "CONSTANT") - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv2d(x_pad, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - normed = tf.compat.v1.layers.batch_normalization(conv) - conv_weights2 = tf.compat.v1.get_variable( - "weight2", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv2 = tf.nn.conv2d(top_relu, conv_weights2, strides=[1, 2, 2, 1], padding="SAME") - normed2 = tf.compat.v1.layers.batch_normalization(conv2) - add = tf.raw_ops.Add(x=normed, y=normed2, name="addv2") - relu = tf.nn.relu(add) - relu6 = tf.nn.relu6(relu, name="op_to_store") - out_name = relu6.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - - quantizer = Quantization("fake_yaml_2.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - - dequant_count = 0 - quantize_count = 0 - for i in output_graph.graph_def.node: - if i.op == "HostConst": - self.assertTrue("min" in i.name or "max" in i.name) - if i.op == "Dequantize": - dequant_count += 1 - if i.op == "QuantizeV2": - quantize_count += 1 - - self.assertEqual(dequant_count, 5) - self.assertEqual(quantize_count, 4) - - @disable_random() - @unittest.skipIf(version1_lt_version2(tf.version.VERSION, "2.8.0"), "Only supports tf greater 2.7.0") - def test_depthwiseconv2d_case(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.depthwise_conv2d(x, conv_weights, strides=[1, 1, 1, 1], padding="VALID") - out_name = conv.name.split(":")[0] - - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml_1.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - reshape_counter = 0 - - for i in output_graph.graph_def.node: - if i.op == "Reshape": - reshape_counter += 1 - self.assertEqual(reshape_counter, 2) - - @disable_random() - @unittest.skipIf( - version1_lt_version2(tf.version.VERSION, "2.8.0") or platform.system().lower() == "windows", - "Only supports tf greater 2.7.0 and Linux", - ) - def test_itex_benchmark_gpu(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - top_relu = tf.nn.relu(x) - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(top_relu, paddings, "CONSTANT") - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv2d(x_pad, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - normed = tf.compat.v1.layers.batch_normalization(conv) - conv_weights2 = tf.compat.v1.get_variable( - "weight2", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv2 = tf.nn.conv2d(top_relu, conv_weights2, strides=[1, 2, 2, 1], padding="SAME") - normed2 = tf.compat.v1.layers.batch_normalization(conv2) - add = tf.raw_ops.Add(x=normed, y=normed2, name="addv2") - relu = tf.nn.relu(add) - relu6 = tf.nn.relu6(relu, name="op_to_store") - out_name = relu6.name.split(":")[0] - num_of_instance = 1 - cores_per_instance = 1 - log_file = "" - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - - quantizer = Quantization("fake_yaml_2.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - - evaluator = Benchmark("fake_yaml_2.yaml") - evaluator.b_dataloader = common.DataLoader(dataset) - num_of_instance = evaluator.conf.usr_cfg.evaluation.performance.configs.num_of_instance - cores_per_instance = evaluator.conf.usr_cfg.evaluation.performance.configs.cores_per_instance - log_file = "{}_{}_{}.log".format(num_of_instance, cores_per_instance, 0) - if gfile.Exists(log_file): - os.remove(log_file) - evaluator.model = output_graph - evaluator("performance") - - found_multi_instance_log = False - for file_name in os.listdir(os.getcwd()): - if file_name == log_file: - found_multi_instance_log = True - break - - self.assertEqual(found_multi_instance_log, False) - - @disable_random() - @unittest.skipIf(version1_lt_version2(tf.version.VERSION, "2.8.0"), "Only supports tf greater 2.7.0") - def test_itex_convert_shared_y_pattern_normal_case(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - top_relu = tf.nn.relu(x) - - conv_weights2 = tf.compat.v1.get_variable( - "weight2", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv2 = tf.nn.conv2d(top_relu, conv_weights2, strides=[1, 1, 1, 1], padding="SAME") - normed2 = tf.compat.v1.layers.batch_normalization(conv2) - # relu2 = tf.nn.relu(normed2) - add = tf.raw_ops.Add(x=top_relu, y=normed2, name="addv2") - relu = tf.nn.relu(add) - relu6 = tf.nn.relu6(relu, name="op_to_store") - - out_name = relu6.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - - quantizer = Quantization("fake_yaml_1.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - - dequant_count = 0 - quantize_count = 0 - for i in output_graph.graph_def.node: - if i.op == "Dequantize": - dequant_count += 1 - if i.op == "QuantizeV2": - quantize_count += 1 - - self.assertEqual(dequant_count, 2) - self.assertEqual(quantize_count, 2) - - @disable_random() - @unittest.skipIf(version1_lt_version2(tf.version.VERSION, "2.8.0"), "Only supports tf greater 2.7.0") - def test_itex_convert_share_y_pattern_abnormal_case1(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - top_relu = tf.nn.relu(x) - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(top_relu, paddings, "CONSTANT") - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv2d(x_pad, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - normed = tf.compat.v1.layers.batch_normalization(conv) - relu = tf.nn.relu(normed) - - conv_weights2 = tf.compat.v1.get_variable( - "weight2", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv2 = tf.nn.conv2d(top_relu, conv_weights2, strides=[1, 2, 2, 1], padding="SAME") - normed2 = tf.compat.v1.layers.batch_normalization(conv2) - relu2 = tf.nn.relu(normed2) - add = tf.raw_ops.Add(x=relu, y=relu2, name="addv2") - relu = tf.nn.relu(add) - relu6 = tf.nn.relu6(relu, name="op_to_store") - - out_name = relu6.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - - quantizer = Quantization("fake_yaml_1.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - - dequant_count = 0 - quantize_count = 0 - for i in output_graph.graph_def.node: - if i.op == "Dequantize": - dequant_count += 1 - if i.op == "QuantizeV2": - quantize_count += 1 - - self.assertEqual(dequant_count, 4) - self.assertEqual(quantize_count, 3) - - @disable_random() - @unittest.skipIf(version1_lt_version2(tf.version.VERSION, "2.8.0"), "Only supports tf greater 2.7.0") - def test_itex_convert_share_y_pattern_abnormal_case2(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - top_relu = tf.nn.relu(x) - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(top_relu, paddings, "CONSTANT") - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv2d(x_pad, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - normed = tf.compat.v1.layers.batch_normalization(conv) - relu = tf.nn.relu(normed) - relu6 = tf.nn.relu6(relu, name="op_to_store") - - out_name = relu6.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - - quantizer = Quantization("fake_yaml_1.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - - dequant_count = 0 - quantize_count = 0 - for i in output_graph.graph_def.node: - if i.op == "Dequantize": - dequant_count += 1 - if i.op == "QuantizeV2": - quantize_count += 1 - - self.assertEqual(dequant_count, 2) - self.assertEqual(quantize_count, 2) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/itex/test_tensorflow_qdq_convert_to_onnx_qdq.py b/test/itex/test_tensorflow_qdq_convert_to_onnx_qdq.py index d9e35afc356..0bc8b94bb8c 100644 --- a/test/itex/test_tensorflow_qdq_convert_to_onnx_qdq.py +++ b/test/itex/test_tensorflow_qdq_convert_to_onnx_qdq.py @@ -1,59 +1,19 @@ -# -# -*- coding: utf-8 -*- -# -import os import shutil import unittest import tensorflow as tf -import yaml from tensorflow.compat.v1 import graph_util from neural_compressor.adaptor.tf_utils.util import disable_random, version1_gte_version2, version1_lt_version2 -from neural_compressor.experimental import Benchmark, Quantization, common - - -def build_fake_yaml(fake_yaml, save_path, **kwargs): - y = yaml.load(fake_yaml, Loader=yaml.SafeLoader) - with open(file=save_path, mode=kwargs["mode"], encoding=kwargs["encoding"]) as f: - yaml.dump(y, f) class TestConvertTensorflowQDQToOnnxQDQ(unittest.TestCase): @classmethod def setUpClass(self): - fake_yaml = """ - model: - name: fake_model_cpu - framework: tensorflow_itex - inputs: input - device: cpu - quantization: - model_wise: - weight: - granularity: per_tensor - scheme: sym - dtype: int8 - algorithm: minmax - evaluation: - accuracy: - metric: - topk: 1 - tuning: - strategy: - name: basic - accuracy_criterion: - relative: 0.1 - exit_policy: - performance_only: True - workspace: - path: workspace - """ - build_fake_yaml(fake_yaml, "fake_yaml.yaml", mode="w", encoding="utf-8") + pass @classmethod def tearDownClass(self): - os.remove("fake_yaml.yaml") if version1_gte_version2(tf.version.VERSION, "2.8.0"): shutil.rmtree("workspace") diff --git a/test/metric/test_exp_metrics.py b/test/metric/test_exp_metrics.py deleted file mode 100644 index de6eb66c83e..00000000000 --- a/test/metric/test_exp_metrics.py +++ /dev/null @@ -1,1291 +0,0 @@ -"""Tests for the metrics module.""" - -import platform -import unittest - -import numpy as np - -from neural_compressor.experimental.metric import METRICS -from neural_compressor.experimental.metric.evaluate_squad import evaluate as evaluate_squad -from neural_compressor.experimental.metric.f1 import evaluate - - -class InCorrectMetric: - def __init__(self): - self.item = None - - -class CorrectMetric: - def __init__(self): - self.item = [] - - def update(self, samples): - self.item.append(samples) - - def result(self): - return 0 - - def reset(self): - self.item = [] - - -class CorrectMetric_v2: - def __init__(self): - self.item = [] - - def update(self, labels, preds): - self.item.append(preds) - - def result(self): - return "res", 0 - - def reset(self): - self.item = [] - - -class TestMetrics(unittest.TestCase): - def testUserMetric(self): - from neural_compressor.experimental import Benchmark, Graph_Optimization, Quantization, common - - for i in [Quantization(), Benchmark(), Graph_Optimization()]: - item = i - with self.assertRaises(AssertionError): - item.metric = InCorrectMetric() - item.framework = "tensorflow" - item.metric = common.Metric(CorrectMetric, str(i)) - - def testmIOU(self): - metrics = METRICS("tensorflow") - miou = metrics["mIOU"]() - preds = np.array([0, 0, 1, 1]) - labels = np.array([0, 1, 0, 1]) - miou.update(preds, labels) - self.assertAlmostEqual(miou.result(), 0.33333334) - - miou.reset() - preds = np.array([0, 0, 1, 1]) - labels = np.array([0, 1, 1, 1]) - miou.update(preds, labels) - self.assertAlmostEqual(miou.result(), 0.58333333) - - def testBLEU(self): - metrics = METRICS("tensorflow") - bleu = metrics["BLEU"]() - preds = ["Gutach: Mehr Sicherheit für Fußgänger"] - labels = ("Gutach: Noch mehr Sicherheit für Fußgänger",) - bleu.update(preds, labels) - self.assertAlmostEqual(bleu.result(), 51.1507809) - bleu.reset() - - preds = ["Dies wurde auch von Peter Arnold vom Offenburg District Office bestätigt."] - labels = ("Dies bestätigt auch Peter Arnold vom Landratsamt Offenburg.",) - bleu.update(preds, labels) - self.assertAlmostEqual(bleu.result(), 16.108992695) - with self.assertRaises(ValueError): - bleu.update(["a", "b"], ("c",)) - - def test_onnxrt_GLUE(self): - metrics = METRICS("onnxrt_qlinearops") - glue = metrics["GLUE"]("mrpc") - preds = [ - np.array( - [ - [-3.2443411, 3.0909934], - [2.0500996, -2.3100944], - [1.870293, -2.0741048], - [-2.8377204, 2.617834], - [2.008347, -2.0215416], - [-2.9693947, 2.7782154], - [-2.9949608, 2.7887983], - [-3.0623112, 2.8748074], - ] - ) - ] - labels = [np.array([1, 0, 0, 1, 0, 1, 0, 1])] - glue.update(preds, labels) - self.assertEqual(glue.result(), 0.875) - preds_2 = [ - np.array( - [ - [-3.1296735, 2.8356276], - [-3.172515, 2.9173899], - [-3.220131, 3.0916846], - [2.1452675, -1.9398905], - [1.5475761, -1.9101546], - [-2.9797182, 2.721741], - [-3.2052834, 2.9934788], - [-2.7451005, 2.622343], - ] - ) - ] - labels_2 = [np.array([1, 1, 1, 0, 0, 1, 1, 1])] - glue.update(preds_2, labels_2) - self.assertEqual(glue.result(), 0.9375) - - glue.reset() - glue.update(preds, labels) - self.assertEqual(glue.result(), 0.875) - - def test_tensorflow_F1(self): - metrics = METRICS("tensorflow") - F1 = metrics["F1"]() - preds = [1, 1, 1, 1] - labels = [0, 1, 1, 0] - - F1.update(preds, labels) - self.assertEqual(F1.result(), 0.5) - - def test_squad_evaluate(self): - label = [ - { - "paragraphs": [ - { - "qas": [ - { - "answers": [ - {"answer_start": 177, "text": "Denver Broncos"}, - {"answer_start": 177, "text": "Denver Broncos"}, - {"answer_start": 177, "text": "Denver Broncos"}, - ], - "question": "Which NFL team represented the AFC at Super Bowl 50?", - "id": "56be4db0acb8001400a502ec", - } - ] - } - ] - } - ] - preds = {"56be4db0acb8001400a502ec": "Denver Broncos"} - f1 = evaluate(preds, label) - self.assertEqual(f1, 100.0) - dataset = [ - { - "paragraphs": [ - { - "qas": [ - { - "answers": [ - {"answer_start": 177, "text": "Denver Broncos"}, - {"answer_start": 177, "text": "Denver Broncos"}, - {"answer_start": 177, "text": "Denver Broncos"}, - ], - "question": "Which NFL team represented the AFC at Super Bowl 50?", - "id": "56be4db0acb8001400a502ec", - } - ] - } - ] - } - ] - predictions = {"56be4db0acb8001400a502ec": "Denver Broncos"} - f1_squad = evaluate_squad(dataset, predictions) - self.assertEqual(f1_squad["f1"], 100.0) - self.assertEqual(f1_squad["exact_match"], 100.0) - - def test_pytorch_F1(self): - metrics = METRICS("pytorch") - F1 = metrics["F1"]() - F1.reset() - preds = [1, 1] - labels = [2, 1, 1] - - F1.update(preds, labels) - self.assertEqual(F1.result(), 0.8) - - @unittest.skipIf(platform.system().lower() == "windows", "not support mxnet on windows yet") - def test_mxnet_F1(self): - metrics = METRICS("mxnet") - F1 = metrics["F1"]() - preds = [0, 1, 1, 1, 1, 0] - labels = [0, 1, 1, 1] - - F1.update(preds, labels) - self.assertEqual(F1.result(), 0.8) - - def test_onnx_topk(self): - metrics = METRICS("onnxrt_qlinearops") - top1 = metrics["topk"]() - top1.reset() - self.assertEqual(top1.result(), 0) - self.assertEqual(top1.result(), 0) - top2 = metrics["topk"](k=2) - top3 = metrics["topk"](k=3) - - predicts = [[0, 0.2, 0.9, 0.3], [0, 0.9, 0.8, 0]] - single_predict = [0, 0.2, 0.9, 0.3] - - labels = [[0, 1, 0, 0], [0, 0, 1, 0]] - sparse_labels = [2, 2] - single_label = 2 - - # test functionality of one-hot label - top1.update(predicts, labels) - top2.update(predicts, labels) - top3.update(predicts, labels) - self.assertEqual(top1.result(), 0.0) - self.assertEqual(top2.result(), 0.5) - self.assertEqual(top3.result(), 1) - - # test functionality of sparse label - top1.update(predicts, sparse_labels) - top2.update(predicts, sparse_labels) - top3.update(predicts, sparse_labels) - self.assertEqual(top1.result(), 0.25) - self.assertEqual(top2.result(), 0.75) - self.assertEqual(top3.result(), 1) - - # test functionality of single label - top1.update(single_predict, single_label) - top2.update(single_predict, single_label) - top3.update(single_predict, single_label) - self.assertEqual(top1.result(), 0.4) - self.assertEqual(top2.result(), 0.8) - self.assertEqual(top3.result(), 1) - - @unittest.skipIf(platform.system().lower() == "windows", "not support mxnet on windows yet") - def test_mxnet_topk(self): - metrics = METRICS("mxnet") - top1 = metrics["topk"]() - top1.reset() - self.assertEqual(top1.result(), 0) - top2 = metrics["topk"](k=2) - top3 = metrics["topk"](k=3) - - predicts = [[0, 0.2, 0.9, 0.3], [0, 0.9, 0.8, 0]] - single_predict = [0, 0.2, 0.9, 0.3] - - labels = [[0, 1, 0, 0], [0, 0, 1, 0]] - sparse_labels = [2, 2] - single_label = 2 - - # test functionality of one-hot label - top1.update(predicts, labels) - top2.update(predicts, labels) - top3.update(predicts, labels) - self.assertEqual(top1.result(), 0.0) - self.assertEqual(top2.result(), 0.5) - self.assertEqual(top3.result(), 1) - - # test functionality of sparse label - top1.update(predicts, sparse_labels) - top2.update(predicts, sparse_labels) - top3.update(predicts, sparse_labels) - self.assertEqual(top1.result(), 0.25) - self.assertEqual(top2.result(), 0.75) - self.assertEqual(top3.result(), 1) - - # test functionality of single label - top1.update(single_predict, single_label) - top2.update(single_predict, single_label) - top3.update(single_predict, single_label) - self.assertEqual(top1.result(), 0.4) - self.assertEqual(top2.result(), 0.8) - self.assertEqual(top3.result(), 1) - - def test_tensorflow_topk(self): - metrics = METRICS("tensorflow") - top1 = metrics["topk"]() - top1.reset() - self.assertEqual(top1.result(), 0) - top2 = metrics["topk"](k=2) - top3 = metrics["topk"](k=3) - - predicts = [[0, 0.2, 0.9, 0.3], [0, 0.9, 0.8, 0]] - single_predict = [0, 0.2, 0.9, 0.3] - int_predict = 0 - ndarry_predict = np.array([[0, 0.2, 0.9, 0.3], [0, 0.9, 0.8, 0]]) - - labels = [[0, 1, 0, 0], [0, 0, 1, 0]] - sparse_labels = [2, 2] - single_label = 2 - tuple_label = tuple( - [ - [0, 1], - [ - 0, - 0, - ], - ] - ) - list_tuple_label = [ - tuple( - [ - [0, 1], - [ - 0, - 0, - ], - ] - ) - ] - - # test functionality of one-hot label - top1.update(predicts, labels) - top2.update(predicts, labels) - top3.update(predicts, labels) - self.assertEqual(top1.result(), 0.0) - self.assertEqual(top2.result(), 0.5) - self.assertEqual(top3.result(), 1) - - # test functionality of sparse label - top1.update(predicts, sparse_labels) - top2.update(predicts, sparse_labels) - top3.update(predicts, sparse_labels) - self.assertEqual(top1.result(), 0.25) - self.assertEqual(top2.result(), 0.75) - self.assertEqual(top3.result(), 1) - - # test functionality of single label - top1.update(single_predict, single_label) - top2.update(single_predict, single_label) - top3.update(single_predict, single_label) - self.assertEqual(top1.result(), 0.4) - self.assertEqual(top2.result(), 0.8) - self.assertEqual(top3.result(), 1) - - # test functionality of int pred and label - top1.reset() - top1.update(int_predict, single_label) - self.assertEqual(top1.result(), 0) - - # test functionality of ndarray pred and tuple label - top1.reset() - top1.update(ndarry_predict, tuple_label) - self.assertEqual(top1.result(), 0.5) - - # test functionality of ndarray pred and tuple label - top1.reset() - top1.update(ndarry_predict, list_tuple_label) - self.assertEqual(top1.result(), 0.5) - - def test_tensorflow_mAP(self): - import json - import os - - metrics = METRICS("tensorflow") - fake_dict = "dog: 1" - with open("anno.yaml", "w", encoding="utf-8") as f: - f.write(fake_dict) - mAP = metrics["mAP"]("anno.yaml") - self.assertEqual(mAP.category_map_reverse["dog"], 1) - detection = [ - np.array([[5]]), - np.array([[5]]), - np.array( - [ - [ - [0.16117382, 0.59801614, 0.81511605, 0.7858219], - [0.5589304, 0.0, 0.98301625, 0.520178], - [0.62706745, 0.35748824, 0.6892729, 0.41513762], - [0.40032804, 0.01218696, 0.6924763, 0.30341768], - [0.62706745, 0.35748824, 0.6892729, 0.41513762], - ] - ] - ), - np.array([[0.9267181, 0.8510787, 0.60418576, 0.35155892, 0.31158054]]), - np.array([[1.0, 67.0, 51.0, 79.0, 47.0]]), - ] - ground_truth = [ - np.array([[[0.5633255, 0.34003124, 0.69857144, 0.4009531], [0.4763466, 0.7769531, 0.54334897, 0.9675937]]]), - np.array([["a", "b"]]), - np.array([[]]), - np.array([b"000000397133.jpg"]), - ] - self.assertRaises(ValueError, mAP.update, detection, ground_truth) - - detection = [ - np.array( - [[[0.16117382, 0.59801614, 0.81511605, 0.7858219], [0.62706745, 0.35748824, 0.6892729, 0.41513762]]] - ), - np.array([[0.9267181, 0.8510787]]), - np.array([[1.0, 1.0]]), - ] - ground_truth = [ - np.array( - [[[0.16117382, 0.59801614, 0.81511605, 0.7858219], [0.62706745, 0.35748824, 0.6892729, 0.41513762]]] - ), - np.array([[b"dog", b"dog"]]), - np.array([[]]), - np.array([b"000000397133.jpg"]), - ] - mAP.update(detection, ground_truth) - mAP.result() - self.assertEqual(format(mAP.result(), ".5f"), "1.00000") - - detection = [ - np.array( - [ - [ - [0.16117382, 0.59801614, 0.81511605, 0.7858219], - [0.5589304, 0.0, 0.98301625, 0.520178], - [0.62706745, 0.35748824, 0.6892729, 0.41513762], - [0.40032804, 0.01218696, 0.6924763, 0.30341768], - [0.62706745, 0.35748824, 0.6892729, 0.41513762], - ] - ] - ), - np.array([[0.9267181, 0.8510787, 0.60418576, 0.35155892, 0.31158054]]), - np.array([[1.0, 67.0, 51.0, 79.0, 47.0]]), - ] - detection_2 = [ - np.array([[8]]), - np.array( - [ - [ - [0.82776225, 0.5865939, 0.8927653, 0.6302338], - [0.8375764, 0.6424138, 0.9055594, 0.6921875], - [0.57902956, 0.39394334, 0.8342961, 0.5577197], - [0.7949219, 0.6513021, 0.8472295, 0.68427753], - [0.809729, 0.5947042, 0.8539927, 0.62916476], - [0.7258591, 0.08907133, 1.0, 0.86224866], - [0.43100086, 0.37782395, 0.8384069, 0.5616918], - [0.32005906, 0.84334356, 1.0, 1.0], - ] - ] - ), - np.array([[0.86698544, 0.7562499, 0.66414887, 0.64498234, 0.63083494, 0.46618757, 0.3914739, 0.3094324]]), - np.array([[55.0, 55.0, 79.0, 55.0, 55.0, 67.0, 79.0, 82.0]]), - ] - ground_truth = [ - np.array( - [ - [ - [0.5633255, 0.34003124, 0.69857144, 0.4009531], - [0.56262296, 0.0015625, 1.0, 0.5431719], - [0.16374707, 0.60728127, 0.813911, 0.77823436], - [0.5841452, 0.21182813, 0.65156907, 0.24670312], - [0.8056206, 0.048875, 0.90124124, 0.1553125], - [0.6729742, 0.09317187, 0.7696956, 0.21203125], - [0.3848478, 0.002125, 0.61522245, 0.303], - [0.61548007, 0.0, 0.7015925, 0.097125], - [0.6381967, 0.1865625, 0.7184075, 0.22534375], - [0.6274239, 0.22104688, 0.71140516, 0.27134374], - [0.39566743, 0.24370313, 0.43578455, 0.284375], - [0.2673302, 0.245625, 0.3043794, 0.27353126], - [0.7137705, 0.15429688, 0.726815, 0.17114063], - [0.6003747, 0.25942189, 0.6438876, 0.27320313], - [0.68845433, 0.13501562, 0.714637, 0.17245312], - [0.69358313, 0.10959375, 0.7043091, 0.12409375], - [0.493911, 0.0, 0.72571427, 0.299], - [0.69576114, 0.15107812, 0.70714283, 0.16332813], - [0.4763466, 0.7769531, 0.54334897, 0.9675937], - ] - ] - ), - np.array([[]]), - np.array([[44, 67, 1, 49, 51, 51, 79, 1, 47, 47, 51, 51, 56, 50, 56, 56, 79, 57, 81]]), - np.array([b"000000397133.jpg"]), - ] - ground_truth_2 = [ - np.array( - [ - [ - [0.51508695, 0.2911648, 0.5903478, 0.31360796], - [0.9358696, 0.07528409, 0.99891305, 0.25], - [0.8242174, 0.3309659, 0.93508697, 0.47301137], - [0.77413046, 0.22599432, 0.9858696, 0.8179261], - [0.32582608, 0.8575, 0.98426086, 0.9984659], - [0.77795655, 0.6268466, 0.89930433, 0.73434657], - [0.5396087, 0.39053977, 0.8483913, 0.5615057], - [0.58473915, 0.75661933, 0.5998261, 0.83579546], - [0.80391306, 0.6129829, 0.8733478, 0.66201705], - [0.8737391, 0.6579546, 0.943, 0.7053693], - [0.775, 0.6549716, 0.8227391, 0.6882955], - [0.8130869, 0.58292615, 0.90526086, 0.62551135], - [0.7844348, 0.68735796, 0.98182607, 0.83329546], - [0.872, 0.6190057, 0.9306522, 0.6591761], - ] - ] - ), - np.array([[]]), - np.array([[64, 62, 62, 67, 82, 52, 79, 81, 55, 55, 55, 55, 62, 55]]), - np.array([b"000000037777.jpg"]), - ] - - mAP = metrics["mAP"]() - - self.assertEqual(mAP.result(), 0) - - mAP.update(detection, ground_truth) - - mAP.update(detection, ground_truth) - self.assertEqual(format(mAP.result(), ".5f"), "0.18182") - - mAP.update(detection_2, ground_truth_2) - self.assertEqual(format(mAP.result(), ".5f"), "0.20347") - mAP.reset() - mAP.update(detection, ground_truth) - self.assertEqual(format(mAP.result(), ".5f"), "0.18182") - - ground_truth_1 = [ - np.array([[[0.51508695, 0.2911648, 0.5903478, 0.31360796], [0.872, 0.6190057, 0.9306522, 0.6591761]]]), - np.array([[]]), - np.array([[[64, 62]]]), - np.array([b"000000037777.jpg"]), - ] - self.assertRaises(ValueError, mAP.update, detection, ground_truth_1) - ground_truth_2 = [ - np.array([[[0.51508695, 0.2911648, 0.5903478, 0.31360796], [0.872, 0.6190057, 0.9306522, 0.6591761]]]), - np.array([[]]), - np.array([[64]]), - np.array([b"000000037700.jpg"]), - ] - self.assertRaises(ValueError, mAP.update, detection, ground_truth_2) - detection_1 = [ - np.array([[[0.16117382, 0.59801614, 0.81511605, 0.7858219], [0.5589304, 0.0, 0.98301625, 0.520178]]]), - np.array([[0.9267181, 0.8510787, 0.60418576, 0.35155892, 0.31158054]]), - np.array([[1.0, 67.0, 51.0, 79.0, 47.0]]), - ] - ground_truth_1 = [ - np.array([[[0.51508695, 0.2911648, 0.5903478, 0.31360796], [0.872, 0.6190057, 0.9306522, 0.6591761]]]), - np.array([[]]), - np.array([[64, 62]]), - np.array([b"000000011.jpg"]), - ] - self.assertRaises(ValueError, mAP.update, detection_1, ground_truth_1) - ground_truth_2 = [ - np.array([[[0.51508695, 0.2911648, 0.5903478, 0.31360796], [0.872, 0.6190057, 0.9306522, 0.6591761]]]), - np.array([[]]), - np.array([[64, 62]]), - np.array([b"000000012.jpg"]), - ] - detection_2 = [ - np.array([[[0.16117382, 0.59801614, 0.81511605, 0.7858219], [0.5589304, 0.0, 0.98301625, 0.520178]]]), - np.array([[0.9267181, 0.8510787]]), - np.array([[1.0, 67.0, 51.0, 79.0, 47.0]]), - ] - self.assertRaises(ValueError, mAP.update, detection_2, ground_truth_2) - os.remove("anno.yaml") - - def test_tensorflow_VOCmAP(self): - import os - - metrics = METRICS("tensorflow") - fake_dict = "dog: 1" - with open("anno.yaml", "w", encoding="utf-8") as f: - f.write(fake_dict) - mAP = metrics["VOCmAP"]("anno.yaml") - self.assertEqual(mAP.iou_thrs, 0.5) - self.assertEqual(mAP.map_points, 0) - self.assertEqual(mAP.category_map_reverse["dog"], 1) - detection = [ - np.array([[5]]), - np.array([[5]]), - np.array( - [ - [ - [0.16117382, 0.59801614, 0.81511605, 0.7858219], - [0.5589304, 0.0, 0.98301625, 0.520178], - [0.62706745, 0.35748824, 0.6892729, 0.41513762], - [0.40032804, 0.01218696, 0.6924763, 0.30341768], - [0.62706745, 0.35748824, 0.6892729, 0.41513762], - ] - ] - ), - np.array([[0.9267181, 0.8510787, 0.60418576, 0.35155892, 0.31158054]]), - np.array([[1.0, 67.0, 51.0, 79.0, 47.0]]), - ] - ground_truth = [ - np.array([[[0.5633255, 0.34003124, 0.69857144, 0.4009531], [0.4763466, 0.7769531, 0.54334897, 0.9675937]]]), - np.array([["a", "b"]]), - np.array([[]]), - np.array([b"000000397133.jpg"]), - ] - self.assertRaises(ValueError, mAP.update, detection, ground_truth) - - os.remove("anno.yaml") - - mAP = metrics["VOCmAP"]() - detection = [ - np.array( - [ - [ - [0.16117382, 0.59801614, 0.81511605, 0.7858219], - [0.5589304, 0.0, 0.98301625, 0.520178], - [0.62706745, 0.35748824, 0.6892729, 0.41513762], - [0.40032804, 0.01218696, 0.6924763, 0.30341768], - [0.62706745, 0.35748824, 0.6892729, 0.41513762], - ] - ] - ), - np.array([[0.9267181, 0.8510787, 0.60418576, 0.35155892, 0.31158054]]), - np.array([[1.0, 67.0, 51.0, 79.0, 47.0]]), - ] - detection_2 = [ - np.array([[8]]), - np.array( - [ - [ - [0.82776225, 0.5865939, 0.8927653, 0.6302338], - [0.8375764, 0.6424138, 0.9055594, 0.6921875], - [0.57902956, 0.39394334, 0.8342961, 0.5577197], - [0.7949219, 0.6513021, 0.8472295, 0.68427753], - [0.809729, 0.5947042, 0.8539927, 0.62916476], - [0.7258591, 0.08907133, 1.0, 0.86224866], - [0.43100086, 0.37782395, 0.8384069, 0.5616918], - [0.32005906, 0.84334356, 1.0, 1.0], - ] - ] - ), - np.array([[0.86698544, 0.7562499, 0.66414887, 0.64498234, 0.63083494, 0.46618757, 0.3914739, 0.3094324]]), - np.array([[55.0, 55.0, 79.0, 55.0, 55.0, 67.0, 79.0, 82.0]]), - ] - ground_truth = [ - np.array( - [ - [ - [0.5633255, 0.34003124, 0.69857144, 0.4009531], - [0.56262296, 0.0015625, 1.0, 0.5431719], - [0.16374707, 0.60728127, 0.813911, 0.77823436], - [0.5841452, 0.21182813, 0.65156907, 0.24670312], - [0.8056206, 0.048875, 0.90124124, 0.1553125], - [0.6729742, 0.09317187, 0.7696956, 0.21203125], - [0.3848478, 0.002125, 0.61522245, 0.303], - [0.61548007, 0.0, 0.7015925, 0.097125], - [0.6381967, 0.1865625, 0.7184075, 0.22534375], - [0.6274239, 0.22104688, 0.71140516, 0.27134374], - [0.39566743, 0.24370313, 0.43578455, 0.284375], - [0.2673302, 0.245625, 0.3043794, 0.27353126], - [0.7137705, 0.15429688, 0.726815, 0.17114063], - [0.6003747, 0.25942189, 0.6438876, 0.27320313], - [0.68845433, 0.13501562, 0.714637, 0.17245312], - [0.69358313, 0.10959375, 0.7043091, 0.12409375], - [0.493911, 0.0, 0.72571427, 0.299], - [0.69576114, 0.15107812, 0.70714283, 0.16332813], - [0.4763466, 0.7769531, 0.54334897, 0.9675937], - ] - ] - ), - np.array([[]]), - np.array([[44, 67, 1, 49, 51, 51, 79, 1, 47, 47, 51, 51, 56, 50, 56, 56, 79, 57, 81]]), - np.array([b"000000397133.jpg"]), - ] - ground_truth_2 = [ - np.array( - [ - [ - [0.51508695, 0.2911648, 0.5903478, 0.31360796], - [0.9358696, 0.07528409, 0.99891305, 0.25], - [0.8242174, 0.3309659, 0.93508697, 0.47301137], - [0.77413046, 0.22599432, 0.9858696, 0.8179261], - [0.32582608, 0.8575, 0.98426086, 0.9984659], - [0.77795655, 0.6268466, 0.89930433, 0.73434657], - [0.5396087, 0.39053977, 0.8483913, 0.5615057], - [0.58473915, 0.75661933, 0.5998261, 0.83579546], - [0.80391306, 0.6129829, 0.8733478, 0.66201705], - [0.8737391, 0.6579546, 0.943, 0.7053693], - [0.775, 0.6549716, 0.8227391, 0.6882955], - [0.8130869, 0.58292615, 0.90526086, 0.62551135], - [0.7844348, 0.68735796, 0.98182607, 0.83329546], - [0.872, 0.6190057, 0.9306522, 0.6591761], - ] - ] - ), - np.array([[]]), - np.array([[64, 62, 62, 67, 82, 52, 79, 81, 55, 55, 55, 55, 62, 55]]), - np.array([b"000000037777.jpg"]), - ] - - self.assertEqual(mAP.result(), 0) - - mAP.update(detection, ground_truth) - - mAP.update(detection, ground_truth) - self.assertEqual(format(mAP.result(), ".5f"), "0.18182") - - mAP.update(detection_2, ground_truth_2) - self.assertEqual(format(mAP.result(), ".5f"), "0.20347") - mAP.reset() - mAP.update(detection, ground_truth) - self.assertEqual(format(mAP.result(), ".5f"), "0.18182") - - ground_truth_1 = [ - np.array([[[0.51508695, 0.2911648, 0.5903478, 0.31360796], [0.872, 0.6190057, 0.9306522, 0.6591761]]]), - np.array([[]]), - np.array([[[64, 62]]]), - np.array([b"000000037777.jpg"]), - ] - self.assertRaises(ValueError, mAP.update, detection, ground_truth_1) - ground_truth_2 = [ - np.array([[[0.51508695, 0.2911648, 0.5903478, 0.31360796], [0.872, 0.6190057, 0.9306522, 0.6591761]]]), - np.array([[]]), - np.array([[64]]), - np.array([b"000000037700.jpg"]), - ] - self.assertRaises(ValueError, mAP.update, detection, ground_truth_2) - detection_1 = [ - np.array([[[0.16117382, 0.59801614, 0.81511605, 0.7858219], [0.5589304, 0.0, 0.98301625, 0.520178]]]), - np.array([[0.9267181, 0.8510787, 0.60418576, 0.35155892, 0.31158054]]), - np.array([[1.0, 67.0, 51.0, 79.0, 47.0]]), - ] - ground_truth_1 = [ - np.array([[[0.51508695, 0.2911648, 0.5903478, 0.31360796], [0.872, 0.6190057, 0.9306522, 0.6591761]]]), - np.array([[]]), - np.array([[64, 62]]), - np.array([b"000000011.jpg"]), - ] - self.assertRaises(ValueError, mAP.update, detection_1, ground_truth_1) - ground_truth_2 = [ - np.array([[[0.51508695, 0.2911648, 0.5903478, 0.31360796], [0.872, 0.6190057, 0.9306522, 0.6591761]]]), - np.array([[]]), - np.array([[64, 62]]), - np.array([b"000000012.jpg"]), - ] - detection_2 = [ - np.array([[[0.16117382, 0.59801614, 0.81511605, 0.7858219], [0.5589304, 0.0, 0.98301625, 0.520178]]]), - np.array([[0.9267181, 0.8510787]]), - np.array([[1.0, 67.0, 51.0, 79.0, 47.0]]), - ] - self.assertRaises(ValueError, mAP.update, detection_2, ground_truth_2) - - def test_tensorflow_COCOmAP(self): - import os - - output_index_mapping = {"num_detections": 0, "boxes": 1, "scores": 2, "classes": 3} - metrics = METRICS("tensorflow") - fake_dict = "dog: 1" - with open("anno.yaml", "w", encoding="utf-8") as f: - f.write(fake_dict) - mAP = metrics["COCOmAP"]("anno.yaml") - mAP2 = metrics["COCOmAPv2"]("anno.yaml", output_index_mapping=output_index_mapping) - self.assertEqual(mAP.category_map_reverse["dog"], 1) - self.assertEqual(mAP2.category_map_reverse["dog"], 1) - detection = [ - np.array([[5]]), - np.array([[5]]), - np.array( - [ - [ - [0.16117382, 0.59801614, 0.81511605, 0.7858219], - [0.5589304, 0.0, 0.98301625, 0.520178], - [0.62706745, 0.35748824, 0.6892729, 0.41513762], - [0.40032804, 0.01218696, 0.6924763, 0.30341768], - [0.62706745, 0.35748824, 0.6892729, 0.41513762], - ] - ] - ), - np.array([[0.9267181, 0.8510787, 0.60418576, 0.35155892, 0.31158054]]), - np.array([[1.0, 67.0, 51.0, 79.0, 47.0]]), - ] - ground_truth = [ - np.array([[[0.5633255, 0.34003124, 0.69857144, 0.4009531], [0.4763466, 0.7769531, 0.54334897, 0.9675937]]]), - np.array([["a", "b"]]), - np.array([[]]), - np.array([b"000000397133.jpg"]), - ] - self.assertRaises(ValueError, mAP.update, detection, ground_truth) - - os.remove("anno.yaml") - - mAP = metrics["COCOmAP"]() - mAP2 = metrics["COCOmAPv2"]() - detection = [ - np.array( - [ - [ - [0.16117382, 0.59801614, 0.81511605, 0.7858219], - [0.5589304, 0.0, 0.98301625, 0.520178], - [0.62706745, 0.35748824, 0.6892729, 0.41513762], - [0.40032804, 0.01218696, 0.6924763, 0.30341768], - [0.62706745, 0.35748824, 0.6892729, 0.41513762], - ] - ] - ), - np.array([[0.9267181, 0.8510787, 0.60418576, 0.35155892, 0.31158054]]), - np.array([[1.0, 67.0, 51.0, 79.0, 47.0]]), - ] - detection_2 = [ - np.array([[8]]), - np.array( - [ - [ - [0.82776225, 0.5865939, 0.8927653, 0.6302338], - [0.8375764, 0.6424138, 0.9055594, 0.6921875], - [0.57902956, 0.39394334, 0.8342961, 0.5577197], - [0.7949219, 0.6513021, 0.8472295, 0.68427753], - [0.809729, 0.5947042, 0.8539927, 0.62916476], - [0.7258591, 0.08907133, 1.0, 0.86224866], - [0.43100086, 0.37782395, 0.8384069, 0.5616918], - [0.32005906, 0.84334356, 1.0, 1.0], - ] - ] - ), - np.array([[0.86698544, 0.7562499, 0.66414887, 0.64498234, 0.63083494, 0.46618757, 0.3914739, 0.3094324]]), - np.array([[55.0, 55.0, 79.0, 55.0, 55.0, 67.0, 79.0, 82.0]]), - ] - ground_truth = [ - np.array( - [ - [ - [0.5633255, 0.34003124, 0.69857144, 0.4009531], - [0.56262296, 0.0015625, 1.0, 0.5431719], - [0.16374707, 0.60728127, 0.813911, 0.77823436], - [0.5841452, 0.21182813, 0.65156907, 0.24670312], - [0.8056206, 0.048875, 0.90124124, 0.1553125], - [0.6729742, 0.09317187, 0.7696956, 0.21203125], - [0.3848478, 0.002125, 0.61522245, 0.303], - [0.61548007, 0.0, 0.7015925, 0.097125], - [0.6381967, 0.1865625, 0.7184075, 0.22534375], - [0.6274239, 0.22104688, 0.71140516, 0.27134374], - [0.39566743, 0.24370313, 0.43578455, 0.284375], - [0.2673302, 0.245625, 0.3043794, 0.27353126], - [0.7137705, 0.15429688, 0.726815, 0.17114063], - [0.6003747, 0.25942189, 0.6438876, 0.27320313], - [0.68845433, 0.13501562, 0.714637, 0.17245312], - [0.69358313, 0.10959375, 0.7043091, 0.12409375], - [0.493911, 0.0, 0.72571427, 0.299], - [0.69576114, 0.15107812, 0.70714283, 0.16332813], - [0.4763466, 0.7769531, 0.54334897, 0.9675937], - ] - ] - ), - np.array([[]]), - np.array([[44, 67, 1, 49, 51, 51, 79, 1, 47, 47, 51, 51, 56, 50, 56, 56, 79, 57, 81]]), - np.array([b"000000397133.jpg"]), - ] - ground_truth_2 = [ - np.array( - [ - [ - [0.51508695, 0.2911648, 0.5903478, 0.31360796], - [0.9358696, 0.07528409, 0.99891305, 0.25], - [0.8242174, 0.3309659, 0.93508697, 0.47301137], - [0.77413046, 0.22599432, 0.9858696, 0.8179261], - [0.32582608, 0.8575, 0.98426086, 0.9984659], - [0.77795655, 0.6268466, 0.89930433, 0.73434657], - [0.5396087, 0.39053977, 0.8483913, 0.5615057], - [0.58473915, 0.75661933, 0.5998261, 0.83579546], - [0.80391306, 0.6129829, 0.8733478, 0.66201705], - [0.8737391, 0.6579546, 0.943, 0.7053693], - [0.775, 0.6549716, 0.8227391, 0.6882955], - [0.8130869, 0.58292615, 0.90526086, 0.62551135], - [0.7844348, 0.68735796, 0.98182607, 0.83329546], - [0.872, 0.6190057, 0.9306522, 0.6591761], - ] - ] - ), - np.array([[]]), - np.array([[64, 62, 62, 67, 82, 52, 79, 81, 55, 55, 55, 55, 62, 55]]), - np.array([b"000000037777.jpg"]), - ] - - self.assertEqual(mAP.result(), 0) - self.assertEqual(mAP2.result(), 0) - - mAP.update(detection, ground_truth) - - mAP.update(detection, ground_truth) - self.assertEqual(format(mAP.result(), ".5f"), "0.14149") - - mAP.update(detection_2, ground_truth_2) - self.assertEqual(format(mAP.result(), ".5f"), "0.13366") - mAP.reset() - mAP.update(detection, ground_truth) - self.assertEqual(format(mAP.result(), ".5f"), "0.14149") - - mAP2.update(detection, ground_truth) - - mAP2.update(detection, ground_truth) - self.assertEqual(format(mAP2.result(), ".5f"), "0.14149") - - mAP2 = metrics["COCOmAPv2"](output_index_mapping=output_index_mapping) - - mAP2.update(detection_2, ground_truth_2) - self.assertEqual(format(mAP2.result(), ".5f"), "0.20520") - mAP2.reset() - mAP2.update(detection_2, ground_truth_2) - self.assertEqual(format(mAP2.result(), ".5f"), "0.20520") - - mAP2 = metrics["COCOmAPv2"]() - - ground_truth_1 = [ - np.array([[[0.51508695, 0.2911648, 0.5903478, 0.31360796], [0.872, 0.6190057, 0.9306522, 0.6591761]]]), - np.array([[]]), - np.array([[[64, 62]]]), - np.array([b"000000037777.jpg"]), - ] - self.assertRaises(ValueError, mAP.update, detection, ground_truth_1) - self.assertRaises(ValueError, mAP2.update, detection, ground_truth_1) - - ground_truth_2 = [ - np.array([[[0.51508695, 0.2911648, 0.5903478, 0.31360796], [0.872, 0.6190057, 0.9306522, 0.6591761]]]), - np.array([[]]), - np.array([[64]]), - np.array([b"000000037700.jpg"]), - ] - self.assertRaises(ValueError, mAP.update, detection, ground_truth_2) - self.assertRaises(ValueError, mAP2.update, detection, ground_truth_2) - - detection_1 = [ - np.array([[[0.16117382, 0.59801614, 0.81511605, 0.7858219], [0.5589304, 0.0, 0.98301625, 0.520178]]]), - np.array([[0.9267181, 0.8510787, 0.60418576, 0.35155892, 0.31158054]]), - np.array([[1.0, 67.0, 51.0, 79.0, 47.0]]), - ] - ground_truth_1 = [ - np.array([[[0.51508695, 0.2911648, 0.5903478, 0.31360796], [0.872, 0.6190057, 0.9306522, 0.6591761]]]), - np.array([[]]), - np.array([[64, 62]]), - np.array([b"000000011.jpg"]), - ] - self.assertRaises(ValueError, mAP.update, detection_1, ground_truth_1) - self.assertRaises(ValueError, mAP2.update, detection_1, ground_truth_1) - - ground_truth_2 = [ - np.array([[[0.51508695, 0.2911648, 0.5903478, 0.31360796], [0.872, 0.6190057, 0.9306522, 0.6591761]]]), - np.array([[]]), - np.array([[64, 62]]), - np.array([b"000000012.jpg"]), - ] - detection_2 = [ - np.array([[[0.16117382, 0.59801614, 0.81511605, 0.7858219], [0.5589304, 0.0, 0.98301625, 0.520178]]]), - np.array([[0.9267181, 0.8510787]]), - np.array([[1.0, 67.0, 51.0, 79.0, 47.0]]), - ] - self.assertRaises(ValueError, mAP.update, detection_2, ground_truth_2) - self.assertRaises(ValueError, mAP2.update, detection_2, ground_truth_2) - - @unittest.skipIf(platform.system().lower() == "windows", "not support mxnet on windows now") - def test__accuracy(self): - predicts1 = [1, 0, 1, 1] - labels1 = [0, 1, 1, 1] - - predicts2 = [[0, 0], [0, 0]] - labels2 = [[0, 1], [1, 1]] - - predicts3 = [[[0, 1], [0, 0], [0, 1]], [[0, 1], [0, 1], [0, 1]]] - labels3 = [[[0, 1], [0, 1], [1, 0]], [[1, 0], [1, 0], [1, 0]]] - - predicts4 = [[0.2, 0.8], [0.1, 0.9], [0.3, 0.7], [0.4, 0.6]] # 1,1,1,1 - labels4 = [0, 1, 0, 0] - - predicts5 = [[0], [0]] - labels5 = [0, 1] - - metrics = METRICS("pytorch") - acc = metrics["Accuracy"]() - acc.update(predicts1, labels1) - acc_result = acc.result() - self.assertEqual(acc_result, 0.5) - acc.reset() - acc.update(predicts2, labels2) - self.assertEqual(acc.result(), 0.25) - acc.reset() - acc.update(predicts3, labels3) - self.assertEqual(acc.result(), 0.25) - acc.reset() - acc.update(predicts4, labels4) - self.assertEqual(acc.result(), 0.25) - acc.reset() - acc.update(predicts5, labels5) - self.assertEqual(acc.result(), 1.0) - - metrics = METRICS("mxnet") - acc = metrics["Accuracy"]() - acc.update(predicts1, labels1) - acc_result = acc.result() - self.assertEqual(acc_result, 0.5) - acc.reset() - acc.update(predicts2, labels2) - self.assertEqual(acc.result(), 0.25) - acc.reset() - acc.update(predicts3, labels3) - self.assertEqual(acc.result(), 0.25) - acc.reset() - acc.update(predicts4, labels4) - self.assertEqual(acc.result(), 0.25) - - metrics = METRICS("onnxrt_qlinearops") - acc = metrics["Accuracy"]() - acc.update(predicts1, labels1) - acc_result = acc.result() - self.assertEqual(acc_result, 0.5) - acc.reset() - acc.update(predicts2, labels2) - self.assertEqual(acc.result(), 0.25) - acc.reset() - acc.update(predicts3, labels3) - self.assertEqual(acc.result(), 0.25) - acc.reset() - acc.update(predicts4, labels4) - self.assertEqual(acc.result(), 0.25) - - acc.reset() - acc.update(1, 1) - self.assertEqual(acc.result(), 1.0) - - wrong_predictions = [1, 0, 0] - wrong_labels = [[0, 1, 1]] - self.assertRaises(ValueError, acc.update, wrong_predictions, wrong_labels) - - @unittest.skipIf(platform.system().lower() == "windows", "not support mxnet on windows yet") - def test_mxnet_accuracy(self): - metrics = METRICS("mxnet") - acc = metrics["Accuracy"]() - predicts = [1, 0, 1, 1] - labels = [0, 1, 1, 1] - acc.update(predicts, labels) - acc_result = acc.result() - self.assertEqual(acc_result, 0.5) - - @unittest.skipIf(platform.system().lower() == "windows", "not support mxnet on windows now") - def test_mse(self): - predicts1 = [1, 0, 0, 1] - labels1 = [0, 1, 0, 0] - predicts2 = [1, 1, 1, 1] - labels2 = [0, 1, 1, 0] - - metrics = METRICS("onnxrt_qlinearops") - mse = metrics["MSE"](compare_label=False) - mse.update(predicts1, labels1) - mse_result = mse.result() - self.assertEqual(mse_result, 0.75) - mse.update(predicts2, labels2) - mse_result = mse.result() - self.assertEqual(mse_result, 0.625) - - metrics = METRICS("tensorflow") - mse = metrics["MSE"](compare_label=False) - mse.update(predicts1, labels1) - mse_result = mse.result() - self.assertEqual(mse_result, 0.75) - mse.update(predicts2, labels2) - mse_result = mse.result() - self.assertEqual(mse_result, 0.625) - - metrics = METRICS("mxnet") - mse = metrics["MSE"]() - mse.update(predicts1, labels1) - mse_result = mse.result() - self.assertEqual(mse_result, 0.75) - mse.update(predicts2, labels2) - mse_result = mse.result() - self.assertEqual(mse_result, 0.625) - - metrics = METRICS("pytorch") - mse = metrics["MSE"]() - mse.update(predicts1, labels1) - mse_result = mse.result() - self.assertEqual(mse_result, 0.75) - mse.update(predicts2, labels2) - mse_result = mse.result() - self.assertEqual(mse_result, 0.625) - - @unittest.skipIf(platform.system().lower() == "windows", "not support mxnet on windows now") - def test_mae(self): - predicts1 = [1, 0, 0, 1] - labels1 = [0, 1, 0, 0] - predicts2 = [1, 1, 1, 1] - labels2 = [1, 1, 1, 0] - - metrics = METRICS("tensorflow") - mae = metrics["MAE"]() - mae.update(predicts1, labels1) - mae_result = mae.result() - self.assertEqual(mae_result, 0.75) - mae.update(0, 1) - mae_result = mae.result() - self.assertEqual(mae_result, 0.8) - mae.reset() - mae.update(predicts2, labels2) - mae_result = mae.result() - self.assertEqual(mae_result, 0.25) - - metrics = METRICS("pytorch") - mae = metrics["MAE"]() - mae.update(predicts1, labels1) - mae_result = mae.result() - self.assertEqual(mae_result, 0.75) - mae.update(predicts2, labels2) - mae_result = mae.result() - self.assertEqual(mae_result, 0.5) - - metrics = METRICS("mxnet") - mae = metrics["MAE"]() - mae.update(predicts1, labels1) - mae_result = mae.result() - self.assertEqual(mae_result, 0.75) - mae.update(predicts2, labels2) - mae_result = mae.result() - self.assertEqual(mae_result, 0.5) - - metrics = METRICS("onnxrt_qlinearops") - mae = metrics["MAE"]() - mae.update(predicts1, labels1) - mae_result = mae.result() - self.assertEqual(mae_result, 0.75) - mae.update(predicts2, labels2) - mae_result = mae.result() - self.assertEqual(mae_result, 0.5) - - self.assertRaises(AssertionError, mae.update, [1], [1, 2]) - self.assertRaises(AssertionError, mae.update, 1, [1, 2]) - self.assertRaises(AssertionError, mae.update, [1, 2], [1]) - self.assertRaises(AssertionError, mae.update, 1, np.array([1, 2])) - - @unittest.skipIf(platform.system().lower() == "windows", "not support mxnet on windows now") - def test_rmse(self): - predicts1 = [1, 0, 0, 1] - labels1 = [1, 0, 0, 0] - predicts2 = [1, 1, 1, 1] - labels2 = [1, 0, 0, 0] - - metrics = METRICS("tensorflow") - rmse = metrics["RMSE"]() - rmse.update(predicts1, labels1) - rmse_result = rmse.result() - self.assertEqual(rmse_result, 0.5) - rmse.reset() - rmse.update(predicts2, labels2) - rmse_result = rmse.result() - self.assertAlmostEqual(rmse_result, np.sqrt(0.75)) - - metrics = METRICS("pytorch") - rmse = metrics["RMSE"]() - rmse.update(predicts1, labels1) - rmse_result = rmse.result() - self.assertEqual(rmse_result, 0.5) - rmse.update(predicts2, labels2) - rmse_result = rmse.result() - self.assertAlmostEqual(rmse_result, np.sqrt(0.5)) - - metrics = METRICS("mxnet") - rmse = metrics["RMSE"]() - rmse.update(predicts1, labels1) - rmse_result = rmse.result() - self.assertEqual(rmse_result, 0.5) - rmse.update(predicts2, labels2) - rmse_result = rmse.result() - self.assertAlmostEqual(rmse_result, np.sqrt(0.5)) - - metrics = METRICS("onnxrt_qlinearops") - rmse = metrics["RMSE"]() - rmse.update(predicts1, labels1) - rmse_result = rmse.result() - self.assertEqual(rmse_result, 0.5) - rmse.update(predicts2, labels2) - rmse_result = rmse.result() - self.assertAlmostEqual(rmse_result, np.sqrt(0.5)) - - def test_loss(self): - metrics = METRICS("pytorch") - loss = metrics["Loss"]() - predicts = [1, 0, 0, 1] - labels = [0, 1, 0, 0] - loss.update(predicts, labels) - loss_result = loss.result() - self.assertEqual(loss_result, 0.5) - predicts = [1, 1, 0, 1] - labels = [0, 1, 0, 0] - loss.update(predicts, labels) - loss_result = loss.result() - self.assertEqual(loss_result, 0.625) - loss.reset() - predicts = [1, 0, 0, 1] - labels = [0, 1, 0, 0] - loss.update(predicts, labels) - self.assertEqual(loss.result(), 0.5) - - metrics = METRICS("onnxrt_qlinearops") - loss = metrics["Loss"]() - predicts = [1, 0, 0, 1] - labels = [0, 1, 0, 0] - loss.update(predicts, labels) - loss_result = loss.result() - self.assertEqual(loss_result, 0.5) - predicts = [1, 1, 0, 1] - labels = [0, 1, 0, 0] - loss.update(predicts, labels) - loss_result = loss.result() - self.assertEqual(loss_result, 0.625) - loss.reset() - predicts = [1, 0, 0, 1] - labels = [0, 1, 0, 0] - loss.update(predicts, labels) - self.assertEqual(loss.result(), 0.5) - - def test_ROC(self): - metrics = METRICS("pytorch") - roc = metrics["ROC"]() - predicts = [[1, 0, 0, 1]] - labels = [[0, 1, 0, 0]] - roc.update(predicts, labels) - roc_result = roc.result() - self.assertEqual(roc_result, 0.25) - predicts = [[1]] - labels = [[0]] - roc.update(predicts, labels) - roc_result = roc.result() - self.assertEqual(roc_result, 0.2) - roc.reset() - predicts = [[1, 0, 0, 1]] - labels = [[0, 1, 0, 0]] - roc.update(predicts, labels) - self.assertEqual(roc.result(), 0.25) - - def test_tensorflow_SquadF1(self): - metrics = METRICS("tensorflow") - squad = metrics["SquadF1"]() - labels = [ - { - "paragraphs": [ - { - "qas": [ - { - "answers": [ - {"answer_start": 177, "text": "Denver Broncos"}, - {"answer_start": 177, "text": "Denver Broncos"}, - {"answer_start": 177, "text": "Denver Broncos"}, - ], - "question": "Which NFL team represented the AFC at Super Bowl 50?", - "id": "56be4db0acb8001400a502ec", - } - ] - } - ] - } - ] - predicts = {"56be4db0acb8001400a502ec": "Denver Broncos"} - squad.update(predicts, labels) - self.assertEqual(squad.result(), 100.0) - squad.reset() - squad.update(predicts, labels) - self.assertEqual(squad.result(), 100.0) - - def test_PyTorchLoss(self): - import torch - - from neural_compressor.experimental.metric.metric import PyTorchLoss - - pytorch_loss = PyTorchLoss() - pytorch_loss.update([torch.ones(2, 3), torch.ones(2, 3)]) - self.assertEqual(pytorch_loss.compute(), 3) - pytorch_loss.reset() - self.assertEqual(pytorch_loss._num_examples, 0) - - def test_WrapMetric(self): - metirc = CorrectMetric() - metric_v2 = CorrectMetric_v2() - - from neural_compressor.experimental.metric.metric import WrapPyTorchMetric - - pytorch_metric = WrapPyTorchMetric(metirc) - self.assertIsInstance(pytorch_metric.metric, CorrectMetric) - self.assertIsNone(pytorch_metric.hvd) - pytorch_metric.update([1], [1]) - self.assertEqual(pytorch_metric.result(), 0) - pytorch_metric.reset() - self.assertEqual(len(pytorch_metric.metric.item), 0) - - from neural_compressor.experimental.metric.metric import WrapONNXRTMetric - - onnx_metric = WrapONNXRTMetric(metric_v2) - self.assertIsInstance(onnx_metric.metric, CorrectMetric_v2) - self.assertIsNone(onnx_metric.hvd) - onnx_metric.update([1], [1]) - self.assertEqual(onnx_metric.result(), 0) - onnx_metric.reset() - self.assertEqual(len(onnx_metric.metric.item), 0) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/metric/test_metrics.py b/test/metric/test_metrics.py index 0e7bdb2308c..e91928b782f 100644 --- a/test/metric/test_metrics.py +++ b/test/metric/test_metrics.py @@ -30,15 +30,6 @@ def reset(self): class TestMetrics(unittest.TestCase): - def testUserMetric(self): - from neural_compressor.experimental import Benchmark, Graph_Optimization, Quantization, common - - for i in [Quantization(), Benchmark(), Graph_Optimization()]: - item = i - with self.assertRaises(AssertionError): - item.metric = InCorrectMetric() - item.framework = "tensorflow" - item.metric = common.Metric(CorrectMetric, str(i)) def testmIOU(self): metrics = METRICS("tensorflow") diff --git a/test/metric/test_mse_metric.py b/test/metric/test_mse_metric.py deleted file mode 100644 index 5a266a060f0..00000000000 --- a/test/metric/test_mse_metric.py +++ /dev/null @@ -1,234 +0,0 @@ -import copy -import os -import shutil -import unittest - -import numpy as np -import torch -import torchvision -from packaging.version import Version - -import neural_compressor.adaptor.pytorch as nc_torch -from neural_compressor.adaptor import FRAMEWORKS -from neural_compressor.experimental import Quantization, common -from neural_compressor.model import MODELS - -try: - try: - import intel_pytorch_extension as ipex - except: - import intel_extension_for_pytorch as ipex - TEST_IPEX = True -except: - TEST_IPEX = False - -PT_VERSION = nc_torch.get_torch_version() -if PT_VERSION >= Version("1.8.0-rc1"): - FX_MODE = True -else: - FX_MODE = False - -torch.manual_seed(1) - -fake_ptq_yaml = """ - model: - name: imagenet - framework: pytorch - - evaluation: - accuracy: - metric: - MSE: - compare_label: False - performance: - warmup: 5 - iteration: 10 - - tuning: - accuracy_criterion: - absolute: 100.0 - higher_is_better: False - exit_policy: - timeout: 0 - random_seed: 9527 - workspace: - path: saved - """ - -fake_dynamic_yaml = """ - model: - name: imagenet - framework: pytorch - - quantization: - approach: post_training_dynamic_quant - evaluation: - accuracy: - metric: - MSE: - compare_label: False - performance: - warmup: 5 - iteration: 10 - - tuning: - accuracy_criterion: - absolute: 100.0 - higher_is_better: False - exit_policy: - timeout: 0 - random_seed: 9527 - workspace: - path: saved - """ - - -def build_ptq_yaml(): - with open("ptq_yaml.yaml", "w", encoding="utf-8") as f: - f.write(fake_ptq_yaml) - - -def build_dynamic_yaml(): - with open("dynamic_yaml.yaml", "w", encoding="utf-8") as f: - f.write(fake_dynamic_yaml) - - -def build_fx_ptq_yaml(): - fake_fx_ptq_yaml = fake_ptq_yaml.replace("pytorch", "pytorch_fx") - with open("fx_ptq_yaml.yaml", "w", encoding="utf-8") as f: - f.write(fake_fx_ptq_yaml) - - -def build_fx_dynamic_yaml(): - fake_fx_dynamic_yaml = fake_dynamic_yaml.replace("pytorch", "pytorch_fx") - with open("fx_dynamic_yaml.yaml", "w", encoding="utf-8") as f: - f.write(fake_fx_dynamic_yaml) - - -def build_ipex_yaml(): - fake_yaml = """ - model: - name: imagenet - framework: pytorch_ipex - - evaluation: - accuracy: - metric: - MSE: - compare_label: False - performance: - warmup: 5 - iteration: 10 - - tuning: - accuracy_criterion: - relative: 0.01 - exit_policy: - timeout: 0 - random_seed: 9527 - workspace: - path: saved - """ - with open("ipex_yaml.yaml", "w", encoding="utf-8") as f: - f.write(fake_yaml) - - -@unittest.skipIf(TEST_IPEX, "TODO: Please wait to IPEX + PyTorch1.7 release") -class TestPytorchAdaptor(unittest.TestCase): - framework_specific_info = { - "device": "cpu", - "approach": "post_training_static_quant", - "random_seed": 1234, - "q_dataloader": None, - "workspace_path": "./", - } - framework = "pytorch" - adaptor = FRAMEWORKS[framework](framework_specific_info) - model = torchvision.models.quantization.resnet18() - nc_model = MODELS["pytorch"](model) - - @classmethod - def setUpClass(self): - build_ptq_yaml() - build_dynamic_yaml() - - @classmethod - def tearDownClass(self): - os.remove("ptq_yaml.yaml") - os.remove("dynamic_yaml.yaml") - shutil.rmtree("./saved", ignore_errors=True) - shutil.rmtree("runs", ignore_errors=True) - - def test_quantization_saved(self): - for fake_yaml in ["dynamic_yaml.yaml", "ptq_yaml.yaml"]: - if fake_yaml in ["dynamic_yaml.yaml"]: - model = torchvision.models.quantization.resnet18() - else: - model = copy.deepcopy(self.model) - if fake_yaml in ["ptq_yaml.yaml"]: - model.eval().fuse_model() - quantizer = Quantization(fake_yaml) - dataset = quantizer.dataset("dummy", (100, 3, 256, 256), label=True) - quantizer.model = model - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.eval_dataloader = common.DataLoader(dataset) - q_model = quantizer.fit() - self.assertTrue(bool(q_model)) - - -@unittest.skipIf(not FX_MODE, "Unsupported Fx Mode with PyTorch Version Below 1.8") -class TestPytorchFXAdaptor(unittest.TestCase): - framework_specific_info = { - "device": "cpu", - "approach": "post_training_static_quant", - "random_seed": 1234, - "q_dataloader": None, - "workspace_path": "./", - } - framework = "pytorch_fx" - adaptor = FRAMEWORKS[framework](framework_specific_info) - model = torchvision.models.quantization.resnet18() - nc_model = MODELS["pytorch_fx"](model) - - @classmethod - def setUpClass(self): - build_fx_ptq_yaml() - build_fx_dynamic_yaml() - - @classmethod - def tearDownClass(self): - os.remove("fx_ptq_yaml.yaml") - os.remove("fx_dynamic_yaml.yaml") - shutil.rmtree("./saved", ignore_errors=True) - shutil.rmtree("runs", ignore_errors=True) - - def test_fx_static_quantization_saved(self): - fake_yaml = "fx_ptq_yaml.yaml" - model = copy.deepcopy(self.model) - model.eval().fuse_model() - quantizer = Quantization(fake_yaml) - dataset = quantizer.dataset("dummy", (100, 3, 256, 256), label=True) - quantizer.model = model - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.eval_dataloader = common.DataLoader(dataset) - q_model = quantizer.fit() - self.assertTrue(bool(q_model)) - - @unittest.skipIf( - PT_VERSION < Version("1.9.0-rc1"), - "Please use PyTroch 1.9 or higher version for dynamic quantization with pytorch_fx backend", - ) - def test_fx_dynamic_quantization_saved(self): - fake_yaml = "fx_dynamic_yaml.yaml" - model = torchvision.models.resnet18() - quantizer = Quantization(fake_yaml) - quantizer.model = model - dataset = quantizer.dataset("dummy", (100, 3, 256, 256), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.eval_dataloader = common.DataLoader(dataset) - q_model = quantizer.fit() - self.assertTrue(bool(q_model)) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/metric/test_mse_metric_1x.py b/test/metric/test_mse_metric_1x.py deleted file mode 100644 index ee2e6bf4c78..00000000000 --- a/test/metric/test_mse_metric_1x.py +++ /dev/null @@ -1,303 +0,0 @@ -"""Tests for quantization.""" - -import os -import shutil -import unittest - -import numpy as np -import torch -import torchvision -import yaml - - -def build_fake_yaml(): - fake_yaml = """ - model: - name: fake_yaml - framework: tensorflow - inputs: x - outputs: op_to_store - device: cpu - evaluation: - accuracy: - metric: - topk: 1 - tuning: - strategy: - name: mse - accuracy_criterion: - relative: 0.01 - workspace: - path: saved - """ - y = yaml.load(fake_yaml, Loader=yaml.SafeLoader) - with open("fake_yaml.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - f.close() - - -def build_fake_yaml2(): - fake_yaml = """ - model: - name: fake_yaml - framework: tensorflow - inputs: x - outputs: op_to_store - device: cpu - evaluation: - accuracy: - metric: - topk: 1 - tuning: - strategy: - name: mse - exit_policy: - max_trials: 5 - accuracy_criterion: - relative: -0.01 - workspace: - path: saved - """ - y = yaml.load(fake_yaml, Loader=yaml.SafeLoader) - with open("fake_yaml2.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - f.close() - - -def build_fake_yaml3(): - fake_yaml = """ - model: - name: fake_yaml - framework: tensorflow - inputs: x - outputs: op_to_store - device: cpu - evaluation: - accuracy: - multi_metrics: - topk: 1 - MSE: - compare_label: False - tuning: - strategy: - name: mse - exit_policy: - max_trials: 5 - timeout: 50 - accuracy_criterion: - relative: 0.01 - workspace: - path: saved - """ - y = yaml.load(fake_yaml, Loader=yaml.SafeLoader) - with open("fake_yaml3.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - f.close() - - -def build_fake_yaml4(): - fake_yaml = """ - model: - name: fake_yaml - framework: tensorflow - inputs: x - outputs: op_to_store - device: cpu - evaluation: - accuracy: - multi_metrics: - topk: 1 - MSE: - compare_label: False - weight: [1, 0] - tuning: - strategy: - name: mse - exit_policy: - max_trials: 5 - timeout: 50 - accuracy_criterion: - relative: 0.01 - workspace: - path: saved - """ - y = yaml.load(fake_yaml, Loader=yaml.SafeLoader) - with open("fake_yaml4.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - f.close() - - -def build_ox_yaml(): - fake_yaml = """ - model: - name: fake_yaml - framework: onnxrt_qlinearops - inputs: input - outputs: output - evaluation: - accuracy: - metric: - Accuracy: {} - tuning: - strategy: - name: mse - accuracy_criterion: - relative: -0.01 - higher_is_better: False - exit_policy: - max_trials: 3 - workspace: - path: saved - """ - y = yaml.load(fake_yaml, Loader=yaml.SafeLoader) - with open("ox_yaml.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - f.close() - - -def build_fake_model(): - import tensorflow as tf - - try: - graph = tf.Graph() - graph_def = tf.GraphDef() - with tf.Session() as sess: - x = tf.placeholder(tf.float64, shape=(1, 3, 3, 1), name="x") - y = tf.constant(np.random.random((2, 2, 1, 1)), name="y") - op = tf.nn.conv2d(input=x, filter=y, strides=[1, 1, 1, 1], padding="VALID", name="op_to_store") - - sess.run(tf.global_variables_initializer()) - constant_graph = tf.graph_util.convert_variables_to_constants(sess, sess.graph_def, ["op_to_store"]) - - graph_def.ParseFromString(constant_graph.SerializeToString()) - with graph.as_default(): - tf.import_graph_def(graph_def, name="") - except: - graph = tf.Graph() - graph_def = tf.compat.v1.GraphDef() - with tf.compat.v1.Session() as sess: - x = tf.compat.v1.placeholder(tf.float64, shape=(1, 3, 3, 1), name="x") - y = tf.compat.v1.constant(np.random.random((2, 2, 1, 1)), name="y") - op = tf.nn.conv2d(input=x, filters=y, strides=[1, 1, 1, 1], padding="VALID", name="op_to_store") - - sess.run(tf.compat.v1.global_variables_initializer()) - constant_graph = tf.compat.v1.graph_util.convert_variables_to_constants( - sess, sess.graph_def, ["op_to_store"] - ) - - graph_def.ParseFromString(constant_graph.SerializeToString()) - with graph.as_default(): - tf.import_graph_def(graph_def, name="") - return graph - - -def build_ox_model(): - path = "mb_v2.onnx" - model = torchvision.models.mobilenet_v2() - - x = torch.randn(100, 3, 224, 224, requires_grad=True) - torch_out = model(x) - - torch.onnx.export( - model, - x, - path, - export_params=True, - opset_version=12, - do_constant_folding=True, - input_names=["input"], - output_names=["output"], - dynamic_axes={"input": {0: "batch_size"}, "output": {0: "batch_size"}}, - ) - - -class dataset: - def __init__(self): - self.data = [] - self.label = [] - for i in range(10): - self.data.append(np.zeros((3, 224, 224)).astype(np.float32)) - self.label.append(0) - - def __len__(self): - return len(self.data) - - def __getitem__(self, index): - return self.data[index], self.label[index] - - -class TestQuantization(unittest.TestCase): - @classmethod - def setUpClass(self): - self.constant_graph = build_fake_model() - build_fake_yaml() - build_fake_yaml2() - build_ox_model() - build_ox_yaml() - build_fake_yaml3() - build_fake_yaml4() - - @classmethod - def tearDownClass(self): - os.remove("fake_yaml.yaml") - os.remove("fake_yaml2.yaml") - os.remove("ox_yaml.yaml") - os.remove("mb_v2.onnx") - os.remove("fake_yaml4.yaml") - os.remove("fake_yaml3.yaml") - - shutil.rmtree("saved", ignore_errors=True) - - def test_ru_mse_one_trial(self): - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", (100, 3, 3, 1), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.model = self.constant_graph - quantizer.fit() - - def test_ru_mse_max_trials(self): - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml2.yaml") - dataset = quantizer.dataset("dummy", (100, 3, 3, 1), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.model = self.constant_graph - quantizer.fit() - - def test_ru_mse_max_trials_multimetric(self): - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml3.yaml") - dataset = quantizer.dataset("dummy", (100, 3, 3, 1), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.model = self.constant_graph - quantizer.fit() - - def test_ru_mse_max_trials_multimetric_weight(self): - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml4.yaml") - dataset = quantizer.dataset("dummy", (100, 3, 3, 1), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.model = self.constant_graph - quantizer.fit() - - def test_ox_mse(self): - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("ox_yaml.yaml") - ds = dataset() - quantizer.calib_dataloader = common.DataLoader(ds) - quantizer.eval_dataloader = common.DataLoader(ds) - quantizer.model = "mb_v2.onnx" - quantizer.fit() - - -if __name__ == "__main__": - unittest.main() diff --git a/test/metric/test_register_metric_transform.py b/test/metric/test_register_metric_transform.py deleted file mode 100644 index e4fa1cc9d11..00000000000 --- a/test/metric/test_register_metric_transform.py +++ /dev/null @@ -1,136 +0,0 @@ -"""Tests for neural_compressor register metric and postprocess.""" - -import os -import platform -import re -import unittest - -import numpy as np -import yaml - - -def build_fake_yaml(): - fake_yaml = """ - model: - name: resnet_v1_101 - framework: tensorflow - inputs: input - outputs: resnet_v1_101/predictions/Reshape_1 - device: cpu - """ - y = yaml.load(fake_yaml, Loader=yaml.SafeLoader) - with open("fake_yaml.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - f.close() - - -def build_benchmark(): - seq = [ - "from argparse import ArgumentParser\n", - "arg_parser = ArgumentParser(description='Parse args')\n", - "arg_parser.add_argument('--input_model', dest='input_model', default='input_model', help='input model')\n", - "args = arg_parser.parse_args()\n", - "import os\n", - "import numpy as np\n", - "import PIL.Image\n", - "image = np.array(PIL.Image.open('images/cat.jpg'))\n", - "resize_image = np.resize(image, (224, 224, 3))\n", - "mean = [123.68, 116.78, 103.94]\n", - "resize_image = resize_image - mean\n", - "images = np.expand_dims(resize_image, axis=0)\n", - "labels = [768]\n", - "from neural_compressor.data.transforms.imagenet_transform import LabelShift\n", - "from neural_compressor.experimental import Benchmark, common\n", - "from neural_compressor.experimental.common import Metric, Postprocess\n", - "from neural_compressor.metric import TensorflowTopK\n", - "os.environ['NC_ENV_CONF'] = 'True'\n", - "evaluator = Benchmark('fake_yaml.yaml')\n", - "nc_postprocess = Postprocess(LabelShift, 'label_benchmark', label_shift=1)\n", - "evaluator.postprocess = nc_postprocess\n", - "nc_metric = Metric(TensorflowTopK, 'topk_benchmark')\n", - "evaluator.metric = nc_metric\n", - "evaluator.b_dataloader = common.DataLoader(dataset=list(zip(images, labels)))\n", - "evaluator.model = args.input_model\n", - "evaluator.fit()\n", - ] - - with open("fake.py", "w", encoding="utf-8") as f: - f.writelines(seq) - - -def build_benchmark2(): - seq = [ - "from argparse import ArgumentParser\n", - "arg_parser = ArgumentParser(description='Parse args')\n", - "arg_parser.add_argument('--input_model', dest='input_model', default='input_model', help='input model')\n", - "args = arg_parser.parse_args()\n", - "import os\n", - "import numpy as np\n", - "import PIL.Image\n", - "image = np.array(PIL.Image.open('images/cat.jpg'))\n", - "resize_image = np.resize(image, (224, 224, 3))\n", - "mean = [123.68, 116.78, 103.94]\n", - "resize_image = resize_image - mean\n", - "images = np.expand_dims(resize_image, axis=0)\n", - "labels = [768]\n", - "from neural_compressor.data.transforms.imagenet_transform import LabelShift\n", - "from neural_compressor.experimental import Benchmark, common\n", - "from neural_compressor.experimental.common import Metric, Postprocess\n", - "from neural_compressor.metric import TensorflowTopK\n", - "os.environ['NC_ENV_CONF'] = 'True'\n", - "evaluator = Benchmark('fake_yaml.yaml')\n", - "nc_metric = Metric(TensorflowTopK, 'topk_second')\n", - "evaluator.metric = nc_metric\n", - "evaluator.b_dataloader = common.DataLoader(dataset=list(zip(images, labels)))\n", - "evaluator.model = args.input_model\n\n", - "evaluator.fit()\n", - ] - - with open("fake2.py", "w", encoding="utf-8") as f: - f.writelines(seq) - - -class TestRegisterMetric(unittest.TestCase): - model_url = ( - "https://storage.googleapis.com/intel-optimized-tensorflow/models/v1_6/resnet101_fp32_pretrained_model.pb" - ) - pb_path = "/tmp/.neural_compressor/resnet101_fp32_pretrained_model.pb" - # image_path = 'images/1024px-Doll_face_silver_Persian.jpg' - image_path = "images/cat.jpg" - platform = platform.system().lower() - if platform == "windows": - pb_path = "C:\\tmp\.neural_compressor\\resnet101_fp32_pretrained_model.pb" - - @classmethod - def setUpClass(self): - build_fake_yaml() - build_benchmark() - build_benchmark2() - if not os.path.exists(self.pb_path) and self.platform == "linux": - os.system("mkdir -p /tmp/.neural_compressor && wget {} -O {}".format(self.model_url, self.pb_path)) - - @classmethod - def tearDownClass(self): - if os.path.exists("fake.py"): - os.remove("fake.py") - if os.path.exists("fake2.py"): - os.remove("fake2.py") - - def test_register_metric_postprocess(self): - os.system("python fake.py --input_model={} 2>&1 | tee benchmark.log".format(self.pb_path)) - with open("benchmark.log", "r") as f: - for line in f: - throughput = re.search(r"Throughput:\s+(\d+(\.\d+)?) images/sec", line) - self.assertIsNotNone(throughput) - os.system("rm benchmark.log") - - os.system("python fake2.py --input_model={} 2>&1 | tee benchmark.log".format(self.pb_path)) - with open("benchmark.log", "r") as f: - for line in f: - throughput = re.search(r"Throughput:\s+(\d+(\.\d+)?) images/sec", line) - self.assertIsNotNone(throughput) - os.system("rm benchmark.log") - - -if __name__ == "__main__": - unittest.main() diff --git a/test/mixed_precision/test_mixed_precision.py b/test/mixed_precision/test_mixed_precision.py index 1f333dde0fb..10ab9afccf2 100644 --- a/test/mixed_precision/test_mixed_precision.py +++ b/test/mixed_precision/test_mixed_precision.py @@ -331,15 +331,6 @@ def test_mixed_precision_with_evaluation(self): ) self.assertTrue(any([i.op_type == "Cast" for i in output_model.nodes()])) - def test_mixed_precision_with_evaluation_old_api(self): - from neural_compressor.conf.config import MixedPrecision_Conf - from neural_compressor.experimental import MixedPrecision - - converter = MixedPrecision(MixedPrecision_Conf("test.yaml")) - converter.model = self.onnx_model - output_model = converter.fit() - self.assertTrue(any([i.op_type != "Cast" for i in output_model.nodes()])) - def test_mixed_precision_with_eval_func(self): def eval(model): return 0.5 diff --git a/test/nas/test_nas.py b/test/nas/test_nas.py deleted file mode 100644 index 98ccb79fb40..00000000000 --- a/test/nas/test_nas.py +++ /dev/null @@ -1,210 +0,0 @@ -import os -import shutil -import unittest - -import numpy as np -import torch - -from neural_compressor.conf.config import NASConfig -from neural_compressor.data import Datasets -from neural_compressor.experimental import NAS, common -from neural_compressor.experimental.data.dataloaders.pytorch_dataloader import PyTorchDataLoader - - -def build_fake_yaml(approach=None, search_algorithm=None, metrics=["acc"]): - fake_yaml = """ - model: - name: imagenet_nas - framework: pytorch - - nas: - %s - search: - search_space: {'channels': [16, 32, 64], 'dimensions': [32, 64, 128]} - %s - %s - max_trials: 3 - train: - start_epoch: 0 - end_epoch: 1 - iteration: 10 - optimizer: - SGD: - learning_rate: 0.001 - criterion: - CrossEntropyLoss: - reduction: sum - dataloader: - batch_size: 8 - dataset: - dummy: - shape: [32, 3, 64, 64] - label: True - evaluation: - accuracy: - metric: - topk: 1 - dataloader: - batch_size: 8 - dataset: - dummy: - shape: [32, 3, 64, 64] - label: True - """ % ( - "approach: '{}'".format(approach) if approach else "", - "search_algorithm: '{}'".format(search_algorithm) if search_algorithm else "", - "metrics: [{}]".format(",".join(["'{}'".format(m) for m in metrics])) if metrics else "", - ) - with open("fake.yaml", "w", encoding="utf-8") as f: - f.write(fake_yaml) - - -def build_dynas_fake_yaml(): - fake_yaml = """ - model: - name: imagenet_nas - framework: pytorch - - nas: - approach: dynas - search: - search_algorithm: nsga2 - dynas: - supernet: ofa_resnet50 - metrics: ['accuracy_top1', 'macs'] - results_csv_path: './search_results.csv' - """ - with open("dynas_fake.yaml", "w", encoding="utf-8") as f: - f.write(fake_yaml) - - -def build_dynas_results_csv(): - results_csv = """ -Sub-network,Date,Latency (ms), MACs,Top-1 Acc (%) -"{'wid': None, 'ks': [7, 7, 3, 3, 5, 7, 7, 3, 5, 5, 3, 3, 7, 3, 5, 5, 5, 7, 5, 7], 'e': [3, 4, 4, 4, 4, 6, 6, 4, 4, 3, 4, 4, 3, 6, 4, 3, 4, 6, 3, 3], 'd': [2, 4, 4, 2, 3], 'r': [224]}",2022-07-07 03:13:06.306540,39,391813792,77.416 -"{'wid': None, 'ks': [3, 5, 5, 7, 5, 5, 3, 3, 7, 7, 7, 5, 7, 3, 7, 5, 3, 5, 3, 3], 'e': [4, 6, 3, 4, 4, 4, 4, 6, 3, 6, 4, 3, 4, 3, 4, 3, 6, 4, 4, 6], 'd': [4, 3, 3, 2, 3], 'r': [224]}",2022-07-07 03:14:50.398553,41,412962768,77.234 -"{'wid': None, 'ks': [5, 5, 5, 3, 7, 5, 7, 5, 7, 3, 3, 7, 7, 5, 7, 3, 5, 5, 7, 3], 'e': [6, 4, 3, 3, 3, 3, 4, 4, 3, 4, 3, 6, 4, 4, 3, 6, 4, 3, 4, 6], 'd': [4, 4, 4, 2, 4], 'r': [224]}",2022-07-07 03:16:53.105436,44,444295456,77.632 -"{'wid': None, 'ks': [3, 5, 3, 7, 3, 5, 7, 5, 3, 3, 3, 7, 3, 5, 3, 5, 3, 3, 7, 3], 'e': [4, 6, 3, 3, 6, 3, 3, 6, 6, 4, 4, 6, 3, 4, 3, 6, 3, 6, 3, 4], 'd': [4, 4, 2, 2, 4], 'r': [224]}",2022-07-07 03:18:47.301137,41,410969240,76.79 -"{'wid': None, 'ks': [3, 3, 3, 3, 7, 5, 3, 5, 3, 5, 5, 7, 7, 7, 3, 5, 7, 5, 3, 7], 'e': [3, 6, 6, 4, 6, 3, 3, 4, 3, 6, 3, 4, 4, 6, 3, 6, 4, 3, 6, 3], 'd': [2, 3, 4, 4, 2], 'r': [224]}",2022-07-07 03:20:35.391443,40,405868672,77.338 -"{'wid': None, 'ks': [3, 3, 3, 7, 5, 7, 7, 3, 3, 3, 3, 5, 7, 3, 7, 5, 3, 7, 5, 5], 'e': [4, 6, 3, 6, 4, 3, 3, 6, 3, 6, 4, 6, 4, 4, 3, 6, 4, 3, 4, 4], 'd': [3, 4, 4, 2, 2], 'r': [224]}",2022-07-07 03:22:14.504855,37,370501152,76.448 -"{'wid': None, 'ks': [7, 5, 3, 5, 7, 5, 3, 3, 5, 3, 3, 7, 7, 3, 5, 3, 3, 5, 5, 7], 'e': [3, 3, 4, 4, 4, 6, 6, 6, 6, 6, 6, 6, 6, 4, 3, 6, 3, 3, 3, 4], 'd': [4, 4, 3, 4, 2], 'r': [224]}",2022-07-07 03:24:12.500905,48,482299704,77.7 -"{'wid': None, 'ks': [7, 3, 5, 7, 5, 5, 7, 5, 3, 3, 3, 5, 5, 3, 7, 5, 5, 7, 3, 7], 'e': [3, 6, 4, 6, 6, 3, 3, 3, 6, 3, 6, 4, 4, 6, 4, 4, 4, 4, 6, 6], 'd': [4, 4, 2, 2, 2], 'r': [224]}",2022-07-07 03:25:50.198665,42,423721952,76.506 -"{'wid': None, 'ks': [7, 7, 3, 7, 5, 7, 5, 5, 5, 3, 5, 3, 3, 7, 3, 5, 3, 7, 7, 3], 'e': [3, 3, 3, 4, 4, 3, 4, 4, 4, 4, 4, 6, 6, 4, 3, 3, 3, 6, 3, 4], 'd': [4, 2, 2, 3, 3], 'r': [224]}",2022-07-07 03:27:26.901886,37,373770104,77.258 -"{'wid': None, 'ks': [3, 7, 5, 5, 7, 3, 5, 3, 5, 5, 5, 3, 5, 5, 3, 5, 7, 3, 7, 5], 'e': [3, 4, 6, 6, 4, 3, 6, 6, 6, 3, 3, 3, 3, 6, 3, 6, 6, 3, 6, 3], 'd': [3, 2, 3, 2, 3], 'r': [224]}",2022-07-07 03:29:00.989578,36,369186480,77.096 -"{'wid': None, 'ks': [7, 7, 5, 5, 7, 5, 3, 3, 3, 5, 7, 3, 7, 7, 5, 5, 3, 7, 3, 7], 'e': [6, 3, 6, 3, 4, 3, 3, 3, 4, 3, 6, 4, 3, 3, 6, 4, 4, 3, 4, 3], 'd': [4, 4, 3, 4, 4], 'r': [224]}",2022-07-07 03:31:07.608402,51,518341312,78.104 - """ - with open("search_results.csv", "w", encoding="utf-8") as f: - f.write(results_csv) - - -def model_builder(model_arch_params): - channels = model_arch_params["channels"] - dimensions = model_arch_params["dimensions"] - return ConvNet(channels, dimensions) - - -class ConvNet(torch.nn.Module): - def __init__(self, channels, dimensions): - super().__init__() - self.conv = torch.nn.Conv2d(3, channels, (3, 3), padding=1) - self.avg_pooling = torch.nn.AvgPool2d((64, 64)) - self.dense = torch.nn.Linear(channels, dimensions) - self.out = torch.nn.Linear(dimensions, 1) - self.activation = torch.nn.Sigmoid() - - def forward(self, inputs): - outputs = self.conv(inputs) - outputs = self.avg_pooling(outputs).squeeze() - outputs = self.dense(outputs) - outputs = self.out(outputs) - outputs = self.activation(outputs) - return outputs - - -class TestNAS(unittest.TestCase): - @classmethod - def setUpClass(cls): - build_fake_yaml() - build_dynas_fake_yaml() - build_dynas_results_csv() - - @classmethod - def tearDownClass(cls): - os.remove("fake.yaml") - os.remove("dynas_fake.yaml") - os.remove("search_results.csv") - shutil.rmtree(os.path.join(os.getcwd(), "NASResults"), ignore_errors=True) - shutil.rmtree("runs", ignore_errors=True) - - def test_basic_nas(self): - # Built-in train, evaluation - nas_agent = NAS("fake.yaml") - nas_agent.model_builder = lambda model_arch_params: common.Model(model_builder(model_arch_params)) - best_model_archs = nas_agent() - self.assertTrue(len(best_model_archs) > 0) - - # Customized train, evaluation - datasets = Datasets("pytorch") - dummy_dataset = datasets["dummy"](shape=(32, 3, 64, 64), low=0.0, high=1.0, label=True) - dummy_dataloader = PyTorchDataLoader(dummy_dataset) - - def train_func(model): - epochs = 2 - iters = 10 - criterion = torch.nn.CrossEntropyLoss() - optimizer = torch.optim.SGD(model.parameters(), lr=0.0001) - for nepoch in range(epochs): - model.train() - cnt = 0 - for image, target in dummy_dataloader: - print(".", end="") - cnt += 1 - output = model(image).unsqueeze(dim=0) - loss = criterion(output, target) - optimizer.zero_grad() - loss.backward() - optimizer.step() - if cnt >= iters: - break - - def eval_func(model): - model.eval() - acc = 0 - for image, target in dummy_dataloader: - output = model(image).cpu().detach().numpy() - acc += np.sum(output == target) - return {"acc": acc / len(dummy_dataset)} - - for approach, search_algorithm in [(None, None), ("basic", "grid"), ("basic", "random"), ("basic", "bo")]: - print("{fix}Search algorithm: {msg}{fix}".format(msg=search_algorithm, fix="=" * 30)) - search_space = {"channels": [16, 32], "dimensions": [32]} - nas_config = NASConfig(approach=approach, search_space=search_space, search_algorithm=search_algorithm) - nas_config.usr_cfg.model.framework = "pytorch" - nas_agent = NAS(nas_config) - nas_agent.model_builder = model_builder - nas_agent.train_func = train_func - nas_agent.eval_func = eval_func - best_model_archs = nas_agent() - self.assertTrue(len(best_model_archs) > 0) - - def test_dynas(self): - nas_agent = NAS("dynas_fake.yaml") - for search_algorithm, supernet in [ - ("nsga2", "ofa_mbv3_d234_e346_k357_w1.2"), - ("age", "ofa_mbv3_d234_e346_k357_w1.2"), - ]: - config = NASConfig(approach="dynas", search_algorithm=search_algorithm) - config.dynas.supernet = supernet - config.dynas.metrics = ["params", "latency"] - config.dynas.population = 10 - config.dynas.num_evals = 10 - config.nas.search.seed = 71 - config.dynas.batch_size = 1 - config.dynas.results_csv_path = "search_results.csv" - nas_agent = NAS(config) - best_model_archs = nas_agent.search() - self.assertTrue(len(best_model_archs) == config.dynas.population) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/objective/test_objective.py b/test/objective/test_objective.py index 3f926cf862b..c1171031379 100644 --- a/test/objective/test_objective.py +++ b/test/objective/test_objective.py @@ -1,367 +1,9 @@ """Tests for neural_compressor quantization.""" -import importlib -import os -import random -import shutil import unittest -import numpy as np -import yaml - - -def build_fake_yaml_footprint(): - fake_yaml = """ - model: - name: fake_yaml - framework: tensorflow - inputs: x - outputs: op_to_store - device: cpu - evaluation: - accuracy: - metric: - topk: 1 - performance: {} - tuning: - objective: footprint - strategy: - name: fake - accuracy_criterion: - relative: 0.01 - workspace: - path: saved - """ - y = yaml.load(fake_yaml, Loader=yaml.SafeLoader) - with open("fake_yaml_footprint.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - f.close() - - -def build_fake_yaml_model_size(): - fake_yaml = """ - model: - name: fake_yaml - framework: tensorflow - inputs: x - outputs: op_to_store - device: cpu - evaluation: - accuracy: - metric: - topk: 1 - performance: {} - tuning: - objective: modelsize - strategy: - name: fake - accuracy_criterion: - relative: 0.01 - workspace: - path: saved - """ - y = yaml.load(fake_yaml, Loader=yaml.SafeLoader) - with open("fake_yaml_model_size.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - f.close() - - -def build_fake_yaml(): - fake_yaml = """ - model: - name: fake_yaml - framework: tensorflow - inputs: x - outputs: op_to_store - device: cpu - evaluation: - accuracy: - metric: - topk: 1 - performance: {} - tuning: - strategy: - name: fake - accuracy_criterion: - relative: 0.01 - workspace: - path: saved - """ - y = yaml.load(fake_yaml, Loader=yaml.SafeLoader) - with open("fake_yaml.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - f.close() - - -def build_fake_model(): - import tensorflow as tf - - try: - graph = tf.Graph() - graph_def = tf.GraphDef() - with tf.Session(graph=graph) as sess: - x = tf.placeholder(tf.float64, shape=(1, 256, 256, 1), name="x") - y = tf.constant(np.random.random((2, 2, 1, 1)), name="y") - op = tf.nn.conv2d(input=x, filter=y, strides=[1, 1, 1, 1], padding="VALID", name="op_to_store") - - sess.run(tf.global_variables_initializer()) - constant_graph = tf.graph_util.convert_variables_to_constants(sess, sess.graph_def, ["op_to_store"]) - - graph_def.ParseFromString(constant_graph.SerializeToString()) - with graph.as_default(): - tf.import_graph_def(graph_def, name="") - except: - import tensorflow as tf - - graph = tf.Graph() - graph_def = tf.compat.v1.GraphDef() - with tf.compat.v1.Session(graph=graph) as sess: - x = tf.compat.v1.placeholder(tf.float64, shape=(1, 256, 256, 1), name="x") - y = tf.compat.v1.constant(np.random.random((3, 3, 1, 1)), name="y") - op = tf.nn.conv2d(input=x, filters=y, strides=[1, 1, 1, 1], padding="VALID", name="op_to_store") - - sess.run(tf.compat.v1.global_variables_initializer()) - constant_graph = tf.compat.v1.graph_util.convert_variables_to_constants( - sess, sess.graph_def, ["op_to_store"] - ) - - graph_def.ParseFromString(constant_graph.SerializeToString()) - with graph.as_default(): - tf.import_graph_def(graph_def, name="") - return graph - - -def build_fake_model1(): - import tensorflow as tf - - try: - graph = tf.Graph() - graph_def = tf.GraphDef() - with tf.Session(graph=graph) as sess: - x = tf.placeholder(tf.float64, shape=(1, 256, 256, 1), name="x") - y_1 = tf.constant(np.random.random((3, 3, 1, 1)), name="y_1") - y_2 = tf.constant(np.random.random((3, 3, 1, 1)), name="y_2") - conv1 = tf.nn.conv2d(input=x, filter=y_1, strides=[1, 1, 1, 1], padding="VALID", name="conv1") - op = tf.nn.conv2d(input=conv1, filter=y_2, strides=[1, 1, 1, 1], padding="VALID", name="op_to_store") - - sess.run(tf.global_variables_initializer()) - constant_graph = tf.graph_util.convert_variables_to_constants(sess, sess.graph_def, ["op_to_store"]) - - graph_def.ParseFromString(constant_graph.SerializeToString()) - with graph.as_default(): - tf.import_graph_def(graph_def, name="") - except: - import tensorflow as tf - - graph = tf.Graph() - graph_def = tf.compat.v1.GraphDef() - with tf.compat.v1.Session(graph=graph) as sess: - x = tf.compat.v1.placeholder(tf.float64, shape=(1, 256, 256, 1), name="x") - y_1 = tf.constant(np.random.random((3, 3, 1, 1)), name="y_1") - y_2 = tf.constant(np.random.random((3, 3, 1, 1)), name="y_2") - conv1 = tf.nn.conv2d(input=x, filters=y_1, strides=[1, 1, 1, 1], padding="VALID", name="conv1") - op = tf.nn.conv2d(input=conv1, filters=y_2, strides=[1, 1, 1, 1], padding="VALID", name="op_to_store") - - sess.run(tf.compat.v1.global_variables_initializer()) - constant_graph = tf.compat.v1.graph_util.convert_variables_to_constants( - sess, sess.graph_def, ["op_to_store"] - ) - - graph_def.ParseFromString(constant_graph.SerializeToString()) - with graph.as_default(): - tf.import_graph_def(graph_def, name="") - return graph - - -def build_fake_strategy(): - with open( - os.path.join( - os.path.dirname(importlib.util.find_spec("neural_compressor").origin), "experimental/strategy/fake.py" - ), - "w", - encoding="utf-8", - ) as f: - seq = [ - "import time \n", - "import copy \n", - "import numpy as np \n", - "from collections import OrderedDict \n", - "from .strategy import strategy_registry, TuneStrategy \n", - "from ...utils import logger \n", - "from .utils.tuning_sampler import OpTypeWiseTuningSampler, FallbackTuningSampler \n", - "from .utils.tuning_structs import OpTuningConfig \n", - "import copy \n", - "@strategy_registry \n", - "class FakeTuneStrategy(TuneStrategy): \n", - " def __init__(self, model, cfg, q_dataloader, q_func=None, eval_dataloader=None, \n", - " eval_func=None, dicts=None, q_hooks=None): \n", - " self.id = 0 \n", - " self.resume = True if dicts else False \n", - " super(FakeTuneStrategy, self).__init__(model, cfg, q_dataloader, \n", - " q_func, eval_dataloader, eval_func, dicts) \n", - " def __getstate__(self): \n", - " for history in self.tuning_history: \n", - " if self._same_yaml(history['cfg'], self.cfg): \n", - " history['id'] = self.id \n", - " save_dict = super(FakeTuneStrategy, self).__getstate__() \n", - " return save_dict \n", - " def next_tune_cfg(self): \n", - " if self.resume: \n", - " #assert self.id == 1 \n", - " assert len(self.tuning_history) == 1 \n", - " history = self.tuning_history[0] \n", - " assert self._same_yaml(history['cfg'], self.cfg) \n", - " assert len(history['history']) \n", - " for h in history['history']: \n", - " assert h \n", - " from copy import deepcopy \n", - " tuning_space = self.tuning_space \n", - " initial_op_tuning_cfg = {} \n", - " for item in tuning_space.root_item.options: \n", - " if item.item_type == 'op': \n", - " op_name, op_type = item.name \n", - " initial_op_tuning_cfg[item.name] = OpTuningConfig(op_name, op_type, 'fp32', tuning_space) \n", - " calib_sampling_size_lst = tuning_space.root_item.get_option_by_name('calib_sampling_size').options \n", - " for calib_sampling_size in calib_sampling_size_lst: \n", - " # step1. collect the ops that support static and dynamic \n", - " quant_mode_wise_items = OrderedDict() \n", - " query_order = ['static', 'dynamic', 'bf16', 'fp16', 'fp32'] \n", - " pre_items = set() \n", - " for quant_mode in query_order: \n", - " items = tuning_space.query_items_by_quant_mode(quant_mode) \n", - " filtered_items = [item for item in items if item not in pre_items] \n", - " pre_items = pre_items.union(set(items)) \n", - " quant_mode_wise_items[quant_mode] = filtered_items \n", - " def initial_op_quant_mode(items_lst, target_quant_mode, op_item_dtype_dict): \n", - " for item in items_lst: \n", - " op_item_dtype_dict[item.name] = target_quant_mode \n", - " op_item_dtype_dict = OrderedDict() \n", - " for quant_mode, quant_mode_items in quant_mode_wise_items.items(): \n", - " initial_op_quant_mode(quant_mode_items, quant_mode, op_item_dtype_dict) \n", - " # step3. optype-wise tuning tuning items: the algorithm/scheme/granularity of activation(weight) \n", - " early_stop_tuning = False \n", - " stage1_cnt = 0 \n", - " int8_ops = quant_mode_wise_items['dynamic'] + quant_mode_wise_items['static'] \n", - " stage1_max = min(5, len(int8_ops)) # TODO set a more appropriate value \n", - " op_wise_tuning_sampler = OpTypeWiseTuningSampler(tuning_space, [], [], \n", - " op_item_dtype_dict, initial_op_tuning_cfg) \n", - " for op_tuning_cfg in op_wise_tuning_sampler: \n", - " stage1_cnt += 1 \n", - " if early_stop_tuning and stage1_cnt > stage1_max: \n", - " logger.info('Early stopping the stage 1.') \n", - " break \n", - " op_tuning_cfg['calib_sampling_size'] = calib_sampling_size \n", - " self.id += 1 \n", - " yield op_tuning_cfg \n", - ] - f.writelines(seq) - f.close() - - -class TestObjective(unittest.TestCase): - @classmethod - def setUpClass(self): - self.constant_graph = build_fake_model() - self.constant_graph_1 = build_fake_model1() - build_fake_yaml() - build_fake_yaml_footprint() - build_fake_yaml_model_size() - build_fake_strategy() - - @classmethod - def tearDownClass(self): - os.remove("fake_yaml.yaml") - os.remove("fake_yaml_model_size.yaml") - os.remove("fake_yaml_footprint.yaml") - os.remove( - os.path.join( - os.path.dirname(importlib.util.find_spec("neural_compressor").origin), "experimental/strategy/fake.py" - ) - ) - shutil.rmtree("./saved", ignore_errors=True) - - def test_performance(self): - from neural_compressor.data import Datasets - - dataset = Datasets("tensorflow")["dummy"]((100, 256, 256, 1), label=True) - - from neural_compressor.experimental import Quantization, common - from neural_compressor.model import tensorflow_model - - quantizer = Quantization("fake_yaml.yaml") - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.model = self.constant_graph - q_model = quantizer.fit() - self.assertTrue(isinstance(q_model, tensorflow_model.TensorflowBaseModel)) - - from neural_compressor.experimental import Benchmark, common - - benchmarker = Benchmark("fake_yaml.yaml") - benchmarker.b_dataloader = common.DataLoader(dataset) - benchmarker.model = self.constant_graph_1 - benchmarker.fit(mode="accuracy") - - def test_model_size(self): - from neural_compressor.data import Datasets - from neural_compressor.experimental import Benchmark, common - - dataset = Datasets("tensorflow")["dummy"]((100, 256, 256, 1), label=True) - - benchmarker = Benchmark("fake_yaml_model_size.yaml") - benchmarker.b_dataloader = common.DataLoader(dataset) - benchmarker.model = self.constant_graph_1 - benchmarker(mode="accuracy") - - def test_footprint(self): - from neural_compressor.data import Datasets - from neural_compressor.experimental import Benchmark, common - - dataset = Datasets("tensorflow")["dummy"]((100, 256, 256, 1), label=True) - - benchmarker = Benchmark("fake_yaml_footprint.yaml") - benchmarker.b_dataloader = common.DataLoader(dataset) - benchmarker.model = self.constant_graph_1 - benchmarker.fit(mode="accuracy") - - -def build_matmul_model(): - from onnx import TensorProto, helper - - A = helper.make_tensor_value_info("A", TensorProto.FLOAT, [1, 1, 5, 5]) - B = helper.make_tensor_value_info("B", TensorProto.FLOAT, [1, 1, 5, 1]) - C = helper.make_tensor_value_info("C", TensorProto.FLOAT, [1, 1, 5, 1]) - matmul_node = helper.make_node("MatMul", ["A", "B"], ["C"], name="Matmul") - graph = helper.make_graph([matmul_node], "test_graph_1", [A, B], [C]) - model = helper.make_model(graph) - model = helper.make_model(graph, **{"opset_imports": [helper.make_opsetid("", 13)]}) - return model - class TestObjs(unittest.TestCase): - def test_model(self): - def eval(model): - return random.random() - - model = build_matmul_model() - - from neural_compressor.conf.config import conf - from neural_compressor.experimental import Quantization - from neural_compressor.model import onnx_model - - conf.model.framework = "onnxrt_integerops" - conf.quantization.approach = "post_training_dynamic_quant" - conf.tuning.accuracy_criterion.absolute = 0.3 - conf.tuning.multi_objectives.objective = ["accuracy", "performance"] - conf.tuning.multi_objectives.weight = [0.8, 0.2] - conf.tuning.exit_policy.timeout = 10000 - conf.tuning.exit_policy.max_trials = 2 - quantize = Quantization(conf) - quantize.model = model - quantize.eval_func = eval - q_model = quantize() - self.assertTrue(isinstance(q_model, onnx_model.ONNXModel)) - self.assertTrue("quantize" in str(q_model.model.producer_name)) def test_tune_data(self): from neural_compressor.objective import MultiObjective diff --git a/test/pruning_with_pt/pruning_1.x_v1/test_pruning_experimental.py b/test/pruning_with_pt/pruning_1.x_v1/test_pruning_experimental.py deleted file mode 100644 index 11bd2031344..00000000000 --- a/test/pruning_with_pt/pruning_1.x_v1/test_pruning_experimental.py +++ /dev/null @@ -1,202 +0,0 @@ -import os -import shutil -import unittest - -import torch -import torch.nn as nn -import torchvision - -from neural_compressor.data import Datasets -from neural_compressor.experimental.data.dataloaders.pytorch_dataloader import PyTorchDataLoader -from neural_compressor.experimental.pruning import Pruning # old API - - -def build_fake_yaml_basic(): - fake_snip_yaml = """ - model: - name: imagenet_prune - framework: pytorch - - pruning: - approach: - weight_compression_pytorch: - initial_sparsity: 0.0 - target_sparsity: 0.9 - start_step: 0 - end_step: 10 - excluded_names: ["classifier"] - - update_frequency_on_step: 1 - sparsity_decay_type: "exp" - pruners: - - !Pruner - start_step: 0 - sparsity_decay_type: "cos" - end_step: 10 - prune_type: "magnitude" - names: ['layer1.*'] - extra_excluded_names: ['layer2.*'] - prune_domain: "global" - pattern: "tile_pattern_4x1" - - - !Pruner - start_step: 1 - end_step: 1 - target_sparsity: 0.5 - prune_type: "snip_momentum" - update_frequency: 2 - names: ['layer2.*'] - prune_domain: local - pattern: "tile_pattern_2:4" - - - !Pruner - start_step: 2 - end_step: 8 - target_sparsity: 0.8 - prune_type: "snip" - names: ['layer3.*'] - prune_domain: "local" - pattern: "tile_pattern_16x1" - sparsity_decay_type: "cube" - - """ - with open("fake_snip.yaml", "w", encoding="utf-8") as f: - f.write(fake_snip_yaml) - - -def build_fake_yaml_channel(): - fake_channel_pruning_yaml = """ - model: - name: imagenet_prune - framework: pytorch - - pruning: - approach: - weight_compression_pytorch: - initial_sparsity: 0.0 - target_sparsity: 0.9 - start_step: 0 - end_step: 10 - excluded_names: ["classifier"] - - update_frequency_on_step: 1 - sparsity_decay_type: "exp" - pruners: - - !Pruner - start_step: 5 - end_step: 5 - prune_type: "pattern_lock" - names: ['layer1.*'] - extra_excluded_names: ['layer2.*'] - prune_domain: "global" - pattern: "channelx1" - - - !Pruner - start_step: 1 - end_step: 1 - target_sparsity: 0.5 - prune_type: "pattern_lock" - update_frequency: 2 - names: ['layer2.*'] - prune_domain: local - pattern: "2:4" - - - !Pruner - start_step: 2 - end_step: 8 - target_sparsity: 0.8 - prune_type: "snip" - names: ['layer3.*'] - prune_domain: "local" - pattern: "1xchannel" - sparsity_decay_type: "cube" - - """ - - with open("fake_channel_pruning.yaml", "w", encoding="utf-8") as f: - f.write(fake_channel_pruning_yaml) - - -class TestPytorchPruning(unittest.TestCase): - model = torchvision.models.resnet18() - - @classmethod - def setUpClass(cls): - build_fake_yaml_basic() - build_fake_yaml_channel() - - @classmethod - def tearDownClass(cls): - os.remove("fake_channel_pruning.yaml") - os.remove("fake_snip.yaml") - shutil.rmtree("./saved", ignore_errors=True) - shutil.rmtree("runs", ignore_errors=True) - - def test_pytorch_pruning_basic(self): - prune = Pruning("fake_snip.yaml") - prune.update_items_for_all_pruners(start_step=1) - prune.model = self.model - - criterion = nn.CrossEntropyLoss() - optimizer = torch.optim.SGD(self.model.parameters(), lr=0.0001) - datasets = Datasets("pytorch") - dummy_dataset = datasets["dummy"](shape=(10, 3, 224, 224), low=0.0, high=1.0, label=True) - dummy_dataloader = PyTorchDataLoader(dummy_dataset) - - prune.prepare() - prune.on_train_begin() - for epoch in range(2): - self.model.train() - prune.on_epoch_begin(epoch) - local_step = 0 - for image, target in dummy_dataloader: - prune.on_step_begin(local_step) - output = self.model(image) - loss = criterion(output, target) - optimizer.zero_grad() - loss.backward() - prune.on_before_optimizer_step() - optimizer.step() - prune.on_after_optimizer_step() - prune.on_step_end() - local_step += 1 - - prune.on_epoch_end() - prune.get_sparsity_ratio() - prune.on_train_end() - prune.on_before_eval() - prune.on_after_eval() - - def test_pytorch_pruner_channel_pruning(self): - prune = Pruning("fake_channel_pruning.yaml") - prune.model = self.model - - criterion = nn.CrossEntropyLoss() - optimizer = torch.optim.SGD(self.model.parameters(), lr=0.0001) - datasets = Datasets("pytorch") - dummy_dataset = datasets["dummy"](shape=(10, 3, 224, 224), low=0.0, high=1.0, label=True) - dummy_dataloader = PyTorchDataLoader(dummy_dataset) - - prune.prepare() - prune.on_train_begin() - for epoch in range(2): - self.model.train() - prune.on_epoch_begin(epoch) - local_step = 0 - for image, target in dummy_dataloader: - prune.on_step_begin(local_step) - output = self.model(image) - loss = criterion(output, target) - optimizer.zero_grad() - loss.backward() - prune.on_before_optimizer_step() - optimizer.step() - prune.on_after_optimizer_step() - prune.on_step_end() - local_step += 1 - - prune.on_epoch_end() - - -if __name__ == "__main__": - unittest.main() diff --git a/test/pruning_with_pt/pruning_1.x_v1/test_pruning_gradient_sensitivity.py b/test/pruning_with_pt/pruning_1.x_v1/test_pruning_gradient_sensitivity.py deleted file mode 100644 index 05ab9b1e918..00000000000 --- a/test/pruning_with_pt/pruning_1.x_v1/test_pruning_gradient_sensitivity.py +++ /dev/null @@ -1,258 +0,0 @@ -import os -import shutil -import unittest - -import torch -import torch.nn as nn -import torchvision - -from neural_compressor.data import Datasets -from neural_compressor.experimental.data.dataloaders.pytorch_dataloader import PyTorchDataLoader - - -def build_fake_yaml(): - fake_yaml = """ - model: - name: gradient_sensitivity_prune - framework: pytorch - pruning: - approach: - weight_compression: - start_epoch: 0 - end_epoch: 1 - pruners: - - !Pruner - start_epoch: 0 - end_epoch: 1 - prune_type: gradient_sensitivity - update_frequency: 1 - names: [ - 'bert.encoder.layer.*.attention.self.query.weight', - 'bert.encoder.layer.*.attention.self.query.bias', - 'bert.encoder.layer.*.attention.self.key.weight', - 'bert.encoder.layer.*.attention.self.key.bias', - 'bert.encoder.layer.*.attention.self.value.weight', - 'bert.encoder.layer.*.attention.self.value.bias', - ] - parameters: { - target: 8, - normalize: True, - stride: 64, - transpose: False, - importance_inputs: ['head_mask'], - importance_metric: abs_gradient, - } - - - !Pruner - start_epoch: 0 - end_epoch: 1 - prune_type: gradient_sensitivity - update_frequency: 1 - names: [ - 'bert.encoder.layer.*.attention.output.dense.weight', - ] - parameters: { - target: 8, - normalize: True, - stride: 64, - transpose: True, - importance_inputs: ['head_mask'], - importance_metric: abs_gradient, - } - - - !Pruner - prune_type: gradient_sensitivity - names: [ - 'bert.encoder.layer.*.intermediate.dense.weight', - 'bert.encoder.layer.*.intermediate.dense.bias', - ] - parameters: { - target: 600, - normalize: False, - stride: 1, - transpose: False, - importance_inputs: [ - 'bert.encoder.layer.*.intermediate.dense.weight', - 'bert.encoder.layer.*.intermediate.dense.bias', - ], - importance_metric: 'weighted_gradient', - } - - - !Pruner - prune_type: gradient_sensitivity - names: [ - 'bert.encoder.layer.*.output.dense.weight', - ] - parameters: { - target: 600, - normalize: False, - stride: 1, - transpose: True, - importance_inputs: [ - 'bert.encoder.layer.*.intermediate.dense.weight', - 'bert.encoder.layer.*.intermediate.dense.bias', - ], - importance_metric: 'weighted_gradient', - } - - tuning: - accuracy_criterion: - relative: 0.1 # only verifying workflow, accuracy loss percentage: 10% - exit_policy: - timeout: 0 # tuning timeout (seconds) - random_seed: 9527 # random seed - """ - with open("fake.yaml", "w", encoding="utf-8") as f: - f.write(fake_yaml) - - -def build_fake_yaml_unstructured(): - fake_yaml_unstructured = """ - model: - name: imagenet_prune - framework: pytorch - - pruning: - approach: - weight_compression: - initial_sparsity: 0.0 - start_epoch: 0 - end_epoch: 4 - pruners: - - !Pruner - start_epoch: 1 - end_epoch: 3 - target_sparsity: 0.8 - prune_type: gradient_sensitivity - names: ['layer1.0.conv1.weight'] - - - !Pruner - target_sparsity: 0.6 - prune_type: basic_magnitude - update_frequency: 2 - names: ['layer1.0.conv2.weight'] - evaluation: - accuracy: - metric: - topk: 1 - """ - with open("fake_unstructured.yaml", "w", encoding="utf-8") as f: - f.write(fake_yaml_unstructured) - - -class TestGradientSensitivity(unittest.TestCase): - @classmethod - def setUpClass(cls): - build_fake_yaml() - - @classmethod - def tearDownClass(cls): - os.remove("fake.yaml") - shutil.rmtree("./saved", ignore_errors=True) - shutil.rmtree("runs", ignore_errors=True) - - def test_gradient_sensitivity(self): - from neural_compressor.experimental import Pruning, common - - prune = Pruning("fake.yaml") - - from transformers import BertForSequenceClassification - - model = BertForSequenceClassification.from_pretrained("bert-base-uncased") - - def training_func_for_nc(model): - inputs = { - "input_ids": torch.rand([1, 12]).long(), - "attention_mask": torch.rand([1, 12]).long(), - "labels": torch.tensor([1]).long(), - } - model.eval() - - # To calculate head prune - prune.on_epoch_begin(0) - head_mask = torch.ones(model.config.num_hidden_layers, model.config.num_attention_heads) - head_mask.requires_grad_(requires_grad=True) - - outputs = model(output_attentions=True, **inputs, head_mask=head_mask) - tmp_eval_loss, logits = outputs[:2] - tmp_eval_loss.backward() - prune.on_step_end() - prune.on_epoch_end() - - def eval_func_for_nc(model): - pass - - prune.model = model - prune.pruning_func = training_func_for_nc - prune.eval_func = eval_func_for_nc - _ = prune() - for bertlayer in model.bert.encoder.layer: - self.assertEqual(bertlayer.attention.self.query.weight.shape, (512, 768)) - self.assertEqual(bertlayer.attention.self.key.weight.shape, (512, 768)) - self.assertEqual(bertlayer.attention.self.value.weight.shape, (512, 768)) - self.assertEqual(bertlayer.attention.output.dense.weight.shape, (768, 512)) - self.assertEqual(bertlayer.intermediate.dense.weight.shape, (600, 768)) - self.assertEqual(bertlayer.output.dense.weight.shape, (768, 600)) - - -class TestGradientSensitivityUnstructured(unittest.TestCase): - cv_model = torchvision.models.resnet18() - - @classmethod - def setUpClass(cls): - build_fake_yaml_unstructured() - - @classmethod - def tearDownClass(cls): - os.remove("fake_unstructured.yaml") - shutil.rmtree("./saved", ignore_errors=True) - shutil.rmtree("runs", ignore_errors=True) - - def test_unstructured_pruning(self): - from neural_compressor.experimental import Pruning, common - - prune_cv = Pruning("fake_unstructured.yaml") - datasets = Datasets("pytorch") - dummy_dataset = datasets["dummy"](shape=(100, 3, 224, 224), low=0.0, high=1.0, label=True) - dummy_dataloader = PyTorchDataLoader(dummy_dataset) - - def training_func_for_cv(model): - epochs = 5 - iters = 3 - criterion = nn.CrossEntropyLoss() - optimizer = torch.optim.SGD(model.parameters(), lr=0.0001) - prune_cv.on_train_begin() - for nepoch in range(epochs): - model.train() - cnt = 0 - prune_cv.on_epoch_begin(nepoch) - for image, target in dummy_dataloader: - prune_cv.on_step_begin(cnt) - print(".", end="") - cnt += 1 - output = model(image) - loss = criterion(output, target) - optimizer.zero_grad() - loss.backward() - optimizer.step() - prune_cv.on_step_end() - if cnt >= iters: - break - prune_cv.on_epoch_end() - prune_cv.on_train_end() - - prune_cv.model = self.cv_model - prune_cv.pruning_func = training_func_for_cv - prune_cv.eval_dataloader = dummy_dataloader - prune_cv.train_dataloader = dummy_dataloader - _ = prune_cv() - - # assert sparsity ratio - conv1_weight = self.cv_model.layer1[0].conv1.weight - conv2_weight = self.cv_model.layer1[0].conv2.weight - self.assertAlmostEqual((conv1_weight == 0).sum().item() / conv1_weight.numel(), 0.8, delta=0.01) - self.assertAlmostEqual((conv2_weight == 0).sum().item() / conv2_weight.numel(), 0.48, delta=0.01) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/pruning_with_pt/pruning_1.x_v1/test_pruning_group_lasso.py b/test/pruning_with_pt/pruning_1.x_v1/test_pruning_group_lasso.py deleted file mode 100644 index 9a8325005c5..00000000000 --- a/test/pruning_with_pt/pruning_1.x_v1/test_pruning_group_lasso.py +++ /dev/null @@ -1,109 +0,0 @@ -import os -import shutil -import unittest - -import torch -import torch.nn as nn -import torchvision - -from neural_compressor.data import Datasets -from neural_compressor.experimental.data.dataloaders.pytorch_dataloader import PyTorchDataLoader - - -def build_fake_yaml(): - fake_yaml = """ - model: - name: imagenet_prune - framework: pytorch - - pruning: - train: - start_epoch: 0 - end_epoch: 4 - iteration: 10 - dataloader: - batch_size: 30 - dataset: - dummy: - shape: [128, 3, 224, 224] - label: True - optimizer: - SGD: - learning_rate: 0.1 - momentum: 0.1 - nesterov: True - weight_decay: 0.1 - criterion: - CrossEntropyLoss: - reduction: sum - approach: - weight_compression: - initial_sparsity: 0.0 - target_sparsity: 0.97 - start_epoch: 0 - end_epoch: 4 - pruners: - - !Pruner - start_epoch: 1 - end_epoch: 3 - prune_type: group_lasso - names: ['layer1.0.conv1.weight'] - parameters: { - alpha: 0.006, - pattern: tile_pattern_1x16 - } - - - !Pruner - target_sparsity: 0.6 - prune_type: group_lasso - update_frequency: 2 - names: ['layer1.0.conv2.weight'] - parameters: { - alpha: 0.006, - pattern: tile_pattern_1x16 - } - evaluation: - accuracy: - metric: - topk: 1 - dataloader: - batch_size: 30 - dataset: - dummy: - shape: [128, 3, 224, 224] - label: True - """ - with open("fake.yaml", "w", encoding="utf-8") as f: - f.write(fake_yaml) - - -class TestPruningGroupLasso(unittest.TestCase): - model = torchvision.models.resnet18() - - @classmethod - def setUpClass(cls): - build_fake_yaml() - - @classmethod - def tearDownClass(cls): - os.remove("fake.yaml") - shutil.rmtree("./saved", ignore_errors=True) - shutil.rmtree("runs", ignore_errors=True) - - def test_pruning_internal(self): - from neural_compressor.experimental import Pruning, common - - prune = Pruning("fake.yaml") - - prune.model = self.model - _ = prune() - - # assert sparsity ratio - conv1_weight = self.model.layer1[0].conv1.weight - conv2_weight = self.model.layer1[0].conv2.weight - self.assertAlmostEqual((conv1_weight == 0).sum().item() / conv1_weight.numel(), 0.97, delta=0.01) - self.assertAlmostEqual((conv2_weight == 0).sum().item() / conv2_weight.numel(), 0.48, delta=0.01) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/pruning_with_pt/pruning_1.x_v1/test_pruning_pattern.py b/test/pruning_with_pt/pruning_1.x_v1/test_pruning_pattern.py deleted file mode 100644 index 0b4f0ff73b6..00000000000 --- a/test/pruning_with_pt/pruning_1.x_v1/test_pruning_pattern.py +++ /dev/null @@ -1,44 +0,0 @@ -import copy -import random -import unittest - -import numpy as np - -from neural_compressor.experimental.pruning_recipes.patterns import patterns - - -class TestPruningPattern(unittest.TestCase): - tensor_4d = np.random.random([560, 560, 3, 3]) - tensor_2d = np.random.random([1280, 640]) - - def test_tile_pattern(self): - for tensor in [self.tensor_2d, self.tensor_4d]: - shape = list(tensor.shape) - size = tensor.size - - for mask_shape in [(1, 1), (2, 2), (1, 16), (4, 1), (1, 2)]: - m0 = mask_shape[0] - m1 = mask_shape[1] - pattern = patterns["tile_pattern_{}x{}".format(m0, m1)]() - new_shape = [shape[0] / m0] + [size // shape[0] / m1] - sparse_tensor = self.sparsify_tensor(tensor, [m0, m1], 0.2) - reduced_tensor = pattern.reduce(sparse_tensor) - self.assertEqual(list(reduced_tensor.shape), new_shape) - self.assertAlmostEqual(pattern.compute_sparsity(sparse_tensor), 0.2, delta=0.01) - mask = reduced_tensor == 0 - repeat_mask = pattern.repeat_mask(mask, ori_shape=tensor.shape) - self.assertEqual(repeat_mask.shape, tensor.shape) - - def sparsify_tensor(self, tensor, mask_shape, ratio): - tensor = copy.deepcopy(tensor) - for i in range(tensor.shape[0] // mask_shape[0]): - for j in range(tensor.shape[1] // mask_shape[1]): - if random.random() < ratio: - tensor[ - i * mask_shape[0] : (i + 1) * mask_shape[0], j * mask_shape[1] : (j + 1) * mask_shape[1], ... - ] = 0 - return tensor - - -if __name__ == "__main__": - unittest.main() diff --git a/test/pruning_with_pt/pruning_1.x_v1/test_pruning_pattern_lock.py b/test/pruning_with_pt/pruning_1.x_v1/test_pruning_pattern_lock.py deleted file mode 100644 index 8b00cbf7bb3..00000000000 --- a/test/pruning_with_pt/pruning_1.x_v1/test_pruning_pattern_lock.py +++ /dev/null @@ -1,99 +0,0 @@ -import os -import shutil -import unittest - -import torch -import torch.nn as nn -import torchvision - -from neural_compressor.experimental.data.dataloaders.pytorch_dataloader import PyTorchDataLoader -from neural_compressor.experimental.data.datasets.dummy_dataset import DummyDataset - - -def build_fake_yaml(): - fake_yaml = """ - model: - name: imagenet_prune - framework: pytorch - - pruning: - approach: - weight_compression: - start_epoch: 0 - pruners: - - !Pruner - prune_type: pattern_lock - names: ['layer1.0.conv1.weight'] - evaluation: - accuracy: - metric: - topk: 1 - """ - with open("fake.yaml", "w", encoding="utf-8") as f: - f.write(fake_yaml) - - -class TestPatternLock(unittest.TestCase): - model = torchvision.models.resnet18() - - @classmethod - def setUpClass(cls): - build_fake_yaml() - - @classmethod - def tearDownClass(cls): - os.remove("fake.yaml") - shutil.rmtree("./saved", ignore_errors=True) - shutil.rmtree("runs", ignore_errors=True) - - def test_pattern_lock(self): - from neural_compressor.experimental import Pruning, common - - prune = Pruning("fake.yaml") - - weight = self.model.layer1[0].conv1.weight - mask = torch.ones(weight.numel()) - mask[: round(weight.numel() * 0.9)] = 0.0 - mask = mask[torch.randperm(mask.numel())].view(weight.shape) - weight.data = weight * mask - - self.assertTrue(self.model.layer1[0].conv1.weight.ne(0).eq(mask).all()) - - dummy_dataset = DummyDataset([tuple([100, 3, 256, 256])]) - dummy_dataloader = PyTorchDataLoader(dummy_dataset) - - def training_func_for_nc(model): - epochs = 2 - iters = 30 - criterion = nn.CrossEntropyLoss() - optimizer = torch.optim.SGD(model.parameters(), lr=0.0001) - for nepoch in range(epochs): - model.train() - cnt = 0 - prune.on_epoch_begin(nepoch) - for i, (image, target) in enumerate(dummy_dataloader): - prune.on_step_begin(cnt) - print(".", end="") - cnt += 1 - output = model(image) - loss = criterion(output, target) - optimizer.zero_grad() - loss.backward() - optimizer.step() - prune.on_step_end() - if cnt >= iters: - break - prune.on_epoch_end() - - dummy_dataset = DummyDataset(tuple([100, 3, 256, 256]), label=True) - dummy_dataloader = PyTorchDataLoader(dummy_dataset) - prune.model = self.model - prune.pruning_func = training_func_for_nc - prune.eval_dataloader = dummy_dataloader - prune.train_dataloader = dummy_dataloader - _ = prune() - self.assertTrue(self.model.layer1[0].conv1.weight.ne(0).eq(mask).all()) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/pruning_with_pt/pruning_1.x_v1/test_pruning_pure_yaml.py b/test/pruning_with_pt/pruning_1.x_v1/test_pruning_pure_yaml.py deleted file mode 100644 index 89a22b8865b..00000000000 --- a/test/pruning_with_pt/pruning_1.x_v1/test_pruning_pure_yaml.py +++ /dev/null @@ -1,101 +0,0 @@ -import os -import shutil -import unittest - -import torch -import torch.nn as nn -import torchvision - -from neural_compressor.data import Datasets -from neural_compressor.experimental.data.dataloaders.pytorch_dataloader import PyTorchDataLoader - - -def build_fake_yaml(): - fake_yaml = """ - model: - name: imagenet_prune - framework: pytorch - - pruning: - train: - start_epoch: 0 - end_epoch: 4 - iteration: 10 - dataloader: - batch_size: 30 - dataset: - dummy: - shape: [128, 3, 224, 224] - label: True - optimizer: - SGD: - learning_rate: 0.1 - momentum: 0.1 - nesterov: True - weight_decay: 0.1 - criterion: - CrossEntropyLoss: - reduction: sum - approach: - weight_compression: - initial_sparsity: 0.0 - target_sparsity: 0.97 - start_epoch: 0 - end_epoch: 4 - pruners: - - !Pruner - start_epoch: 1 - end_epoch: 3 - prune_type: basic_magnitude - names: ['layer1.0.conv1.weight'] - - - !Pruner - target_sparsity: 0.6 - prune_type: gradient_sensitivity - update_frequency: 2 - names: ['layer1.0.conv2.weight'] - evaluation: - accuracy: - metric: - topk: 1 - dataloader: - batch_size: 30 - dataset: - dummy: - shape: [128, 3, 224, 224] - label: True - """ - with open("fake.yaml", "w", encoding="utf-8") as f: - f.write(fake_yaml) - - -class TestPruning(unittest.TestCase): - model = torchvision.models.resnet18() - - @classmethod - def setUpClass(cls): - build_fake_yaml() - - @classmethod - def tearDownClass(cls): - os.remove("fake.yaml") - shutil.rmtree("./saved", ignore_errors=True) - shutil.rmtree("runs", ignore_errors=True) - - def test_pruning_internal(self): - from neural_compressor.experimental import Pruning, common - - prune = Pruning("fake.yaml") - - prune.model = self.model - _ = prune() - - # assert sparsity ratio - conv1_weight = self.model.layer1[0].conv1.weight - conv2_weight = self.model.layer1[0].conv2.weight - self.assertAlmostEqual((conv1_weight == 0).sum().item() / conv1_weight.numel(), 0.97, delta=0.01) - self.assertAlmostEqual((conv2_weight == 0).sum().item() / conv2_weight.numel(), 0.48, delta=0.01) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/pruning_with_pt/pruning_1.x_v2/test_pruning.py b/test/pruning_with_pt/pruning_1.x_v2/test_pruning.py deleted file mode 100644 index 97b0773ab46..00000000000 --- a/test/pruning_with_pt/pruning_1.x_v2/test_pruning.py +++ /dev/null @@ -1,66 +0,0 @@ -import unittest - -import torch -import torch.nn as nn -import torchvision - -from neural_compressor.conf.pythonic_config import Config, WeightPruningConfig -from neural_compressor.data import Datasets -from neural_compressor.experimental.data.dataloaders.pytorch_dataloader import PyTorchDataLoader -from neural_compressor.experimental.pruning_v2 import Pruning - - -class TestPruning(unittest.TestCase): - model = torchvision.models.resnet18() - - def test_pruning_basic(self): - local_configs = [ - { - "op_names": ["layer1.*"], - "target_sparsity": 0.5, - "pattern": "8x2", - "pruning_type": "magnitude_progressive", - "false_key": "this is to test unsupported keys", - }, - {"op_names": ["layer2.*"], "target_sparsity": 0.5, "pattern": "2:4"}, - {"op_names": ["layer3.*"], "target_sparsity": 0.7, "pattern": "5x1", "pruning_type": "snip_progressive"}, - ] - conf = WeightPruningConfig(local_configs, target_sparsity=0.8) - config = Config(quantization=None, benchmark=None, pruning=conf, distillation=None) - prune = Pruning(config) - prune.update_config(start_step=1, end_step=10) - prune.model = self.model - - criterion = nn.CrossEntropyLoss() - optimizer = torch.optim.SGD(self.model.parameters(), lr=0.0001) - datasets = Datasets("pytorch") - dummy_dataset = datasets["dummy"](shape=(10, 3, 224, 224), low=0.0, high=1.0, label=True) - dummy_dataloader = PyTorchDataLoader(dummy_dataset) - - prune.on_train_begin() - prune.update_config(pruning_frequency=4) - for epoch in range(2): - self.model.train() - prune.on_epoch_begin(epoch) - local_step = 0 - for image, target in dummy_dataloader: - prune.on_step_begin(local_step) - output = self.model(image) - loss = criterion(output, target) - optimizer.zero_grad() - loss.backward() - prune.on_before_optimizer_step() - optimizer.step() - prune.on_after_optimizer_step() - prune.on_step_end() - local_step += 1 - - prune.on_epoch_end() - prune.get_sparsity_ratio() - prune.on_train_end() - prune.on_before_eval() - prune.on_after_eval() - - -if __name__ == "__main__": - unittest.main() diff --git a/test/pruning_with_pt/pruning_1.x_v2/test_pruning_config.py b/test/pruning_with_pt/pruning_1.x_v2/test_pruning_config.py deleted file mode 100644 index 7eb2874956b..00000000000 --- a/test/pruning_with_pt/pruning_1.x_v2/test_pruning_config.py +++ /dev/null @@ -1,77 +0,0 @@ -import unittest - -import torch -import torch.nn as nn -import torchvision - -from neural_compressor.conf.pythonic_config import Config, WeightPruningConfig -from neural_compressor.data import Datasets -from neural_compressor.experimental.data.dataloaders.pytorch_dataloader import PyTorchDataLoader -from neural_compressor.experimental.pruning_v2 import Pruning - - -class TestPytorchPruning(unittest.TestCase): - model = torchvision.models.resnet18() - - def test_pruning_class_config(self): - local_configs = [ - { - "op_names": ["layer1.*", "layer2.*"], - "excluded_op_names": ["downsample.*"], - "target_sparsity": 0.6, - "pattern": "channelx1", - "pruning_type": "snip_progressive", - "pruning_scope": "local", - "start_step": 0, - "end_step": 10, - }, - {"op_names": ["layer3.*"], "pruning_type": "pattern_lock"}, - ] - conf = WeightPruningConfig( - local_configs, - pruning_frequency=2, - target_sparsity=0.8, - ) - config = Config(quantization=None, benchmark=None, pruning=conf, distillation=None) - prune = Pruning(config) - prune.model = self.model - - criterion = nn.CrossEntropyLoss() - optimizer = torch.optim.SGD(self.model.parameters(), lr=0.0001) - datasets = Datasets("pytorch") - dummy_dataset = datasets["dummy"](shape=(12, 3, 224, 224), low=0.0, high=1.0, label=True) - dummy_dataloader = PyTorchDataLoader(dummy_dataset) - - prune.update_config(pruning_frequency=4) - prune.on_train_begin() - assert prune.pruners[0].config["pruning_frequency"] == 4 - assert prune.pruners[0].config["target_sparsity"] == 0.6 - assert prune.pruners[1].config["target_sparsity"] == 0.8 - assert prune.pruners[0].config["pattern"] == "channelx1" - assert prune.pruners[1].config["pruning_type"] == "pattern_lock" - - for epoch in range(1): - self.model.train() - prune.on_epoch_begin(epoch) - local_step = 0 - for image, target in dummy_dataloader: - prune.on_step_begin(local_step) - output = self.model(image) - loss = criterion(output, target) - optimizer.zero_grad() - loss.backward() - prune.on_before_optimizer_step() - optimizer.step() - prune.on_after_optimizer_step() - prune.on_step_end() - local_step += 1 - - prune.on_epoch_end() - prune.get_sparsity_ratio() - prune.on_train_end() - prune.on_before_eval() - prune.on_after_eval() - - -if __name__ == "__main__": - unittest.main() diff --git a/test/pruning_with_pt/pruning_1.x_v2/test_pruning_criteria.py b/test/pruning_with_pt/pruning_1.x_v2/test_pruning_criteria.py deleted file mode 100644 index ed76f5d90bb..00000000000 --- a/test/pruning_with_pt/pruning_1.x_v2/test_pruning_criteria.py +++ /dev/null @@ -1,83 +0,0 @@ -import unittest - -import torch -import torch.nn as nn -import torchvision - -from neural_compressor.conf.pythonic_config import Config, WeightPruningConfig -from neural_compressor.data import Datasets -from neural_compressor.experimental.data.dataloaders.pytorch_dataloader import PyTorchDataLoader -from neural_compressor.experimental.pruning_v2 import Pruning - - -class TestPruningCriteria(unittest.TestCase): - model = torchvision.models.resnet18() - - def test_pruning_criteria(self): - local_configs = [ - { - "op_names": ["layer1.*"], - "target_sparsity": 0.4, - "pattern": "8x2", - "pruning_type": "magnitude_progressive", - "pruning_scope": "local", - "sparsity_decay_type": "cube", - }, - { - "op_names": ["layer2.*"], - "target_sparsity": 0.45, - "pattern": "2:4", - "pruning_type": "snip", - "start_step": 6, - "end_step": 6, - }, - { - "op_names": ["layer3.*"], - "excluded_op_names": ["downsample.*"], - "target_sparsity": 0.7, - "pattern": "4x1", - "pruning_type": "snip_momentum_progressive", - "pruning_frequency": 4, - "min_sparsity_ratio_per_op": 0.5, - "max_sparsity_ratio_per_op": 0.8, - }, - ] - conf = WeightPruningConfig(local_configs, target_sparsity=0.8, sparsity_decay_type="cube") - config = Config(quantization=None, benchmark=None, pruning=conf, distillation=None) - prune = Pruning(config) - prune.update_config(start_step=1, end_step=10) - prune.model = self.model - - criterion = nn.CrossEntropyLoss() - optimizer = torch.optim.SGD(self.model.parameters(), lr=0.0001) - datasets = Datasets("pytorch") - dummy_dataset = datasets["dummy"](shape=(10, 3, 224, 224), low=0.0, high=1.0, label=True) - dummy_dataloader = PyTorchDataLoader(dummy_dataset) - - prune.on_train_begin() - prune.update_config(pruning_frequency=4) - for epoch in range(2): - self.model.train() - prune.on_epoch_begin(epoch) - local_step = 0 - for image, target in dummy_dataloader: - prune.on_step_begin(local_step) - output = self.model(image) - loss = criterion(output, target) - optimizer.zero_grad() - loss.backward() - prune.on_before_optimizer_step() - optimizer.step() - prune.on_after_optimizer_step() - prune.on_step_end() - local_step += 1 - - prune.on_epoch_end() - prune.get_sparsity_ratio() - prune.on_train_end() - prune.on_before_eval() - prune.on_after_eval() - - -if __name__ == "__main__": - unittest.main() diff --git a/test/pruning_with_pt/pruning_1.x_v2/test_pruning_patterns.py b/test/pruning_with_pt/pruning_1.x_v2/test_pruning_patterns.py deleted file mode 100644 index b9db1bbb58f..00000000000 --- a/test/pruning_with_pt/pruning_1.x_v2/test_pruning_patterns.py +++ /dev/null @@ -1,74 +0,0 @@ -import unittest - -import torch -import torch.nn as nn -import torchvision - -from neural_compressor.conf.pythonic_config import Config, WeightPruningConfig -from neural_compressor.data import Datasets -from neural_compressor.experimental.data.dataloaders.pytorch_dataloader import PyTorchDataLoader -from neural_compressor.experimental.pruning_v2 import Pruning - - -class TestPruningPatterns(unittest.TestCase): - model = torchvision.models.resnet18() - - def test_pruning_pattern(self): - local_configs = [ - {"op_names": ["layer1.*"], "target_sparsity": 0.5, "pattern": "5:8", "pruning_type": "magnitude"}, - {"op_names": ["layer2.*"], "pattern": "1xchannel", "pruning_scope": "global"}, - { - "start_step": 2, - "end_step": 20, - "op_names": ["layer3.*"], - "target_sparsity": 0.666666, - "pattern": "4x2", - "pruning_type": "snip_progressive", - "pruning_frequency": 5, - }, - ] - conf = WeightPruningConfig( - local_configs, - target_sparsity=0.8, - sparsity_decay_type="cos", - excluded_op_names=["downsample.*"], - pruning_scope="local", - min_sparsity_ratio_per_op=0.1, - ) - config = Config(quantization=None, benchmark=None, pruning=conf, distillation=None) - prune = Pruning(config) - prune.update_config(start_step=1, end_step=10) - prune.model = self.model - - criterion = nn.CrossEntropyLoss() - optimizer = torch.optim.SGD(self.model.parameters(), lr=0.0001) - datasets = Datasets("pytorch") - dummy_dataset = datasets["dummy"](shape=(10, 3, 224, 224), low=0.0, high=1.0, label=True) - dummy_dataloader = PyTorchDataLoader(dummy_dataset) - - prune.on_train_begin() - for epoch in range(5): - self.model.train() - prune.on_epoch_begin(epoch) - local_step = 0 - for image, target in dummy_dataloader: - prune.on_step_begin(local_step) - output = self.model(image) - loss = criterion(output, target) - optimizer.zero_grad() - loss.backward() - prune.on_before_optimizer_step() - optimizer.step() - prune.on_after_optimizer_step() - prune.on_step_end() - local_step += 1 - - prune.on_epoch_end() - prune.get_sparsity_ratio() - prune.on_train_end() - prune.on_before_eval() - prune.on_after_eval() - - -if __name__ == "__main__": - unittest.main() diff --git a/test/pruning_with_pt/pruning_1.x_v2/test_pruning_schedulers.py b/test/pruning_with_pt/pruning_1.x_v2/test_pruning_schedulers.py deleted file mode 100644 index 6c89e1511a0..00000000000 --- a/test/pruning_with_pt/pruning_1.x_v2/test_pruning_schedulers.py +++ /dev/null @@ -1,85 +0,0 @@ -import unittest - -import torch -import torch.nn as nn -import torchvision - -from neural_compressor.conf.pythonic_config import Config, WeightPruningConfig -from neural_compressor.data import Datasets -from neural_compressor.experimental.data.dataloaders.pytorch_dataloader import PyTorchDataLoader -from neural_compressor.experimental.pruning_v2 import Pruning - -local_schedulers_config = [ - { - "start_step": 0, - "end_step": 2, - "pruning_type": "magnitude", - "op_names": ["layer1.*"], - "excluded_op_names": ["layer2.*"], - "pruning_scope": "global", - "target_sparsity": 0.5, - "pattern": "4x1", - }, - { - "start_step": 1, - "end_step": 10, - "pruning_type": "snip_momentum", - "pruning_frequency": 2, - "op_names": ["layer2.*"], - "pruning_scope": "local", - "target_sparsity": 0.75, - "pattern": "32x1", - "sparsity_decay_type": "exp", - }, -] - -fake_snip_config = WeightPruningConfig( - local_schedulers_config, - target_sparsity=0.9, - start_step=0, - end_step=10, - pruning_frequency=1, - sparsity_decay_type="exp", -) - - -class TestPruningCriteria(unittest.TestCase): - model = torchvision.models.resnet18() - - def test_pruning_schedulers(self): - config = Config(quantization=None, benchmark=None, pruning=fake_snip_config, distillation=None) - prune = Pruning(config) - prune.update_config(start_step=1) - prune.model = self.model - criterion = nn.CrossEntropyLoss() - optimizer = torch.optim.SGD(self.model.parameters(), lr=0.0001) - datasets = Datasets("pytorch") - dummy_dataset = datasets["dummy"](shape=(10, 3, 224, 224), low=0.0, high=1.0, label=True) - dummy_dataloader = PyTorchDataLoader(dummy_dataset) - prune.on_train_begin() - prune.update_config(pruning_frequency=1) - for epoch in range(2): - self.model.train() - prune.on_epoch_begin(epoch) - local_step = 0 - for image, target in dummy_dataloader: - prune.on_step_begin(local_step) - output = self.model(image) - loss = criterion(output, target) - optimizer.zero_grad() - loss.backward() - prune.on_before_optimizer_step() - optimizer.step() - prune.on_after_optimizer_step() - prune.on_step_end() - local_step += 1 - - prune.on_epoch_end() - prune.get_sparsity_ratio() - prune.on_train_end() - prune.on_before_eval() - prune.on_after_eval() - - -if __name__ == "__main__": - unittest.main() diff --git a/test/pruning_with_pt/pruning_1.x_v2/test_pruning_types.py b/test/pruning_with_pt/pruning_1.x_v2/test_pruning_types.py deleted file mode 100644 index 4dd3f2518d3..00000000000 --- a/test/pruning_with_pt/pruning_1.x_v2/test_pruning_types.py +++ /dev/null @@ -1,88 +0,0 @@ -import unittest - -import torch -import torch.nn as nn -import torchvision - -from neural_compressor.conf.pythonic_config import Config, WeightPruningConfig -from neural_compressor.data import Datasets -from neural_compressor.experimental.data.dataloaders.pytorch_dataloader import PyTorchDataLoader -from neural_compressor.experimental.pruning_v2 import Pruning - -local_types_config = [ - { - "start_step": 0, - "end_step": 0, - "pruning_type": "pattern_lock", - "op_names": ["layer1.*"], - "excluded_op_names": ["layer2.*"], - "pruning_scope": "global", - }, - { - "start_step": 1, - "end_step": 1, - "target_sparsity": 0.5, - "pruning_type": "snip_momentum_progressive", - "pruning_frequency": 2, - "op_names": ["layer2.*"], - "pruning_scope": "local", - "pattern": "4x1", - "sparsity_decay_type": "exp", - }, - { - "start_step": 2, - "end_step": 8, - "target_sparsity": 0.8, - "pruning_type": "snip_progressive", - "pruning_frequency": 1, - "op_names": ["layer3.*"], - "pruning_scope": "local", - "pattern": "16x1", - "sparsity_decay_type": "cube", - }, -] - -fake_snip_config = WeightPruningConfig( - local_types_config, target_sparsity=0.9, start_step=0, end_step=10, pruning_frequency=3, sparsity_decay_type="exp" -) - - -class TestPruningTypes(unittest.TestCase): - model = torchvision.models.resnet18() - - def test_pruning_types(self): - config = Config(quantization=None, benchmark=None, pruning=fake_snip_config, distillation=None) - prune = Pruning(config) - prune.model = self.model - criterion = nn.CrossEntropyLoss() - optimizer = torch.optim.SGD(self.model.parameters(), lr=0.0001) - datasets = Datasets("pytorch") - dummy_dataset = datasets["dummy"](shape=(10, 3, 224, 224), low=0.0, high=1.0, label=True) - dummy_dataloader = PyTorchDataLoader(dummy_dataset) - prune.on_train_begin() - prune.update_config(pruning_frequency=1) - for epoch in range(2): - self.model.train() - prune.on_epoch_begin(epoch) - local_step = 0 - for image, target in dummy_dataloader: - prune.on_step_begin(local_step) - output = self.model(image) - loss = criterion(output, target) - optimizer.zero_grad() - loss.backward() - prune.on_before_optimizer_step() - optimizer.step() - prune.on_after_optimizer_step() - prune.on_step_end() - local_step += 1 - - prune.on_epoch_end() - prune.get_sparsity_ratio() - prune.on_train_end() - prune.on_before_eval() - prune.on_after_eval() - - -if __name__ == "__main__": - unittest.main() diff --git a/test/pruning_with_pt/pruning_1.x_v2/test_pytorch_pruning_experimental.py b/test/pruning_with_pt/pruning_1.x_v2/test_pytorch_pruning_experimental.py deleted file mode 100644 index 1e7a71da386..00000000000 --- a/test/pruning_with_pt/pruning_1.x_v2/test_pytorch_pruning_experimental.py +++ /dev/null @@ -1,202 +0,0 @@ -import os -import shutil -import unittest - -import torch -import torch.nn as nn -import torchvision - -from neural_compressor.data import Datasets -from neural_compressor.experimental.data.dataloaders.pytorch_dataloader import PyTorchDataLoader -from neural_compressor.experimental.pytorch_pruner.pruning import Pruning - - -def build_fake_yaml_basic(): - fake_snip_yaml = """ - model: - name: imagenet_prune - framework: pytorch - - pruning: - approach: - weight_compression_pytorch: - initial_sparsity: 0.0 - target_sparsity: 0.9 - start_step: 0 - end_step: 10 - excluded_names: ["classifier"] - - update_frequency_on_step: 1 - sparsity_decay_type: "exp" - pruners: - - !Pruner - start_step: 0 - sparsity_decay_type: "cos" - end_step: 10 - prune_type: "magnitude" - names: ['layer1.*'] - extra_excluded_names: ['layer2.*'] - prune_domain: "global" - pattern: "tile_pattern_4x1" - - - !Pruner - start_step: 1 - end_step: 1 - target_sparsity: 0.5 - prune_type: "snip_momentum" - update_frequency: 2 - names: ['layer2.*'] - prune_domain: local - pattern: "tile_pattern_2:4" - - - !Pruner - start_step: 2 - end_step: 8 - target_sparsity: 0.8 - prune_type: "snip" - names: ['layer3.*'] - prune_domain: "local" - pattern: "tile_pattern_16x1" - sparsity_decay_type: "cube" - - """ - with open("fake_snip.yaml", "w", encoding="utf-8") as f: - f.write(fake_snip_yaml) - - -def build_fake_yaml_channel(): - fake_channel_pruning_yaml = """ - model: - name: imagenet_prune - framework: pytorch - - pruning: - approach: - weight_compression_pytorch: - initial_sparsity: 0.0 - target_sparsity: 0.9 - start_step: 0 - end_step: 10 - excluded_names: ["classifier"] - - update_frequency_on_step: 1 - sparsity_decay_type: "exp" - pruners: - - !Pruner - start_step: 5 - end_step: 5 - prune_type: "pattern_lock" - names: ['layer1.*'] - extra_excluded_names: ['layer2.*'] - prune_domain: "global" - pattern: "channelx1" - - - !Pruner - start_step: 1 - end_step: 1 - target_sparsity: 0.5 - prune_type: "pattern_lock" - update_frequency: 2 - names: ['layer2.*'] - prune_domain: local - pattern: "2:4" - - - !Pruner - start_step: 2 - end_step: 8 - target_sparsity: 0.8 - prune_type: "snip" - names: ['layer3.*'] - prune_domain: "local" - pattern: "1xchannel" - sparsity_decay_type: "cube" - - """ - - with open("fake_channel_pruning.yaml", "w", encoding="utf-8") as f: - f.write(fake_channel_pruning_yaml) - - -class TestPytorchPruning(unittest.TestCase): - model = torchvision.models.resnet18() - - @classmethod - def setUpClass(cls): - build_fake_yaml_basic() - build_fake_yaml_channel() - - @classmethod - def tearDownClass(cls): - os.remove("fake_channel_pruning.yaml") - os.remove("fake_snip.yaml") - shutil.rmtree("./saved", ignore_errors=True) - shutil.rmtree("runs", ignore_errors=True) - - def test_pytorch_pruning_basic(self): - prune = Pruning("fake_snip.yaml") - prune.update_items_for_all_pruners(start_step=1) - prune.model = self.model - - criterion = nn.CrossEntropyLoss() - optimizer = torch.optim.SGD(self.model.parameters(), lr=0.0001) - datasets = Datasets("pytorch") - dummy_dataset = datasets["dummy"](shape=(10, 3, 224, 224), low=0.0, high=1.0, label=True) - dummy_dataloader = PyTorchDataLoader(dummy_dataset) - - prune.prepare() - prune.on_train_begin() - for epoch in range(2): - self.model.train() - prune.on_epoch_begin(epoch) - local_step = 0 - for image, target in dummy_dataloader: - prune.on_step_begin(local_step) - output = self.model(image) - loss = criterion(output, target) - optimizer.zero_grad() - loss.backward() - prune.on_before_optimizer_step() - optimizer.step() - prune.on_after_optimizer_step() - prune.on_step_end() - local_step += 1 - - prune.on_epoch_end() - prune.get_sparsity_ratio() - prune.on_train_end() - prune.on_before_eval() - prune.on_after_eval() - - def test_pytorch_pruner_channel_pruning(self): - prune = Pruning("fake_channel_pruning.yaml") - prune.model = self.model - - criterion = nn.CrossEntropyLoss() - optimizer = torch.optim.SGD(self.model.parameters(), lr=0.0001) - datasets = Datasets("pytorch") - dummy_dataset = datasets["dummy"](shape=(10, 3, 224, 224), low=0.0, high=1.0, label=True) - dummy_dataloader = PyTorchDataLoader(dummy_dataset) - - prune.prepare() - prune.on_train_begin() - for epoch in range(2): - self.model.train() - prune.on_epoch_begin(epoch) - local_step = 0 - for image, target in dummy_dataloader: - prune.on_step_begin(local_step) - output = self.model(image) - loss = criterion(output, target) - optimizer.zero_grad() - loss.backward() - prune.on_before_optimizer_step() - optimizer.step() - prune.on_after_optimizer_step() - prune.on_step_end() - local_step += 1 - - prune.on_epoch_end() - - -if __name__ == "__main__": - unittest.main() diff --git a/test/pruning_with_pt/pruning_2.x/test_auto_excluding_classifier.py b/test/pruning_with_pt/pruning_2.x/test_auto_excluding_classifier.py index 0eb2d04005a..641525fd26d 100644 --- a/test/pruning_with_pt/pruning_2.x/test_auto_excluding_classifier.py +++ b/test/pruning_with_pt/pruning_2.x/test_auto_excluding_classifier.py @@ -24,7 +24,6 @@ def forward(self, x): class TestPruning(unittest.TestCase): def test_pruning_basic(self): - # import pdb;pdb.set_trace() hidden_size = 32 model = NaiveMLP(hidden_size) # import classifier searching functions diff --git a/test/pruning_with_pt/pruning_2.x/test_auto_slim.py b/test/pruning_with_pt/pruning_2.x/test_auto_slim.py index 7af5cf8de20..b5f09a3c41d 100644 --- a/test/pruning_with_pt/pruning_2.x/test_auto_slim.py +++ b/test/pruning_with_pt/pruning_2.x/test_auto_slim.py @@ -50,7 +50,6 @@ def test_pruning_basic(self): # run mha and ffn pruning compression_manager = prepare_compression(model=model, confs=configs) compression_manager.callbacks.on_train_begin() - # import pdb;pdb.set_trace() for epoch in range(3): model.train() compression_manager.callbacks.on_epoch_begin(epoch) diff --git a/test/pruning_with_tf/pruning_1.x_v1/test_tensorflow_distributed_pruning.py b/test/pruning_with_tf/pruning_1.x_v1/test_tensorflow_distributed_pruning.py deleted file mode 100644 index 6e9fbdb12b9..00000000000 --- a/test/pruning_with_tf/pruning_1.x_v1/test_tensorflow_distributed_pruning.py +++ /dev/null @@ -1,464 +0,0 @@ -"""Tests for the TensorFlow pruning with distributed training and inference.""" - -import hashlib -import os -import re -import shutil -import signal -import subprocess -import sys -import time -import unittest -from platform import platform, system - -import cpuinfo -import tensorflow as tf - -from neural_compressor.adaptor.tf_utils.util import version1_lt_version2 -from neural_compressor.utils import logger - - -def build_fake_ut(): - fake_ut = ''' -from __future__ import print_function -import tensorflow -from tensorflow.keras.layers import Dense, Conv2D, BatchNormalization, Activation -from tensorflow.keras.layers import AveragePooling2D, Input, Flatten -from tensorflow.keras.callbacks import LearningRateScheduler -from tensorflow.keras.callbacks import ReduceLROnPlateau -from tensorflow.keras.regularizers import l2 -from tensorflow.keras.models import Model -from tensorflow.keras.datasets import cifar10 -import numpy as np -import os -import sys -import cpuinfo -import shutil -import unittest -from neural_compressor.adaptor.tf_utils.util import version1_lt_version2 -from neural_compressor.utils import logger -from neural_compressor.utils.utility import CpuInfo - -def lr_schedule(epoch): - """Learning Rate Schedule - Learning rate is scheduled to be reduced after 80, 120, 160, 180 epochs. - Called automatically every epoch as part of callbacks during training. - # Arguments - epoch (int): The number of epochs - # Returns - lr (float32): learning rate - """ - lr = 1e-3 - if epoch > 180: - lr *= 0.5e-3 - elif epoch > 160: - lr *= 1e-3 - elif epoch > 120: - lr *= 1e-2 - elif epoch > 80: - lr *= 1e-1 - print('Learning rate: ', lr) - return lr - -def resnet_layer(inputs, - num_filters=8, - kernel_size=3, - strides=1, - activation='relu', - batch_normalization=True, - conv_first=True): - """2D Convolution-Batch Normalization-Activation stack builder - # Arguments - inputs (tensor): input tensor from input image or previous layer - num_filters (int): Conv2D number of filters - kernel_size (int): Conv2D square kernel dimensions - strides (int): Conv2D square stride dimensions - activation (string): activation name - batch_normalization (bool): whether to include batch normalization - conv_first (bool): conv-bn-activation (True) or - bn-activation-conv (False) - # Returns - x (tensor): tensor as input to the next layer - """ - conv = Conv2D(num_filters, - kernel_size=kernel_size, - strides=strides, - padding='same', - use_bias=True, - kernel_initializer='he_normal', - kernel_regularizer=l2(1e-4)) - - x = inputs - if conv_first: - x = conv(x) - # if batch_normalization: - # x = BatchNormalization()(x) - if activation is not None: - x = Activation(activation)(x) - else: - # if batch_normalization: - # x = BatchNormalization()(x) - if activation is not None: - x = Activation(activation)(x) - x = conv(x) - return x - -def resnet_v2(input_shape, depth, num_classes=10): - """ResNet Version 2 Model builder [b] - Stacks of (1 x 1)-(3 x 3)-(1 x 1) BN-ReLU-Conv2D or also known as - bottleneck layer - First shortcut connection per layer is 1 x 1 Conv2D. - Second and onwards shortcut connection is identity. - At the beginning of each stage, the feature map size is halved (downsampled) - by a convolutional layer with strides=2, while the number of filter maps is - doubled. Within each stage, the layers have the same number filters and the - same filter map sizes. - Features maps sizes: - conv1 : 32x32, 16 - stage 0: 32x32, 64 - stage 1: 16x16, 128 - stage 2: 8x8, 256 - # Arguments - input_shape (tensor): shape of input image tensor - depth (int): number of core convolutional layers - num_classes (int): number of classes (CIFAR10 has 10) - # Returns - model (Model): Keras model instance - """ - if (depth - 2) % 9 != 0: - raise ValueError('depth should be 9n+2 (eg 56 or 110 in [b])') - # Start model definition. - num_filters_in = 4 - num_res_blocks = int((depth - 2) / 9) - - inputs = Input(shape=input_shape) - # v2 performs Conv2D with BN-ReLU on input before splitting into 2 paths - x = resnet_layer(inputs=inputs, - num_filters=num_filters_in, - conv_first=True) - - # Instantiate the stack of residual units - for stage in range(1): - for res_block in range(num_res_blocks): - activation = 'relu' - batch_normalization = True - strides = 1 - if stage == 0: - num_filters_out = num_filters_in * 4 - if res_block == 0: # first layer and first stage - activation = None - batch_normalization = False - else: - num_filters_out = num_filters_in * 2 - if res_block == 0: # first layer but not first stage - strides = 2 # downsample - - # bottleneck residual unit - y = resnet_layer(inputs=x, - num_filters=num_filters_in, - kernel_size=1, - strides=strides, - activation=activation, - batch_normalization=batch_normalization, - conv_first=False) - y = resnet_layer(inputs=y, - num_filters=num_filters_in, - conv_first=False) - - y = resnet_layer(inputs=y, - num_filters=num_filters_out, - kernel_size=1, - conv_first=False) - if res_block == 0: - # linear projection residual shortcut connection to match - # changed dims - x = resnet_layer(inputs=x, - num_filters=num_filters_out, - kernel_size=1, - strides=strides, - activation=None, - batch_normalization=False) - x = tensorflow.keras.layers.add([x, y]) - - num_filters_in = num_filters_out - - # Add classifier on top. - # v2 has BN-ReLU before Pooling - # x = BatchNormalization()(x) - x = Activation('relu')(x) - x = AveragePooling2D(pool_size=8)(x) - y = Flatten()(x) - outputs = Dense(num_classes, - activation='softmax', - kernel_initializer='he_normal')(y) - - # Instantiate model. - model = Model(inputs=inputs, outputs=outputs) - return model - -# Training parameters -batch_size = 128 # orig paper trained all networks with batch_size=128 -epochs = 1 -num_classes = 10 - -# Subtracting pixel mean improves accuracy -subtract_pixel_mean = True - -n = 1 -depth = n * 9 + 2 - -def train(): - # Load the CIFAR10 data. - (x_train, y_train), (x_test, y_test) = cifar10.load_data() - - # Input image dimensions. - input_shape = x_train.shape[1:] - # Normalize data. - x_train = x_train.astype('float32') / 255 - x_test = x_test.astype('float32') / 255 - - x_train_mean = np.mean(x_train, axis=0) - x_train -= x_train_mean - x_test -= x_train_mean - - # Convert class vectors to binary class matrices. - y_train = tensorflow.keras.utils.to_categorical(y_train, num_classes) - y_test = tensorflow.keras.utils.to_categorical(y_test, num_classes) - - model = resnet_v2(input_shape=input_shape, depth=depth) - - model.compile(loss='categorical_crossentropy', - optimizer=tensorflow.keras.optimizers.Adam(learning_rate=0.01), - metrics=['accuracy']) - model.summary() - - lr_scheduler = LearningRateScheduler(lr_schedule) - - lr_reducer = ReduceLROnPlateau(factor=np.sqrt(0.1), - cooldown=0, - patience=5, - min_lr=0.5e-6) - - callbacks = [lr_reducer, lr_scheduler] - - # Run training, with or without data augmentation. - model.fit(x_train, y_train, - batch_size=batch_size, - epochs=epochs, - validation_data=(x_test, y_test), - shuffle=True, - callbacks=callbacks) - - # Score trained model. - scores = model.evaluate(x_test, y_test, verbose=1) - print('Test loss:', scores[0]) - print('Test accuracy:', scores[1]) - model.save("baseline_model") - -class TrainDataset(object): - def __init__(self): - (x_train, y_train), (x_test, y_test) = cifar10.load_data() - x_train, y_train = x_train[:100], y_train[:100] - x_train = x_train.astype('float32') / 255 - x_test = x_test.astype('float32') / 255 - - # If subtract pixel mean is enabled - x_train_mean = np.mean(x_train, axis=0) - x_train -= x_train_mean - x_test -= x_train_mean - - # Convert class vectors to binary class matrices. - y_train = tensorflow.keras.utils.to_categorical(y_train, num_classes) - y_test = tensorflow.keras.utils.to_categorical(y_test, num_classes) - self.test_images = x_test - self.test_labels = y_test - self.train_images = x_train - self.train_labels = y_train - - def __len__(self): - return len(self.train_images) - - def __getitem__(self, idx): - return self.train_images[idx], self.train_labels[idx] - -class EvalDataset(object): - def __init__(self): - (x_train, y_train), (x_test, y_test) = cifar10.load_data() - - x_train = x_train.astype('float32') / 255 - x_test = x_test.astype('float32') / 255 - - # If subtract pixel mean is enabled - x_train_mean = np.mean(x_train, axis=0) - x_train -= x_train_mean - x_test -= x_train_mean - - # Convert class vectors to binary class matrices. - y_train = tensorflow.keras.utils.to_categorical(y_train, num_classes) - y_test = tensorflow.keras.utils.to_categorical(y_test, num_classes) - self.test_images = x_test - self.test_labels = y_test - - def __len__(self): - return len(self.test_images) - - def __getitem__(self, idx): - return self.test_images[idx], self.test_labels[idx] - -class TestTensorflowPruning(unittest.TestCase): - def setUp(self): - logger.info(f"CPU: {cpuinfo.get_cpu_info()['brand_raw']}") - logger.info(f"Test: {sys.modules[__name__].__file__}-{self.__class__.__name__}-{self._testMethodName}") - - def tearDown(self): - logger.info(f"{self._testMethodName} done.\\n") - - def test_tensorflow_pruning(self): - from neural_compressor.experimental import Pruning, common - from neural_compressor.utils import logger - prune = Pruning("./fake_yaml.yaml") - prune.train_distributed = True - prune.evaluation_distributed = True - prune.train_dataloader = common.DataLoader(TrainDataset(), batch_size=16) - prune.eval_dataloader = common.DataLoader(EvalDataset(), batch_size=32) - prune.model = './baseline_model' - pruned_model = prune() - stats, sparsity = pruned_model.report_sparsity() - logger.info(stats) - logger.info(sparsity) - self.assertGreater(sparsity, 20) - self.assertGreater(prune.baseline_score, 0.729) - if bool(CpuInfo().bf16): - self.assertGreater(prune.last_score, 0.742) - else: - self.assertGreater(prune.last_score, 0.743) - - -if __name__ == '__main__': - unittest.main() - ''' - with open("fake_ut.py", "w", encoding="utf-8") as f: - f.write(fake_ut) - build_fake_yaml() - - -def build_fake_yaml(): - fake_yaml = """ - model: - name: resnet_v2_prune - framework: tensorflow - pruning: - train: - epoch: 4 - optimizer: - SGD: - learning_rate: 0.001 - momentum: 0.1 - nesterov: True - weight_decay: 0.1 - criterion: - CrossEntropyLoss: - reduction: sum - approach: - weight_compression: - initial_sparsity: 0.0 - target_sparsity: 0.2 - start_epoch: 0 - end_epoch: 4 - pruners: - - !Pruner - start_epoch: 1 - end_epoch: 3 - prune_type: basic_magnitude - evaluation: - accuracy: - metric: - topk: 1 - """ - with open("fake_yaml.yaml", "w", encoding="utf-8") as f: - f.write(fake_yaml) - - -def dir_md5_check(dir): - files_list = [] - md5_list = [] - - def get_files_list(path, list_name): - for file in sorted(os.listdir(path)): - file_path = os.path.join(path, file) - if os.path.isdir(file_path): - get_files_list(file_path, list_name) - else: - list_name.append(file_path) - - get_files_list(dir, files_list) - for file_path in files_list: - with open(file_path, "rb") as fp: - data = fp.read() - file_md5 = hashlib.md5(data).hexdigest() - md5_list.append(file_md5) - return md5_list - - -class TestDistributed(unittest.TestCase): - dst_path = "./baseline_model" - - @classmethod - def setUpClass(cls): - build_fake_ut() - build_fake_yaml() - if system().lower() == "windows": - src_path = "C:\\tmp\\.neural_compressor\\inc_ut\\resnet_v2\\" - elif system().lower() == "linux": - src_path = "/tmp/.neural_compressor/inc_ut/resnet_v2/" - if os.path.exists(src_path): - shutil.copytree(src_path, os.getcwd(), dirs_exist_ok=True) - if not os.path.exists(cls.dst_path): - raise FileNotFoundError(f"'{cls.dst_path}' doesn't exist.") - elif dir_md5_check(cls.dst_path) != [ - "65625fef42f44e6853d4d6d5e4188a49", - "a783396652bf62db3db4c9f647953175", - "c7259753419d9fc053df5b2059aef8c0", - "77f2a1045cffee9f6a43f2594a5627ba", - ]: - logger.warning("resnet_v2 baseline_model md5 verification failed.") - raise ValueError(f"'{cls.dst_path}' md5 verification failed.") - else: - logger.info("resnet_v2 baseline_model for TF distributed pruning md5 verification succeeded.") - - @classmethod - def tearDownClass(cls): - os.remove("fake_ut.py") - os.remove("fake_yaml.yaml") - shutil.rmtree("nc_workspace", ignore_errors=True) - shutil.rmtree("baseline_model", ignore_errors=True) - - def setUp(self): - logger.info(f"CPU: {cpuinfo.get_cpu_info()['brand_raw']}") - logger.info(f"Test: {sys.modules[__name__].__file__}-{self.__class__.__name__}-{self._testMethodName}") - - def tearDown(self): - logger.info(f"{self._testMethodName} done.\n") - - @unittest.skipIf(version1_lt_version2(tf.version.VERSION, "2.10.0"), "Only test TF 2.10.0 or above") - def test_tf_distributed_pruning(self): - distributed_cmd = "horovodrun -np 2 python fake_ut.py" - p = subprocess.Popen( - distributed_cmd, preexec_fn=os.setsid, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True - ) - try: - out, _ = p.communicate() - for line in out.splitlines(): - print(line.decode().strip()) - matches = re.findall(r"FAILED", out.decode("utf-8")) - self.assertEqual(matches, []) - - matches = re.findall(r"OK", out.decode("utf-8")) - self.assertTrue(len(matches) > 0) - except KeyboardInterrupt: - os.killpg(os.getpgid(p.pid), signal.SIGKILL) - assert 0 - - -if __name__ == "__main__": - unittest.main() diff --git a/test/pruning_with_tf/pruning_1.x_v1/test_tensorflow_pruning.py b/test/pruning_with_tf/pruning_1.x_v1/test_tensorflow_pruning.py deleted file mode 100644 index 3ec84d834be..00000000000 --- a/test/pruning_with_tf/pruning_1.x_v1/test_tensorflow_pruning.py +++ /dev/null @@ -1,487 +0,0 @@ -"""Tests for the TensorFlow pruning.""" - -from __future__ import print_function - -import hashlib -import os -import shutil -import sys -import types -import unittest -from platform import platform, system - -import cpuinfo -import numpy as np -import tensorflow as tf - -from neural_compressor.adaptor import FRAMEWORKS -from neural_compressor.adaptor.tf_utils.util import version1_lt_version2 -from neural_compressor.conf.dotdict import DotDict -from neural_compressor.experimental import Pruning, common -from neural_compressor.experimental.pruning import TfPruningCallback -from neural_compressor.utils import logger -from neural_compressor.utils.create_obj_from_config import create_train_func - - -def build_fake_yaml(): - fake_yaml = """ - model: - name: resnet_v2_prune - framework: tensorflow - pruning: - train: - epoch: 4 - optimizer: - SGD: - learning_rate: 0.001 - momentum: 0.9 - nesterov: True - criterion: - CrossEntropyLoss: - reduction: sum_over_batch_size - approach: - weight_compression: - initial_sparsity: 0.0 - target_sparsity: 0.2 - start_epoch: 0 - end_epoch: 4 - pruners: - - !Pruner - start_epoch: 1 - end_epoch: 3 - prune_type: basic_magnitude - evaluation: - accuracy: - metric: - topk: 1 - """ - with open("fake_yaml.yaml", "w", encoding="utf-8") as f: - f.write(fake_yaml) - - -def lr_schedule(epoch): - """Learning Rate Schedule - Learning rate is scheduled to be reduced after 80, 120, 160, 180 epochs. - - Called automatically every epoch as part of callbacks during training. - # Arguments - epoch (int): The number of epochs - # Returns - lr (float32): learning rate - """ - lr = 1e-3 - if epoch > 180: - lr *= 0.5e-3 - elif epoch > 160: - lr *= 1e-3 - elif epoch > 120: - lr *= 1e-2 - elif epoch > 80: - lr *= 1e-1 - print("Learning rate: ", lr) - return lr - - -def resnet_layer( - inputs, num_filters=8, kernel_size=3, strides=1, activation="relu", batch_normalization=True, conv_first=True -): - """2D Convolution-Batch Normalization-Activation stack builder - # Arguments - inputs (tensor): input tensor from input image or previous layer - num_filters (int): Conv2D number of filters - kernel_size (int): Conv2D square kernel dimensions - strides (int): Conv2D square stride dimensions - activation (string): activation name - batch_normalization (bool): whether to include batch normalization - conv_first (bool): conv-bn-activation (True) or - bn-activation-conv (False) - # Returns - x (tensor): tensor as input to the next layer.""" - conv = tf.keras.layers.Conv2D( - num_filters, - kernel_size=kernel_size, - strides=strides, - padding="same", - use_bias=True, - kernel_initializer="he_normal", - kernel_regularizer=tf.keras.regularizers.l2(1e-4), - ) - - x = inputs - if conv_first: - x = conv(x) - # if batch_normalization: - # x = BatchNormalization()(x) - if activation is not None: - x = tf.keras.layers.Activation(activation)(x) - else: - # if batch_normalization: - # x = BatchNormalization()(x) - if activation is not None: - x = tf.keras.layers.Activation(activation)(x) - x = conv(x) - return x - - -def resnet_v2(input_shape, depth, num_classes=10): - """ResNet Version 2 Model builder [b] - Stacks of (1 x 1)-(3 x 3)-(1 x 1) BN-ReLU-Conv2D or also known as - bottleneck layer - First shortcut connection per layer is 1 x 1 Conv2D. - - Second and onwards shortcut connection is identity. - At the beginning of each stage, the feature map size is halved (downsampled) - by a convolutional layer with strides=2, while the number of filter maps is - doubled. Within each stage, the layers have the same number filters and the - same filter map sizes. - Features maps sizes: - conv1 : 32x32, 16 - stage 0: 32x32, 64 - stage 1: 16x16, 128 - stage 2: 8x8, 256 - # Arguments - input_shape (tensor): shape of input image tensor - depth (int): number of core convolutional layers - num_classes (int): number of classes (CIFAR10 has 10) - # Returns - model (Model): Keras model instance - """ - if (depth - 2) % 9 != 0: - raise ValueError("depth should be 9n+2 (eg 56 or 110 in [b])") - # Start model definition. - num_filters_in = 4 - num_res_blocks = int((depth - 2) / 9) - - inputs = tf.keras.layers.Input(shape=input_shape) - # v2 performs Conv2D with BN-ReLU on input before splitting into 2 paths - x = resnet_layer(inputs=inputs, num_filters=num_filters_in, conv_first=True) - - # Instantiate the stack of residual units - for stage in range(1): - for res_block in range(num_res_blocks): - activation = "relu" - batch_normalization = True - strides = 1 - if stage == 0: - num_filters_out = num_filters_in * 4 - if res_block == 0: # first layer and first stage - activation = None - batch_normalization = False - else: - num_filters_out = num_filters_in * 2 - if res_block == 0: # first layer but not first stage - strides = 2 # downsample - - # bottleneck residual unit - y = resnet_layer( - inputs=x, - num_filters=num_filters_in, - kernel_size=1, - strides=strides, - activation=activation, - batch_normalization=batch_normalization, - conv_first=False, - ) - y = resnet_layer(inputs=y, num_filters=num_filters_in, conv_first=False) - - y = resnet_layer(inputs=y, num_filters=num_filters_out, kernel_size=1, conv_first=False) - if res_block == 0: - # linear projection residual shortcut connection to match - # changed dims - x = resnet_layer( - inputs=x, - num_filters=num_filters_out, - kernel_size=1, - strides=strides, - activation=None, - batch_normalization=False, - ) - x = tf.keras.layers.add([x, y]) - - num_filters_in = num_filters_out - - # Add classifier on top. - # v2 has BN-ReLU before Pooling - # x = BatchNormalization()(x) - x = tf.keras.layers.Activation("relu")(x) - x = tf.keras.layers.AveragePooling2D(pool_size=8)(x) - y = tf.keras.layers.Flatten()(x) - outputs = tf.keras.layers.Dense(num_classes, activation="softmax", kernel_initializer="he_normal")(y) - - # Instantiate model. - model = tf.keras.models.Model(inputs=inputs, outputs=outputs) - return model - - -# Training parameters -batch_size = 128 # orig paper trained all networks with batch_size=128 -epochs = 1 -num_classes = 10 - -# Subtracting pixel mean improves accuracy -subtract_pixel_mean = True - -n = 1 -depth = n * 9 + 2 - - -def train(dst_path): - # Load the CIFAR10 data. - (x_train, y_train), (x_test, y_test) = tf.keras.datasets.cifar10.load_data() - - # Input image dimensions. - input_shape = x_train.shape[1:] - # Normalize data. - x_train = x_train.astype("float32") / 255 - x_test = x_test.astype("float32") / 255 - - x_train_mean = np.mean(x_train, axis=0) - x_train -= x_train_mean - x_test -= x_train_mean - - # Convert class vectors to binary class matrices. - y_train = tf.keras.utils.to_categorical(y_train, num_classes) - y_test = tf.keras.utils.to_categorical(y_test, num_classes) - - model = resnet_v2(input_shape=input_shape, depth=depth) - - model.compile( - loss="categorical_crossentropy", optimizer=tf.keras.optimizers.Adam(learning_rate=0.01), metrics=["accuracy"] - ) - model.summary() - - lr_scheduler = tf.keras.callbacks.LearningRateScheduler(lr_schedule) - - lr_reducer = tf.keras.callbacks.ReduceLROnPlateau(factor=np.sqrt(0.1), cooldown=0, patience=5, min_lr=0.5e-6) - - callbacks = [lr_reducer, lr_scheduler] - - # Run training, with or without data augmentation. - model.fit( - x_train, - y_train, - batch_size=batch_size, - epochs=epochs, - validation_data=(x_test, y_test), - shuffle=True, - callbacks=callbacks, - ) - - # Score trained model. - scores = model.evaluate(x_test, y_test, verbose=1) - print("Test loss:", scores[0]) - print("Test accuracy:", scores[1]) - model.save(dst_path) - - -def dir_md5_check(dir): - files_list = [] - md5_list = [] - - def get_files_list(path, list_name): - for file in sorted(os.listdir(path)): - file_path = os.path.join(path, file) - if os.path.isdir(file_path): - get_files_list(file_path, list_name) - else: - list_name.append(file_path) - - get_files_list(dir, files_list) - for file_path in files_list: - with open(file_path, "rb") as fp: - data = fp.read() - file_md5 = hashlib.md5(data).hexdigest() - md5_list.append(file_md5) - return md5_list - - -class TrainDataset(object): - def __init__(self): - (x_train, y_train), (x_test, y_test) = tf.keras.datasets.cifar10.load_data() - x_train, y_train = x_train[:64], y_train[:64] - x_train = x_train.astype("float32") / 255 - x_test = x_test.astype("float32") / 255 - - # If subtract pixel mean is enabled - x_train_mean = np.mean(x_train, axis=0) - x_train -= x_train_mean - x_test -= x_train_mean - - # Convert class vectors to binary class matrices. - y_train = tf.keras.utils.to_categorical(y_train, num_classes) - y_test = tf.keras.utils.to_categorical(y_test, num_classes) - self.test_images = x_test - self.test_labels = y_test - self.train_images = x_train - self.train_labels = y_train - - def __len__(self): - return len(self.train_images) - - def __getitem__(self, idx): - return self.train_images[idx], self.train_labels[idx] - - -class EvalDataset(object): - def __init__(self): - (x_train, y_train), (x_test, y_test) = tf.keras.datasets.cifar10.load_data() - - x_train = x_train.astype("float32") / 255 - x_test = x_test.astype("float32") / 255 - - # If subtract pixel mean is enabled - x_train_mean = np.mean(x_train, axis=0) - x_train -= x_train_mean - x_test -= x_train_mean - - # Convert class vectors to binary class matrices. - y_train = tf.keras.utils.to_categorical(y_train, num_classes) - y_test = tf.keras.utils.to_categorical(y_test, num_classes) - self.test_images = x_test - self.test_labels = y_test - - def __len__(self): - return len(self.test_images) - - def __getitem__(self, idx): - return self.test_images[idx], self.test_labels[idx] - - -class TestTensorflowPruning(unittest.TestCase): - dst_path = "./baseline_model" - - @classmethod - def setUpClass(cls): - build_fake_yaml() - if system().lower() == "windows": - src_path = "C:\\tmp\\.neural_compressor\\inc_ut\\resnet_v2\\" - elif system().lower() == "linux": - src_path = "/tmp/.neural_compressor/inc_ut/resnet_v2/" - if os.path.exists(src_path): - shutil.copytree(src_path, os.getcwd(), dirs_exist_ok=True) - if not os.path.exists(cls.dst_path): - logger.warning("resnet_v2 baseline_model doesn't exist.") - return unittest.skip("resnet_v2 baseline_model doesn't exist")(TestTensorflowPruning) - elif dir_md5_check(cls.dst_path) != [ - "65625fef42f44e6853d4d6d5e4188a49", - "a783396652bf62db3db4c9f647953175", - "c7259753419d9fc053df5b2059aef8c0", - "77f2a1045cffee9f6a43f2594a5627ba", - ]: - logger.warning("resnet_v2 baseline_model md5 verification failed.") - return unittest.skip("resnet_v2 baseline_model md5 verification failed.")(TestTensorflowPruning) - else: - logger.info("resnet_v2 baseline_model for TF pruning md5 verification succeeded.") - - @classmethod - def tearDownClass(cls): - os.remove("fake_yaml.yaml") - shutil.rmtree("nc_workspace", ignore_errors=True) - shutil.rmtree("baseline_model", ignore_errors=True) - - def setUp(self): - logger.info(f"CPU: {cpuinfo.get_cpu_info()['brand_raw']}") - logger.info(f"Test: {sys.modules[__name__].__file__}-{self.__class__.__name__}-{self._testMethodName}") - - def tearDown(self): - logger.info(f"{self._testMethodName} done.\n") - - @unittest.skipIf( - version1_lt_version2(tf.version.VERSION, "2.3.0"), - "Keras model need tensorflow version >= 2.3.0, so the case is skipped", - ) - def test_create_train_func1(self): - framework = "tensorflow" - framework_specific_info = DotDict( - { - "device": "cpu", - "random_seed": 1978, - "workspace_path": "./nc_workspace/", - "q_dataloader": None, - "inputs": [], - "outputs": [], - "format": "default", - "backend": "default", - } - ) - adaptor = FRAMEWORKS[framework](framework_specific_info) - - dataloader = common.DataLoader(TrainDataset(), batch_size=32) - train_cfg = DotDict( - { - "epoch": 1, - "optimizer": {"AdamW": {"learning_rate": 0.001, "weight_decay": 0.0001}}, - "criterion": {"CrossEntropyLoss": {"reduction": "sum_over_batch_size", "from_logits": True}}, - "execution_mode": "eager", - "start_epoch": 0, - } - ) - callbacks = TfPruningCallback - hooks = {} - pruning_func1 = create_train_func(framework, dataloader, adaptor, train_cfg, hooks, callbacks) - self.assertTrue(isinstance(pruning_func1, types.FunctionType)) - - @unittest.skipIf( - version1_lt_version2(tf.version.VERSION, "2.3.0"), - "Keras model need tensorflow version >= 2.3.0, so the case is skipped", - ) - def test_create_train_func2(self): - framework = "tensorflow" - framework_specific_info = DotDict( - { - "device": "cpu", - "random_seed": 1978, - "workspace_path": "./nc_workspace/", - "q_dataloader": None, - "inputs": [], - "outputs": [], - "format": "default", - "backend": "default", - } - ) - adaptor = FRAMEWORKS[framework](framework_specific_info) - - dataloader = common.DataLoader(TrainDataset(), batch_size=32) - train_cfg = DotDict( - { - "epoch": 1, - "dataloader": { - "distributed": False, - "batch_size": 32, - "dataset": {"ImageRecord": {"root": "./ImageNet"}}, - "transform": { - "ResizeCropImagenet": {"height": 224, "width": 224, "mean_value": [123.68, 116.78, 103.94]} - }, - "last_batch": "rollover", - "shuffle": False, - }, - "postprocess": {"transform": {"LabelShift": 1}}, - "optimizer": {"SGD": {"learning_rate": 0.0001, "momentum": 0.9, "nesterov": True}}, - "criterion": {"SparseCategoricalCrossentropy": {"reduction": "sum_over_batch_size"}}, - "execution_mode": "eager", - "start_epoch": 0, - } - ) - pruning_func2 = create_train_func(framework, dataloader, adaptor, train_cfg) - self.assertTrue(isinstance(pruning_func2, types.FunctionType)) - - @unittest.skipIf( - version1_lt_version2(tf.version.VERSION, "2.3.0"), - "Keras model need tensorflow version >= 2.3.0, so the case is skipped", - ) - def test_tensorflow_pruning(self): - prune = Pruning("./fake_yaml.yaml") - prune.train_dataloader = common.DataLoader(TrainDataset(), batch_size=32) - prune.eval_dataloader = common.DataLoader(EvalDataset(), batch_size=32) - prune.model = self.dst_path - pruned_model = prune() - stats, sparsity = pruned_model.report_sparsity() - logger.info(stats) - logger.info(sparsity) - self.assertGreater(sparsity, 20) - self.assertGreater(prune.baseline_score, 0.72) - self.assertGreater(prune.last_score, 0.73) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/pruning_with_tf/pruning_1.x_v1/test_tensorflow_pruning_utility.py b/test/pruning_with_tf/pruning_1.x_v1/test_tensorflow_pruning_utility.py deleted file mode 100644 index 00d9bb6acd7..00000000000 --- a/test/pruning_with_tf/pruning_1.x_v1/test_tensorflow_pruning_utility.py +++ /dev/null @@ -1,64 +0,0 @@ -import shutil -import unittest - - -def train_func(): - import tensorflow as tf - from tensorflow import keras - - # Load MNIST dataset - mnist = keras.datasets.mnist - (train_images, train_labels), (test_images, test_labels) = mnist.load_data() - - # Normalize the input image so that each pixel value is between 0 to 1. - train_images = train_images / 255.0 - test_images = test_images / 255.0 - - # Define the model architecture. - model = keras.Sequential( - [ - keras.layers.InputLayer(input_shape=(28, 28)), - keras.layers.Reshape(target_shape=(28, 28, 1)), - keras.layers.Conv2D(filters=12, kernel_size=(3, 3), activation="relu"), - keras.layers.MaxPooling2D(pool_size=(2, 2)), - keras.layers.Flatten(), - keras.layers.Dense(10), - ] - ) - # Train the digit classification model - model.compile( - optimizer="adam", loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True), metrics=["accuracy"] - ) - - model.fit( - train_images, - train_labels, - epochs=1, - validation_split=0.1, - ) - - model.save("baseline_model") - - -class TestTensorflowPruning(unittest.TestCase): - @classmethod - def setUpClass(self): - train_func() - - @classmethod - def tearDownClass(self): - shutil.rmtree("baseline_model", ignore_errors=True) - - def test_pruning_utility(self): - from neural_compressor.model import Model - - pruning_model = Model("baseline_model") - all_weights_name = pruning_model.get_all_weight_names() - df, sparsity = pruning_model.report_sparsity() - self.assertEqual(all_weights_name, [1, 4]) - self.assertEqual(df.empty, False) - self.assertNotEqual(sparsity, None) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/pruning_with_tf/pruning_1.x_v2/test_tensorflow_pruning.py b/test/pruning_with_tf/pruning_1.x_v2/test_tensorflow_pruning.py deleted file mode 100644 index ff8d3103602..00000000000 --- a/test/pruning_with_tf/pruning_1.x_v2/test_tensorflow_pruning.py +++ /dev/null @@ -1,487 +0,0 @@ -"""Tests for the TensorFlow pruning.""" - -from __future__ import print_function - -import hashlib -import os -import shutil -import sys -import types -import unittest -from platform import platform, system - -import cpuinfo -import numpy as np -import tensorflow as tf - -from neural_compressor.adaptor import FRAMEWORKS -from neural_compressor.adaptor.tf_utils.util import version1_lt_version2 -from neural_compressor.conf.dotdict import DotDict -from neural_compressor.experimental import Pruning, common -from neural_compressor.experimental.pruning_v2 import TfPruningCallback -from neural_compressor.utils import logger -from neural_compressor.utils.create_obj_from_config import create_train_func - - -def build_fake_yaml(): - fake_yaml = """ - model: - name: resnet_v2_prune - framework: tensorflow - pruning: - train: - epoch: 4 - optimizer: - SGD: - learning_rate: 0.001 - momentum: 0.9 - nesterov: True - criterion: - CrossEntropyLoss: - reduction: sum_over_batch_size - approach: - weight_compression: - initial_sparsity: 0.0 - target_sparsity: 0.2 - start_epoch: 0 - end_epoch: 4 - pruners: - - !Pruner - start_epoch: 1 - end_epoch: 3 - prune_type: basic_magnitude - evaluation: - accuracy: - metric: - topk: 1 - """ - with open("fake_yaml.yaml", "w", encoding="utf-8") as f: - f.write(fake_yaml) - - -def lr_schedule(epoch): - """Learning Rate Schedule - Learning rate is scheduled to be reduced after 80, 120, 160, 180 epochs. - - Called automatically every epoch as part of callbacks during training. - # Arguments - epoch (int): The number of epochs - # Returns - lr (float32): learning rate - """ - lr = 1e-3 - if epoch > 180: - lr *= 0.5e-3 - elif epoch > 160: - lr *= 1e-3 - elif epoch > 120: - lr *= 1e-2 - elif epoch > 80: - lr *= 1e-1 - print("Learning rate: ", lr) - return lr - - -def resnet_layer( - inputs, num_filters=8, kernel_size=3, strides=1, activation="relu", batch_normalization=True, conv_first=True -): - """2D Convolution-Batch Normalization-Activation stack builder - # Arguments - inputs (tensor): input tensor from input image or previous layer - num_filters (int): Conv2D number of filters - kernel_size (int): Conv2D square kernel dimensions - strides (int): Conv2D square stride dimensions - activation (string): activation name - batch_normalization (bool): whether to include batch normalization - conv_first (bool): conv-bn-activation (True) or - bn-activation-conv (False) - # Returns - x (tensor): tensor as input to the next layer.""" - conv = tf.keras.layers.Conv2D( - num_filters, - kernel_size=kernel_size, - strides=strides, - padding="same", - use_bias=True, - kernel_initializer="he_normal", - kernel_regularizer=tf.keras.regularizers.l2(1e-4), - ) - - x = inputs - if conv_first: - x = conv(x) - # if batch_normalization: - # x = BatchNormalization()(x) - if activation is not None: - x = tf.keras.layers.Activation(activation)(x) - else: - # if batch_normalization: - # x = BatchNormalization()(x) - if activation is not None: - x = tf.keras.layers.Activation(activation)(x) - x = conv(x) - return x - - -def resnet_v2(input_shape, depth, num_classes=10): - """ResNet Version 2 Model builder [b] - Stacks of (1 x 1)-(3 x 3)-(1 x 1) BN-ReLU-Conv2D or also known as - bottleneck layer - First shortcut connection per layer is 1 x 1 Conv2D. - - Second and onwards shortcut connection is identity. - At the beginning of each stage, the feature map size is halved (downsampled) - by a convolutional layer with strides=2, while the number of filter maps is - doubled. Within each stage, the layers have the same number filters and the - same filter map sizes. - Features maps sizes: - conv1 : 32x32, 16 - stage 0: 32x32, 64 - stage 1: 16x16, 128 - stage 2: 8x8, 256 - # Arguments - input_shape (tensor): shape of input image tensor - depth (int): number of core convolutional layers - num_classes (int): number of classes (CIFAR10 has 10) - # Returns - model (Model): Keras model instance - """ - if (depth - 2) % 9 != 0: - raise ValueError("depth should be 9n+2 (eg 56 or 110 in [b])") - # Start model definition. - num_filters_in = 4 - num_res_blocks = int((depth - 2) / 9) - - inputs = tf.keras.layers.Input(shape=input_shape) - # v2 performs Conv2D with BN-ReLU on input before splitting into 2 paths - x = resnet_layer(inputs=inputs, num_filters=num_filters_in, conv_first=True) - - # Instantiate the stack of residual units - for stage in range(1): - for res_block in range(num_res_blocks): - activation = "relu" - batch_normalization = True - strides = 1 - if stage == 0: - num_filters_out = num_filters_in * 4 - if res_block == 0: # first layer and first stage - activation = None - batch_normalization = False - else: - num_filters_out = num_filters_in * 2 - if res_block == 0: # first layer but not first stage - strides = 2 # downsample - - # bottleneck residual unit - y = resnet_layer( - inputs=x, - num_filters=num_filters_in, - kernel_size=1, - strides=strides, - activation=activation, - batch_normalization=batch_normalization, - conv_first=False, - ) - y = resnet_layer(inputs=y, num_filters=num_filters_in, conv_first=False) - - y = resnet_layer(inputs=y, num_filters=num_filters_out, kernel_size=1, conv_first=False) - if res_block == 0: - # linear projection residual shortcut connection to match - # changed dims - x = resnet_layer( - inputs=x, - num_filters=num_filters_out, - kernel_size=1, - strides=strides, - activation=None, - batch_normalization=False, - ) - x = tf.keras.layers.add([x, y]) - - num_filters_in = num_filters_out - - # Add classifier on top. - # v2 has BN-ReLU before Pooling - # x = BatchNormalization()(x) - x = tf.keras.layers.Activation("relu")(x) - x = tf.keras.layers.AveragePooling2D(pool_size=8)(x) - y = tf.keras.layers.Flatten()(x) - outputs = tf.keras.layers.Dense(num_classes, activation="softmax", kernel_initializer="he_normal")(y) - - # Instantiate model. - model = tf.keras.models.Model(inputs=inputs, outputs=outputs) - return model - - -# Training parameters -batch_size = 128 # orig paper trained all networks with batch_size=128 -epochs = 1 -num_classes = 10 - -# Subtracting pixel mean improves accuracy -subtract_pixel_mean = True - -n = 1 -depth = n * 9 + 2 - - -def train(dst_path): - # Load the CIFAR10 data. - (x_train, y_train), (x_test, y_test) = tf.keras.datasets.cifar10.load_data() - - # Input image dimensions. - input_shape = x_train.shape[1:] - # Normalize data. - x_train = x_train.astype("float32") / 255 - x_test = x_test.astype("float32") / 255 - - x_train_mean = np.mean(x_train, axis=0) - x_train -= x_train_mean - x_test -= x_train_mean - - # Convert class vectors to binary class matrices. - y_train = tf.keras.utils.to_categorical(y_train, num_classes) - y_test = tf.keras.utils.to_categorical(y_test, num_classes) - - model = resnet_v2(input_shape=input_shape, depth=depth) - - model.compile( - loss="categorical_crossentropy", optimizer=tf.keras.optimizers.Adam(learning_rate=0.01), metrics=["accuracy"] - ) - model.summary() - - lr_scheduler = tf.keras.callbacks.LearningRateScheduler(lr_schedule) - - lr_reducer = tf.keras.callbacks.ReduceLROnPlateau(factor=np.sqrt(0.1), cooldown=0, patience=5, min_lr=0.5e-6) - - callbacks = [lr_reducer, lr_scheduler] - - # Run training, with or without data augmentation. - model.fit( - x_train, - y_train, - batch_size=batch_size, - epochs=epochs, - validation_data=(x_test, y_test), - shuffle=True, - callbacks=callbacks, - ) - - # Score trained model. - scores = model.evaluate(x_test, y_test, verbose=1) - print("Test loss:", scores[0]) - print("Test accuracy:", scores[1]) - model.save(dst_path) - - -def dir_md5_check(dir): - files_list = [] - md5_list = [] - - def get_files_list(path, list_name): - for file in sorted(os.listdir(path)): - file_path = os.path.join(path, file) - if os.path.isdir(file_path): - get_files_list(file_path, list_name) - else: - list_name.append(file_path) - - get_files_list(dir, files_list) - for file_path in files_list: - with open(file_path, "rb") as fp: - data = fp.read() - file_md5 = hashlib.md5(data).hexdigest() - md5_list.append(file_md5) - return md5_list - - -class TrainDataset(object): - def __init__(self): - (x_train, y_train), (x_test, y_test) = tf.keras.datasets.cifar10.load_data() - x_train, y_train = x_train[:64], y_train[:64] - x_train = x_train.astype("float32") / 255 - x_test = x_test.astype("float32") / 255 - - # If subtract pixel mean is enabled - x_train_mean = np.mean(x_train, axis=0) - x_train -= x_train_mean - x_test -= x_train_mean - - # Convert class vectors to binary class matrices. - y_train = tf.keras.utils.to_categorical(y_train, num_classes) - y_test = tf.keras.utils.to_categorical(y_test, num_classes) - self.test_images = x_test - self.test_labels = y_test - self.train_images = x_train - self.train_labels = y_train - - def __len__(self): - return len(self.train_images) - - def __getitem__(self, idx): - return self.train_images[idx], self.train_labels[idx] - - -class EvalDataset(object): - def __init__(self): - (x_train, y_train), (x_test, y_test) = tf.keras.datasets.cifar10.load_data() - - x_train = x_train.astype("float32") / 255 - x_test = x_test.astype("float32") / 255 - - # If subtract pixel mean is enabled - x_train_mean = np.mean(x_train, axis=0) - x_train -= x_train_mean - x_test -= x_train_mean - - # Convert class vectors to binary class matrices. - y_train = tf.keras.utils.to_categorical(y_train, num_classes) - y_test = tf.keras.utils.to_categorical(y_test, num_classes) - self.test_images = x_test - self.test_labels = y_test - - def __len__(self): - return len(self.test_images) - - def __getitem__(self, idx): - return self.test_images[idx], self.test_labels[idx] - - -class TestTensorflowPruning(unittest.TestCase): - dst_path = "./baseline_model" - - @classmethod - def setUpClass(cls): - build_fake_yaml() - if system().lower() == "windows": - src_path = "C:\\tmp\\.neural_compressor\\inc_ut\\resnet_v2\\" - elif system().lower() == "linux": - src_path = "/tmp/.neural_compressor/inc_ut/resnet_v2/" - if os.path.exists(src_path): - shutil.copytree(src_path, os.getcwd(), dirs_exist_ok=True) - if not os.path.exists(cls.dst_path): - logger.warning("resnet_v2 baseline_model doesn't exist.") - return unittest.skip("resnet_v2 baseline_model doesn't exist")(TestTensorflowPruning) - elif dir_md5_check(cls.dst_path) != [ - "65625fef42f44e6853d4d6d5e4188a49", - "a783396652bf62db3db4c9f647953175", - "c7259753419d9fc053df5b2059aef8c0", - "77f2a1045cffee9f6a43f2594a5627ba", - ]: - logger.warning("resnet_v2 baseline_model md5 verification failed.") - return unittest.skip("resnet_v2 baseline_model md5 verification failed.")(TestTensorflowPruning) - else: - logger.info("resnet_v2 baseline_model for TF pruning md5 verification succeeded.") - - @classmethod - def tearDownClass(cls): - os.remove("fake_yaml.yaml") - shutil.rmtree("nc_workspace", ignore_errors=True) - shutil.rmtree("baseline_model", ignore_errors=True) - - def setUp(self): - logger.info(f"CPU: {cpuinfo.get_cpu_info()['brand_raw']}") - logger.info(f"Test: {sys.modules[__name__].__file__}-{self.__class__.__name__}-{self._testMethodName}") - - def tearDown(self): - logger.info(f"{self._testMethodName} done.\n") - - @unittest.skipIf( - version1_lt_version2(tf.version.VERSION, "2.3.0"), - "Keras model need tensorflow version >= 2.3.0, so the case is skipped", - ) - def test_create_train_func1(self): - framework = "tensorflow" - framework_specific_info = DotDict( - { - "device": "cpu", - "random_seed": 1978, - "workspace_path": "./nc_workspace/", - "q_dataloader": None, - "inputs": [], - "outputs": [], - "format": "default", - "backend": "default", - } - ) - adaptor = FRAMEWORKS[framework](framework_specific_info) - - dataloader = common.DataLoader(TrainDataset(), batch_size=32) - train_cfg = DotDict( - { - "epoch": 1, - "optimizer": {"AdamW": {"learning_rate": 0.001, "weight_decay": 0.0001}}, - "criterion": {"CrossEntropyLoss": {"reduction": "sum_over_batch_size", "from_logits": True}}, - "execution_mode": "eager", - "start_epoch": 0, - } - ) - callbacks = TfPruningCallback - hooks = {} - pruning_func1 = create_train_func(framework, dataloader, adaptor, train_cfg, hooks, callbacks) - self.assertTrue(isinstance(pruning_func1, types.FunctionType)) - - @unittest.skipIf( - version1_lt_version2(tf.version.VERSION, "2.3.0"), - "Keras model need tensorflow version >= 2.3.0, so the case is skipped", - ) - def test_create_train_func2(self): - framework = "tensorflow" - framework_specific_info = DotDict( - { - "device": "cpu", - "random_seed": 1978, - "workspace_path": "./nc_workspace/", - "q_dataloader": None, - "inputs": [], - "outputs": [], - "format": "default", - "backend": "default", - } - ) - adaptor = FRAMEWORKS[framework](framework_specific_info) - - dataloader = common.DataLoader(TrainDataset(), batch_size=32) - train_cfg = DotDict( - { - "epoch": 1, - "dataloader": { - "distributed": False, - "batch_size": 32, - "dataset": {"ImageRecord": {"root": "./ImageNet"}}, - "transform": { - "ResizeCropImagenet": {"height": 224, "width": 224, "mean_value": [123.68, 116.78, 103.94]} - }, - "last_batch": "rollover", - "shuffle": False, - }, - "postprocess": {"transform": {"LabelShift": 1}}, - "optimizer": {"SGD": {"learning_rate": 0.0001, "momentum": 0.9, "nesterov": True}}, - "criterion": {"SparseCategoricalCrossentropy": {"reduction": "sum_over_batch_size"}}, - "execution_mode": "eager", - "start_epoch": 0, - } - ) - pruning_func2 = create_train_func(framework, dataloader, adaptor, train_cfg) - self.assertTrue(isinstance(pruning_func2, types.FunctionType)) - - @unittest.skipIf( - version1_lt_version2(tf.version.VERSION, "2.3.0"), - "Keras model need tensorflow version >= 2.3.0, so the case is skipped", - ) - def test_tensorflow_pruning(self): - prune = Pruning("./fake_yaml.yaml") - prune.train_dataloader = common.DataLoader(TrainDataset(), batch_size=32) - prune.eval_dataloader = common.DataLoader(EvalDataset(), batch_size=32) - prune.model = self.dst_path - pruned_model = prune() - stats, sparsity = pruned_model.report_sparsity() - logger.info(stats) - logger.info(sparsity) - self.assertGreater(sparsity, 20) - self.assertGreater(prune.baseline_score, 0.72) - self.assertGreater(prune.last_score, 0.73) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/pruning_with_tf/pruning_2.x/test_pruning_keras.py b/test/pruning_with_tf/pruning_2.x/test_pruning_keras.py index 4f78ff380bc..b4cc81d870b 100644 --- a/test/pruning_with_tf/pruning_2.x/test_pruning_keras.py +++ b/test/pruning_with_tf/pruning_2.x/test_pruning_keras.py @@ -7,11 +7,11 @@ def test_pruning_keras(self): from neural_compressor import WeightPruningConfig from neural_compressor.adaptor import FRAMEWORKS - from neural_compressor.conf.config import default_workspace - from neural_compressor.conf.dotdict import DotDict + from neural_compressor.config import default_workspace from neural_compressor.data import DataLoader, Datasets from neural_compressor.training import prepare_compression from neural_compressor.utils import create_obj_from_config, logger + from neural_compressor.utils.utility import DotDict model = tf.keras.applications.ResNet50V2(weights="imagenet") diff --git a/test/quantization/test_quantization.py b/test/quantization/test_quantization.py deleted file mode 100644 index 878e0669e3f..00000000000 --- a/test/quantization/test_quantization.py +++ /dev/null @@ -1,516 +0,0 @@ -"""Tests for neural_compressor quantization.""" - -import importlib -import os -import shutil -import unittest - -import numpy as np -import yaml - - -def build_fake_yaml(): - fake_yaml = """ - model: - name: fake_yaml - framework: tensorflow - inputs: x - outputs: op_to_store - device: cpu - evaluation: - accuracy: - metric: - topk: 1 - tuning: - strategy: - name: fake - accuracy_criterion: - relative: 0.01 - workspace: - path: saved - """ - y = yaml.load(fake_yaml, Loader=yaml.SafeLoader) - with open("fake_yaml.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - f.close() - - -def build_fake_yaml2(): - fake_yaml = """ - model: - name: fake_yaml - framework: tensorflow - inputs: x - outputs: op_to_store - device: cpu - evaluation: - accuracy: - metric: - topk: 1 - tuning: - strategy: - name: fake - accuracy_criterion: - relative: 0.01 - workspace: - path: saved - resume: ./saved/history.snapshot - """ - y = yaml.load(fake_yaml, Loader=yaml.SafeLoader) - with open("fake_yaml2.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - f.close() - - -def build_fake_yaml3(): - fake_yaml = """ - model: - name: fake_yaml - framework: tensorflow - inputs: x - outputs: op_to_store - device: cpu - evaluation: - accuracy: - metric: - MSE: - compare_label: False - tuning: - strategy: - name: fake - accuracy_criterion: - relative: 0.01 - workspace: - path: saved - """ - y = yaml.load(fake_yaml, Loader=yaml.SafeLoader) - with open("fake_yaml3.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - f.close() - - -def build_fake_yaml4(): - fake_yaml = """ - model: - name: fake_yaml - framework: tensorflow - inputs: x - outputs: op_to_store - device: cpu - evaluation: - accuracy: - metric: - tuning: - strategy: - name: fake - accuracy_criterion: - relative: 0.01 - workspace: - path: saved - """ - y = yaml.load(fake_yaml, Loader=yaml.SafeLoader) - with open("fake_yaml4.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - f.close() - - -def build_fake_yaml5(): - fake_yaml = """ - model: - name: fake_yaml - framework: tensorflow - inputs: x - outputs: op_to_store - device: cpu - evaluation: - accuracy: - metric: - topk: 1 - tuning: - strategy: - name: fake - accuracy_criterion: - relative: 0.01 - exit_policy: - max_trials: 10 - workspace: - path: saved - """ - y = yaml.load(fake_yaml, Loader=yaml.SafeLoader) - with open("fake_yaml5.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - f.close() - - -def build_fake_yaml6(): - fake_yaml = """ - model: - name: fake_yaml - framework: tensorflow - inputs: x - outputs: op_to_store - device: cpu - tuning: - strategy: - name: fake - accuracy_criterion: - relative: 0.01 - workspace: - path: saved - """ - y = yaml.load(fake_yaml, Loader=yaml.SafeLoader) - with open("fake_yaml6.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - f.close() - - -def build_fake_model(): - import tensorflow as tf - from tensorflow.compat.v1 import graph_util - - try: - graph = tf.Graph() - graph_def = tf.GraphDef() - with tf.Session() as sess: - x = tf.placeholder(tf.float64, shape=(1, 3, 3, 1), name="x") - y = tf.constant(np.random.random((2, 2, 1, 1)), name="y") - op = tf.nn.conv2d(input=x, filter=y, strides=[1, 1, 1, 1], padding="VALID", name="op_to_store") - - sess.run(tf.global_variables_initializer()) - constant_graph = tf.graph_util.convert_variables_to_constants(sess, sess.graph_def, ["op_to_store"]) - - graph_def.ParseFromString(constant_graph.SerializeToString()) - with graph.as_default(): - tf.import_graph_def(graph_def, name="") - except: - graph = tf.Graph() - graph_def = tf.compat.v1.GraphDef() - with tf.compat.v1.Session() as sess: - x = tf.compat.v1.placeholder(tf.float64, shape=(1, 3, 3, 1), name="x") - y = tf.compat.v1.constant(np.random.random((2, 2, 1, 1)), name="y") - op = tf.nn.conv2d(input=x, filters=y, strides=[1, 1, 1, 1], padding="VALID", name="op_to_store") - - sess.run(tf.compat.v1.global_variables_initializer()) - constant_graph = tf.compat.v1.graph_util.convert_variables_to_constants( - sess, sess.graph_def, ["op_to_store"] - ) - - graph_def.ParseFromString(constant_graph.SerializeToString()) - with graph.as_default(): - tf.import_graph_def(graph_def, name="") - return graph - - -def build_fake_strategy(): - with open( - os.path.join( - os.path.dirname(importlib.util.find_spec("neural_compressor").origin), "experimental/strategy/fake.py" - ), - "w", - encoding="utf-8", - ) as f: - seq = [ - "import time \n", - "import copy \n", - "import numpy as np \n", - "from collections import OrderedDict \n", - "from .strategy import strategy_registry, TuneStrategy \n", - "from ...utils import logger \n", - "from .utils.tuning_sampler import OpTypeWiseTuningSampler, FallbackTuningSampler \n", - "from .utils.tuning_structs import OpTuningConfig \n", - "import copy \n", - "@strategy_registry \n", - "class FakeTuneStrategy(TuneStrategy): \n", - " def __init__(self, model, cfg, q_dataloader, q_func=None, eval_dataloader=None, \n", - " eval_func=None, dicts=None, q_hooks=None): \n", - " self.id = 0 \n", - " self.resume = True if dicts else False \n", - " super(FakeTuneStrategy, self).__init__(model, cfg, q_dataloader, \n", - " q_func, eval_dataloader, eval_func, dicts) \n", - " def __getstate__(self): \n", - " for history in self.tuning_history: \n", - " if self._same_yaml(history['cfg'], self.cfg): \n", - " history['id'] = self.id \n", - " save_dict = super(FakeTuneStrategy, self).__getstate__() \n", - " return save_dict \n", - " def next_tune_cfg(self): \n", - " if self.resume: \n", - " #assert self.id == 1 \n", - " assert len(self.tuning_history) == 1 \n", - " history = self.tuning_history[0] \n", - " assert self._same_yaml(history['cfg'], self.cfg) \n", - " assert len(history['history']) \n", - " for h in history['history']: \n", - " assert h \n", - " from copy import deepcopy \n", - " tuning_space = self.tuning_space \n", - " initial_op_tuning_cfg = {} \n", - " for item in tuning_space.root_item.options: \n", - " if item.item_type == 'op': \n", - " op_name, op_type = item.name \n", - " initial_op_tuning_cfg[item.name] = OpTuningConfig(op_name, op_type, 'fp32', tuning_space) \n", - " calib_sampling_size_lst = tuning_space.root_item.get_option_by_name('calib_sampling_size').options \n", - " for calib_sampling_size in calib_sampling_size_lst: \n", - " # step1. collect the ops that support static and dynamic \n", - " quant_mode_wise_items = OrderedDict() \n", - " query_order = ['static', 'dynamic', 'bf16', 'fp16', 'fp32'] \n", - " pre_items = set() \n", - " for quant_mode in query_order: \n", - " items = tuning_space.query_items_by_quant_mode(quant_mode) \n", - " filtered_items = [item for item in items if item not in pre_items] \n", - " pre_items = pre_items.union(set(items)) \n", - " quant_mode_wise_items[quant_mode] = filtered_items \n", - " def initial_op_quant_mode(items_lst, target_quant_mode, op_item_dtype_dict): \n", - " for item in items_lst: \n", - " op_item_dtype_dict[item.name] = target_quant_mode \n", - " op_item_dtype_dict = OrderedDict() \n", - " for quant_mode, quant_mode_items in quant_mode_wise_items.items(): \n", - " initial_op_quant_mode(quant_mode_items, quant_mode, op_item_dtype_dict) \n", - " # step3. optype-wise tuning tuning items: the algorithm/scheme/granularity of activation(weight) \n", - " early_stop_tuning = False \n", - " stage1_cnt = 0 \n", - " int8_ops = quant_mode_wise_items['dynamic'] + quant_mode_wise_items['static'] \n", - " stage1_max = min(5, len(int8_ops)) # TODO set a more appropriate value \n", - " op_wise_tuning_sampler = OpTypeWiseTuningSampler(tuning_space, [], [], \n", - " op_item_dtype_dict, initial_op_tuning_cfg) \n", - " for op_tuning_cfg in op_wise_tuning_sampler: \n", - " stage1_cnt += 1 \n", - " if early_stop_tuning and stage1_cnt > stage1_max: \n", - " logger.info('Early stopping the stage 1.') \n", - " break \n", - " op_tuning_cfg['calib_sampling_size'] = calib_sampling_size \n", - " self.id += 1 \n", - " yield op_tuning_cfg \n", - ] - f.writelines(seq) - f.close() - - -class Metric: - def update(self, predict, label): - pass - - def reset(self): - pass - - def result(self): - return 0.5 - - -class TestQuantization(unittest.TestCase): - @classmethod - def setUpClass(self): - self.constant_graph = build_fake_model() - build_fake_yaml() - build_fake_yaml2() - build_fake_yaml3() - build_fake_yaml4() - build_fake_yaml5() - build_fake_yaml6() - build_fake_strategy() - - @classmethod - def tearDownClass(self): - os.remove("fake_yaml.yaml") - os.remove("fake_yaml2.yaml") - os.remove("fake_yaml3.yaml") - os.remove("fake_yaml4.yaml") - os.remove("fake_yaml5.yaml") - os.remove("fake_yaml6.yaml") - os.remove( - os.path.join( - os.path.dirname(importlib.util.find_spec("neural_compressor").origin), "experimental/strategy/fake.py" - ) - ) - shutil.rmtree("./saved", ignore_errors=True) - - def test_resume(self): - import tensorflow as tf - from tensorflow.compat.v1 import graph_util - - tf.compat.v1.disable_eager_execution() - tf.compat.v1.reset_default_graph() - tf.compat.v1.set_random_seed(1) - x = tf.compat.v1.placeholder(tf.float32, [1, 32, 32, 3], name="x") - top_relu = tf.nn.relu(x) - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(top_relu, paddings, "CONSTANT") - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 3, 3], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv2d(x_pad, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - relu = tf.nn.relu(conv) - - relu6 = tf.nn.relu6(relu, name="op_to_store") - - out_name = relu6.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml5.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 32, 32, 3), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - self.assertNotEqual(output_graph, None) - self.assertTrue(os.path.exists("./saved")) - quantizer = Quantization("fake_yaml2.yaml") - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - # self.assertNotEqual(output_graph, None) # disable this check, the code has bug of recover from resume - - def test_autodump(self): - # test auto_dump using old api - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml3.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 3, 3, 1), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = self.constant_graph - output_graph = quantizer.fit() - self.assertNotEqual(output_graph, None) - - def test_performance_only(self): - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml4.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 3, 3, 1), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = self.constant_graph - output_graph = quantizer.fit() - self.assertNotEqual(output_graph, None) - - def test_fit_method(self): - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml4.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 3, 3, 1), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = self.constant_graph - output_graph = quantizer.fit() - self.assertNotEqual(output_graph, None) - - def test_quantization_without_yaml(self): - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization() - quantizer.model = self.constant_graph - dataset = quantizer.dataset("dummy", shape=(100, 3, 3, 1), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - output_graph = quantizer.fit() - self.assertNotEqual(output_graph, None) - - def test_invalid_eval_func(self): - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 3, 3, 1), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = self.constant_graph - - def invalid_eval_func(model): - return [[1.0]] - - quantizer.eval_func = invalid_eval_func - output_graph = quantizer.fit() - self.assertEqual(output_graph, None) - - def invalid_eval_func(model): - return "0.1" - - quantizer.eval_func = invalid_eval_func - output_graph = quantizer.fit() - self.assertEqual(output_graph, None) - - def test_custom_metric(self): - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml6.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 3, 3, 1), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = self.constant_graph - quantizer.metric = Metric() - quantizer.fit() - self.assertEqual(quantizer.strategy.evaluation_result[0], 0.5) - - def test_custom_objective(self): - import tracemalloc - - from neural_compressor.experimental import Quantization, common - from neural_compressor.objective import Objective, objective_registry - - class MyObjective(Objective): - representation = "MyObj" - - def __init__(self): - super().__init__() - - def start(self): - tracemalloc.start() - - def end(self): - _, peak = tracemalloc.get_traced_memory() - tracemalloc.stop() - self._result_list.append(peak // 1048576) - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 3, 3, 1), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = self.constant_graph - quantizer.objective = MyObjective() - output_graph = quantizer.fit() - self.assertNotEqual(output_graph, None) - - class MyObjective(Objective): - representation = "Accuracy" - - def __init__(self): - super().__init__() - - def start(self): - tracemalloc.start() - - def end(self): - _, peak = tracemalloc.get_traced_memory() - tracemalloc.stop() - self._result_list.append(peak // 1048576) - - quantizer = Quantization() - with self.assertRaises(ValueError): - quantizer.objective = MyObjective() - - with self.assertRaises(ValueError): - - @objective_registry - class MyObjective(Objective): - representation = "Accuracy" - - def __init__(self): - super().__init__() - - def start(self): - tracemalloc.start() - - def end(self): - _, peak = tracemalloc.get_traced_memory() - tracemalloc.stop() - self._result_list.append(peak // 1048576) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/quantization/test_tensorflow_recipe.py b/test/quantization/test_tensorflow_recipe.py deleted file mode 100644 index 5498126672d..00000000000 --- a/test/quantization/test_tensorflow_recipe.py +++ /dev/null @@ -1,500 +0,0 @@ -# -# -*- coding: utf-8 -*- -# -import os -import unittest - -import tensorflow as tf -import yaml -from tensorflow.compat.v1 import graph_util - -from neural_compressor.adaptor.tf_utils.util import disable_random - - -def build_fake_yaml_disable_first_quantization(): - fake_yaml = """ - model: - name: fake_yaml - framework: tensorflow - inputs: input - outputs: op_to_store - device: cpu - quantization: - recipes: - first_conv_or_matmul_quantization: False - model_wise: - weight: - granularity: per_tensor - scheme: sym - dtype: int8 - algorithm: minmax - evaluation: - accuracy: - metric: - topk: 1 - tuning: - strategy: - name: basic - accuracy_criterion: - relative: 0.1 - exit_policy: - performance_only: True - workspace: - path: saved - """ - y = yaml.load(fake_yaml, Loader=yaml.SafeLoader) - with open("fake_yaml_disable_first_quantization.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - f.close() - - -def build_fake_yaml_enable_first_quantization(): - fake_yaml = """ - model: - name: fake_yaml - framework: tensorflow - inputs: input - outputs: op_to_store - device: cpu - quantization: - recipes: - first_conv_or_matmul_quantization: True - model_wise: - weight: - granularity: per_tensor - scheme: sym - dtype: int8 - algorithm: minmax - evaluation: - accuracy: - metric: - topk: 1 - tuning: - strategy: - name: basic - accuracy_criterion: - relative: 0.1 - exit_policy: - performance_only: True - workspace: - path: saved - """ - y = yaml.load(fake_yaml, Loader=yaml.SafeLoader) - with open("fake_yaml_enable_first_quantization.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - f.close() - - -def build_fake_yaml_disable_scale_propagation(): - fake_yaml = """ - model: - name: fake_yaml - framework: tensorflow - inputs: input - outputs: op_to_store - device: cpu - quantization: - recipes: - scale_propagation_max_pooling: False - model_wise: - weight: - granularity: per_tensor - scheme: sym - dtype: int8 - algorithm: minmax - evaluation: - accuracy: - metric: - topk: 1 - tuning: - strategy: - name: basic - accuracy_criterion: - relative: 0.1 - exit_policy: - performance_only: True - workspace: - path: saved - """ - y = yaml.load(fake_yaml, Loader=yaml.SafeLoader) - with open("fake_yaml_disable_scale_propagation.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - f.close() - - -def build_fake_yaml_enable_scale_propagation(): - fake_yaml = """ - model: - name: fake_yaml - framework: tensorflow - inputs: input - outputs: op_to_store - device: cpu - quantization: - recipes: - scale_propagation_max_pooling: True - model_wise: - weight: - granularity: per_tensor - scheme: sym - dtype: int8 - algorithm: minmax - evaluation: - accuracy: - metric: - topk: 1 - tuning: - strategy: - name: basic - accuracy_criterion: - relative: 0.1 - exit_policy: - performance_only: True - workspace: - path: saved - """ - y = yaml.load(fake_yaml, Loader=yaml.SafeLoader) - with open("fake_yaml_enable_scale_propagation.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - f.close() - - -def build_fake_yaml_enable_scale_unification(): - fake_yaml = """ - model: - name: fake_yaml - framework: tensorflow - inputs: input - outputs: op_to_store - device: cpu - quantization: - recipes: - scale_propagation_concat: True - model_wise: - weight: - granularity: per_tensor - scheme: sym - dtype: int8 - algorithm: minmax - evaluation: - accuracy: - metric: - topk: 1 - tuning: - strategy: - name: basic - accuracy_criterion: - relative: 0.1 - exit_policy: - performance_only: True - workspace: - path: saved - """ - y = yaml.load(fake_yaml, Loader=yaml.SafeLoader) - with open("fake_yaml_enable_scale_unification.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - f.close() - - -def build_fake_yaml_disable_scale_unification(): - fake_yaml = """ - model: - name: fake_yaml - framework: tensorflow - inputs: input - outputs: op_to_store - device: cpu - quantization: - recipes: - scale_propagation_concat: False - model_wise: - weight: - granularity: per_tensor - scheme: sym - dtype: int8 - algorithm: minmax - evaluation: - accuracy: - metric: - topk: 1 - tuning: - strategy: - name: basic - accuracy_criterion: - relative: 0.1 - exit_policy: - performance_only: False - workspace: - path: saved - """ - y = yaml.load(fake_yaml, Loader=yaml.SafeLoader) - with open("fake_yaml_disable_scale_unification.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - f.close() - - -class TestTensorflowInt8Recipe(unittest.TestCase): - @classmethod - def setUpClass(self): - build_fake_yaml_disable_first_quantization() - build_fake_yaml_enable_first_quantization() - build_fake_yaml_disable_scale_propagation() - build_fake_yaml_enable_scale_propagation() - build_fake_yaml_enable_scale_unification() - build_fake_yaml_disable_scale_unification() - - @classmethod - def tearDownClass(self): - os.remove("fake_yaml_disable_first_quantization.yaml") - os.remove("fake_yaml_enable_first_quantization.yaml") - os.remove("fake_yaml_disable_scale_propagation.yaml") - os.remove("fake_yaml_enable_scale_propagation.yaml") - os.remove("fake_yaml_disable_scale_unification.yaml") - os.remove("fake_yaml_enable_scale_unification.yaml") - - @disable_random() - def test_disable_first_quantization(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - top_relu = tf.nn.relu(x) - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(top_relu, paddings, "CONSTANT") - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv2d(x_pad, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - normed = tf.compat.v1.layers.batch_normalization(conv) - - relu = tf.nn.relu(normed) - - relu6 = tf.nn.relu6(relu, name="op_to_store") - - out_name = relu6.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml_disable_first_quantization.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - - found_fp32_conv = False - - for i in output_graph.graph_def.node: - if i.op == "Conv2D": - found_fp32_conv = True - break - - self.assertEqual(found_fp32_conv, True) - - @disable_random() - def test_enable_first_quantization(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - top_relu = tf.nn.relu(x) - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(top_relu, paddings, "CONSTANT") - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv2d(x_pad, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - normed = tf.compat.v1.layers.batch_normalization(conv) - - relu = tf.nn.relu(normed) - - relu6 = tf.nn.relu6(relu, name="op_to_store") - - out_name = relu6.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml_enable_first_quantization.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - - found_fp32_conv = False - - for i in output_graph.graph_def.node: - if i.op == "Conv2D": - found_fp32_conv = True - break - - self.assertEqual(found_fp32_conv, False) - - @disable_random() - def test_enable_scale_propagation(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 30, 30, 1], name="input") - conv_weights = tf.compat.v1.get_variable( - "weight", [2, 2, 1, 1], initializer=tf.compat.v1.random_normal_initializer() - ) - conv_bias = tf.compat.v1.get_variable("bias", [1], initializer=tf.compat.v1.random_normal_initializer()) - - x = tf.nn.relu(x) - conv = tf.nn.conv2d(x, conv_weights, strides=[1, 2, 2, 1], padding="SAME", name="last") - normed = tf.compat.v1.layers.batch_normalization(conv) - - relu = tf.nn.relu(normed) - pool = tf.nn.avg_pool(relu, ksize=1, strides=[1, 2, 2, 1], padding="SAME") - conv1 = tf.nn.conv2d(pool, conv_weights, strides=[1, 2, 2, 1], padding="SAME", name="last") - conv_bias = tf.nn.bias_add(conv1, conv_bias) - x = tf.nn.relu(conv_bias) - final_node = tf.nn.relu(x, name="op_to_store") - - out_name = final_node.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml_enable_scale_propagation.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 30, 30, 1), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - - max_freezed_out = [] - for i in output_graph.graph_def.node: - if i.op == "QuantizedConv2DWithBiasAndReluAndRequantize": - max_freezed_out.append(i.input[-1]) - - self.assertEqual(1, len(set(max_freezed_out))) - - @disable_random() - def test_disable_scale_propagation(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 30, 30, 1], name="input") - conv_weights = tf.compat.v1.get_variable( - "weight", [2, 2, 1, 1], initializer=tf.compat.v1.random_normal_initializer() - ) - conv_bias = tf.compat.v1.get_variable("bias", [1], initializer=tf.compat.v1.random_normal_initializer()) - - x = tf.nn.relu(x) - conv = tf.nn.conv2d(x, conv_weights, strides=[1, 2, 2, 1], padding="SAME", name="last") - normed = tf.compat.v1.layers.batch_normalization(conv) - - relu = tf.nn.relu(normed) - pool = tf.nn.avg_pool(relu, ksize=1, strides=[1, 2, 2, 1], padding="SAME") - conv1 = tf.nn.conv2d(pool, conv_weights, strides=[1, 2, 2, 1], padding="SAME", name="last") - conv_bias = tf.nn.bias_add(conv1, conv_bias) - x = tf.nn.relu(conv_bias) - final_node = tf.nn.relu(x, name="op_to_store") - - out_name = final_node.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml_disable_scale_propagation.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 30, 30, 1), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - - max_freezed_out = [] - for i in output_graph.graph_def.node: - if i.op == "QuantizedConv2DWithBiasAndReluAndRequantize": - max_freezed_out.append(i.input[-1]) - self.assertEqual(2, len(set(max_freezed_out))) - - @disable_random() - def test_enable_scale_unification(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 128, 128, 16], name="input") - conv_weights = tf.compat.v1.get_variable( - "weight", [2, 2, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv_bias = tf.compat.v1.get_variable("bias", [16], initializer=tf.compat.v1.random_normal_initializer()) - - x = tf.nn.relu(x) - sqrt = tf.math.sqrt(x) - relu_sqrt = tf.nn.relu(sqrt) - conv = tf.nn.conv2d(relu_sqrt, conv_weights, strides=[1, 2, 2, 1], padding="SAME", name="last") - normed = tf.compat.v1.layers.batch_normalization(conv) - - relu = tf.nn.relu(normed) - conv1 = tf.nn.conv2d(x, conv_weights, strides=[1, 2, 2, 1], padding="SAME", name="last") - conv_bias = tf.nn.bias_add(conv1, conv_bias) - relu1 = tf.nn.relu(conv_bias) - concat = tf.concat([relu, relu1], 1) - final_node = tf.nn.relu(concat, name="op_to_store") - out_name = final_node.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml_enable_scale_unification.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 128, 128, 16), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - - max_freezed_out = [] - for i in output_graph.graph_def.node: - if i.op == "QuantizedConv2DWithBiasAndReluAndRequantize": - max_freezed_out.append(i.input[-1]) - self.assertEqual(1, len(set(max_freezed_out))) - - @disable_random() - def test_disable_scale_unification(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 30, 30, 1], name="input") - conv_weights = tf.compat.v1.get_variable( - "weight", [2, 2, 1, 1], initializer=tf.compat.v1.random_normal_initializer() - ) - conv_bias = tf.compat.v1.get_variable("bias", [1], initializer=tf.compat.v1.random_normal_initializer()) - - x = tf.nn.relu(x) - sqrt = tf.math.sqrt(x) - relu_sqrt = tf.nn.relu(sqrt) - conv = tf.nn.conv2d(relu_sqrt, conv_weights, strides=[1, 2, 2, 1], padding="SAME", name="last") - normed = tf.compat.v1.layers.batch_normalization(conv) - - relu = tf.nn.relu(normed) - conv1 = tf.nn.conv2d(x, conv_weights, strides=[1, 2, 2, 1], padding="SAME", name="last") - conv_bias = tf.nn.bias_add(conv1, conv_bias) - relu1 = tf.nn.relu(conv_bias) - concat = tf.concat([relu, relu1], 1) - final_node = tf.nn.relu(concat, name="op_to_store") - out_name = final_node.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml_disable_scale_unification.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 30, 30, 1), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - - max_freezed_out = [] - for i in output_graph.graph_def.node: - if i.op == "QuantizedConv2DWithBiasAndReluAndRequantize": - max_freezed_out.append(i.input[-1]) - self.assertEqual(2, len(set(max_freezed_out))) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/quantization/test_tensorflow_recover.py b/test/quantization/test_tensorflow_recover.py deleted file mode 100644 index ad1d84f5962..00000000000 --- a/test/quantization/test_tensorflow_recover.py +++ /dev/null @@ -1,217 +0,0 @@ -# -# -*- coding: utf-8 -*- -# -import logging -import os -import shutil -import unittest - -import tensorflow as tf -import yaml -from tensorflow.compat.v1 import graph_util -from tensorflow.python.framework import tensor_util -from tensorflow.python.platform import gfile - -from neural_compressor.adaptor.tf_utils.util import disable_random - -logger = logging.getLogger("neural_compressor") -logger.setLevel(logging.DEBUG) - - -def build_fake_yaml(): - fake_yaml = """ - model: - name: fake_yaml - framework: tensorflow - inputs: input - outputs: op_to_store - device: cpu - evaluation: - accuracy: - metric: - topk: 1 - tuning: - accuracy_criterion: - relative: 0.0001 - workspace: - path: saved - """ - y = yaml.load(fake_yaml, Loader=yaml.SafeLoader) - with open("fake_yaml.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - f.close() - - -def build_fake_yaml_2(): - fake_yaml = """ - model: - name: fake_yaml - framework: tensorflow - inputs: input - outputs: op_to_store - device: cpu - graph_optimization: - precisions: [bf16] - evaluation: - accuracy: - metric: - topk: 1 - tuning: - accuracy_criterion: - relative: 0.0001 - workspace: - path: saved - """ - y = yaml.load(fake_yaml, Loader=yaml.SafeLoader) - with open("fake_yaml_2.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - f.close() - - -class TestTensorflowRecover(unittest.TestCase): - @classmethod - def setUpClass(self): - build_fake_yaml() - - @classmethod - def tearDownClass(self): - os.remove("fake_yaml.yaml") - os.remove("test.pb") - shutil.rmtree("./saved", ignore_errors=True) - - @disable_random() - def test_tensorflow_recover(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - top_relu = tf.nn.relu(x) - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(top_relu, paddings, "CONSTANT") - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv_weights_2 = tf.compat.v1.get_variable( - "weight_2", [3, 8, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv2d(x_pad, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - relu = tf.nn.relu(conv) - - max_pool = tf.nn.max_pool(relu, ksize=1, strides=[1, 2, 2, 1], padding="SAME") - conv_bias = tf.compat.v1.get_variable("bias", [16], initializer=tf.compat.v1.random_normal_initializer()) - conv_1 = tf.nn.conv2d(max_pool, conv_weights_2, strides=[1, 2, 2, 1], padding="VALID", name="conv1_3") - conv_bias = tf.math.add(conv_1, conv_bias) - relu6 = tf.nn.relu6(conv_bias, name="op_to_store") - - out_name = relu6.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - constant_graph = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - with gfile.GFile("./test.pb", "wb") as f: - f.write(constant_graph.SerializeToString()) - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("./fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = constant_graph - q_model = quantizer.fit() - - from neural_compressor.utils.utility import recover - - recover_model = recover("./test.pb", "./saved/history.snapshot", 0) - - q_model_const_value = {} - for node in q_model.graph_def.node: - if node.op == "Const": - tensor_value = tensor_util.MakeNdarray(node.attr["value"].tensor) - if not tensor_value.shape: - q_model_const_value[node.name] = tensor_value - for node in recover_model.graph_def.node: - if node.op == "Const": - tensor_value = tensor_util.MakeNdarray(node.attr["value"].tensor) - if node.name in q_model_const_value: - self.assertEqual(tensor_value, q_model_const_value[node.name]) - - -class TestTensorflowRecoverForceBF16(unittest.TestCase): - @classmethod - def setUpClass(self): - os.environ["FORCE_BF16"] = "1" - build_fake_yaml_2() - - @classmethod - def tearDownClass(self): - del os.environ["FORCE_BF16"] - os.remove("fake_yaml_2.yaml") - if os.path.exists("./test.pb"): - os.remove("test.pb") - shutil.rmtree("./saved", ignore_errors=True) - - @disable_random() - @unittest.skipIf(tf.__version__ < "2.0", "currently bf16 converter only support tf > 2.0") - def test_tensorflow_recover_bf16(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - top_relu = tf.nn.relu(x) - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(top_relu, paddings, "CONSTANT") - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv_weights_2 = tf.compat.v1.get_variable( - "weight_2", [3, 8, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv2d(x_pad, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - relu = tf.nn.relu(conv) - - max_pool = tf.nn.max_pool(relu, ksize=1, strides=[1, 2, 2, 1], padding="SAME") - conv_bias = tf.compat.v1.get_variable("bias", [16], initializer=tf.compat.v1.random_normal_initializer()) - conv_1 = tf.nn.conv2d(max_pool, conv_weights_2, strides=[1, 2, 2, 1], padding="VALID", name="conv1_3") - conv_bias = tf.math.add(conv_1, conv_bias) - relu6 = tf.nn.relu6(conv_bias, name="op_to_store") - - out_name = relu6.name.split(":")[0] - - def eval(model): - return 0.5 - - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - constant_graph = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - with gfile.GFile("./test.pb", "wb") as f: - f.write(constant_graph.SerializeToString()) - - from neural_compressor.experimental import MixedPrecision - - convert = MixedPrecision("./fake_yaml_2.yaml") - convert.model = constant_graph - convert.eval_func = eval - output_model = convert.fit() - found_cast_op = False - - from neural_compressor.utils.utility import recover - - recover_model = recover("./test.pb", "./saved/history.snapshot", 0) - - q_model_const_value = {} - for node in output_model.graph_def.node: - if node.op == "Const": - tensor_value = tensor_util.MakeNdarray(node.attr["value"].tensor) - if not tensor_value.shape: - q_model_const_value[node.name] = tensor_value - for node in recover_model.graph_def.node: - if node.op == "Cast": - found_cast_op = True - continue - if node.op == "Const": - tensor_value = tensor_util.MakeNdarray(node.attr["value"].tensor) - if node.name in q_model_const_value: - self.assertEqual(tensor_value, q_model_const_value[node.name]) - - self.assertEqual(found_cast_op, True) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/scheduler/test_oneshot.py b/test/scheduler/test_oneshot.py deleted file mode 100644 index ac1e914bc20..00000000000 --- a/test/scheduler/test_oneshot.py +++ /dev/null @@ -1,589 +0,0 @@ -import copy -import os -import shutil -import unittest - -import torch -import torch.nn as nn -import torchvision -from packaging.version import Version - -import neural_compressor.adaptor.pytorch as nc_torch -from neural_compressor.conf.config import DistillationConf, PruningConf -from neural_compressor.data import Datasets -from neural_compressor.experimental.data.dataloaders.pytorch_dataloader import PyTorchDataLoader -from neural_compressor.experimental.scheduler import Scheduler -from neural_compressor.training import prepare_compression -from neural_compressor.utils import logger -from neural_compressor.utils.pytorch import load - -PT_VERSION = nc_torch.get_torch_version() -if PT_VERSION >= Version("1.8.0-rc1"): - FX_MODE = True -else: - FX_MODE = False - -fake_yaml = """ -model: - name: imagenet_prune - framework: pytorch - -pruning: - approach: - weight_compression: - initial_sparsity: 0.0 - target_sparsity: 0.97 - start_epoch: 0 - end_epoch: 3 - pruners: - - !Pruner - start_epoch: 1 - end_epoch: 3 - prune_type: basic_magnitude - names: ['layer1.0.conv1.weight'] - - - !Pruner - target_sparsity: 0.6 - prune_type: basic_magnitude - update_frequency: 2 - names: ['layer1.0.conv2.weight'] -evaluation: - accuracy: - metric: - topk: 1 -""" - -fake2_yaml = """ -model: - name: imagenet_qat - framework: pytorch - -quantization: - approach: quant_aware_training - -evaluation: - accuracy: - metric: - topk: 1 -tuning: - accuracy_criterion: - relative: 0.01 - exit_policy: - timeout: 0 - random_seed: 9527 -""" - -fake3_yaml = """ -model: - name: imagenet_distillation - framework: pytorch - -distillation: - train: - optimizer: - SGD: - learning_rate: 0.001 - momentum: 0.1 - nesterov: True - weight_decay: 0.001 - criterion: - KnowledgeDistillationLoss: - temperature: 1.0 - loss_types: ['CE', 'KL'] - loss_weights: [0.5, 0.5] - dataloader: - batch_size: 1 - dataset: - dummy: - shape: [16, 3, 224, 224] - label: True - -evaluation: - accuracy: - metric: - topk: 1 - dataloader: - batch_size: 1 - dataset: - dummy: - shape: [16, 3, 224, 224] - label: True -tuning: - accuracy_criterion: - relative: 0.01 - exit_policy: - timeout: 0 - random_seed: 9527 -""" - - -def build_fake_yaml(): - with open("fake.yaml", "w", encoding="utf-8") as f: - f.write(fake_yaml) - - -def build_fake_yaml2(): - with open("fake2.yaml", "w", encoding="utf-8") as f: - f.write(fake2_yaml) - - -def build_fake_yaml3(): - with open("fake3.yaml", "w", encoding="utf-8") as f: - f.write(fake3_yaml) - - -def build_fx_fake_yaml(): - fx_fake_yaml = fake_yaml.replace("pytorch", "pytorch_fx") - with open("fx_fake.yaml", "w", encoding="utf-8") as f: - f.write(fx_fake_yaml) - - -def build_fx_fake_yaml2(): - fx_fake2_yaml = fake2_yaml.replace("pytorch", "pytorch_fx") - with open("fx_fake2.yaml", "w", encoding="utf-8") as f: - f.write(fx_fake2_yaml) - - -def build_fx_fake_yaml3(): - fx_fake3_yaml = fake3_yaml.replace("pytorch", "pytorch_fx") - with open("fx_fake3.yaml", "w", encoding="utf-8") as f: - f.write(fx_fake3_yaml) - - -class DynamicControlModel(torch.nn.Module): - def __init__(self): - super().__init__() - self.conv = nn.Conv2d(3, 1, 1) - self.bn = nn.BatchNorm2d(1) - self.linear = nn.Linear(224 * 224, 1) - - def forward(self, x): - x = self.conv(x) - x = self.bn(x) - if x.size(1) == 1: - x = x.view(1, -1) - x = self.linear(x) - return x - - -class TestPruning(unittest.TestCase): - model = torchvision.models.resnet18() - q_model = torchvision.models.quantization.resnet18() - q_model.fuse_model() - - @classmethod - def setUpClass(cls): - build_fake_yaml() - build_fake_yaml2() - build_fake_yaml3() - build_fx_fake_yaml() - build_fx_fake_yaml2() - build_fx_fake_yaml3() - - @classmethod - def tearDownClass(cls): - os.remove("fake.yaml") - os.remove("fake2.yaml") - os.remove("fake3.yaml") - os.remove("fx_fake.yaml") - os.remove("fx_fake2.yaml") - os.remove("fx_fake3.yaml") - shutil.rmtree("./saved", ignore_errors=True) - shutil.rmtree("runs", ignore_errors=True) - shutil.rmtree("nc_workspace", ignore_errors=True) - - def test_prune_qat_oneshot(self): - from neural_compressor.experimental import Pruning, Quantization - - datasets = Datasets("pytorch") - dummy_dataset = datasets["dummy"](shape=(16, 3, 224, 224), low=0.0, high=1.0, label=True) - dummy_dataloader = PyTorchDataLoader(dummy_dataset) - q_model = copy.deepcopy(self.q_model) - prune = Pruning("./fake.yaml") - quantizer = Quantization("./fake2.yaml") - scheduler = Scheduler() - scheduler.model = q_model - combination = scheduler.combine(prune, quantizer) - - def train_func_for_nc(model): - epochs = 3 - iters = 3 - criterion = nn.CrossEntropyLoss() - optimizer = torch.optim.SGD(model.parameters(), lr=0.0001) - combination.on_train_begin() - for nepoch in range(epochs): - model.train() - cnt = 0 - combination.on_epoch_begin(nepoch) - for image, target in dummy_dataloader: - combination.on_step_begin(cnt) - print(".", end="") - cnt += 1 - output = model(image) - loss = criterion(output, target) - optimizer.zero_grad() - loss.backward() - optimizer.step() - combination.on_step_end() - if cnt >= iters: - break - combination.on_epoch_end() - combination.on_train_end() - - combination.train_func = train_func_for_nc - combination.eval_dataloader = dummy_dataloader - combination.train_dataloader = dummy_dataloader - scheduler.append(combination) - opt_model = scheduler() - opt_model.save("./saved") - logger.info(20 * "=" + "test_prune_qat_oneshot" + 20 * "=") - - try: - conv_weight = opt_model.model.layer1[0].conv1.weight().dequantize() - except: - conv_weight = opt_model.model.layer1[0].conv1.weight - self.assertAlmostEqual((conv_weight == 0).sum().item() / conv_weight.numel(), 0.64, delta=0.05) - self.assertEqual(combination.__repr__().lower(), "combination of pruning,quantization") - # reloading int8 model - reloaded_model = load("./saved", copy.deepcopy(self.q_model)) - try: - reloaded_conv_weight = reloaded_model.layer1[0].conv1.weight().dequantize() - except: - reloaded_conv_weight = reloaded_model.layer1[0].conv1.weight - self.assertEqual(reloaded_conv_weight.sum().item(), conv_weight.sum().item()) - - def test_distillation_qat_oneshot(self): - from neural_compressor.experimental import Distillation, Quantization - - datasets = Datasets("pytorch") - dummy_dataset = datasets["dummy"](shape=(16, 3, 224, 224), low=0.0, high=1.0, label=True) - dummy_dataloader = PyTorchDataLoader(dummy_dataset) - model = copy.deepcopy(self.model) - q_model = copy.deepcopy(self.q_model) - distiller = Distillation("./fake3.yaml") - quantizer = Quantization("./fake2.yaml") - scheduler = Scheduler() - distiller.teacher_model = model - scheduler.model = q_model - combination = scheduler.combine(distiller, quantizer) - - def train_func_for_nc(model): - epochs = 3 - iters = 3 - criterion = nn.CrossEntropyLoss() - optimizer = torch.optim.SGD(model.parameters(), lr=0.0001) - combination.on_train_begin() - for nepoch in range(epochs): - model.train() - cnt = 0 - combination.on_epoch_begin(nepoch) - for image, target in dummy_dataloader: - combination.on_step_begin(cnt) - print(".", end="") - cnt += 1 - output = model(image) - loss = criterion(output, target) - loss = combination.on_after_compute_loss(image, output, loss) - optimizer.zero_grad() - loss.backward() - optimizer.step() - combination.on_step_end() - if cnt >= iters: - break - combination.on_epoch_end() - combination.on_train_end() - - combination.train_func = train_func_for_nc - combination.eval_dataloader = dummy_dataloader - combination.train_dataloader = dummy_dataloader - scheduler.append(combination) - opt_model = scheduler() - opt_model.save("./saved") - logger.info(20 * "=" + "test_distillation_qat_oneshot" + 20 * "=") - - self.assertEqual(combination.__repr__().lower(), "combination of distillation,quantization") - # reloading int8 model - reloaded_model = load("./saved", copy.deepcopy(self.q_model)) - - def test_prune_qat_distillation_oneshot(self): - from neural_compressor.experimental import Distillation, Pruning, Quantization - - datasets = Datasets("pytorch") - dummy_dataset = datasets["dummy"](shape=(16, 3, 224, 224), low=0.0, high=1.0, label=True) - dummy_dataloader = PyTorchDataLoader(dummy_dataset) - model = copy.deepcopy(self.model) - q_model = copy.deepcopy(self.q_model) - prune = Pruning("./fake.yaml") - quantizer = Quantization("./fake2.yaml") - distiller = Distillation("./fake3.yaml") - scheduler = Scheduler() - distiller.teacher_model = model - scheduler.model = q_model - combination = scheduler.combine(prune, quantizer, distiller) - - def train_func_for_nc(model): - epochs = 3 - iters = 3 - criterion = nn.CrossEntropyLoss() - optimizer = torch.optim.SGD(model.parameters(), lr=0.0001) - combination.on_train_begin() - for nepoch in range(epochs): - model.train() - cnt = 0 - combination.on_epoch_begin(nepoch) - for image, target in dummy_dataloader: - combination.on_step_begin(cnt) - print(".", end="") - cnt += 1 - output = model(image) - loss = criterion(output, target) - loss = combination.on_after_compute_loss(image, output, loss) - optimizer.zero_grad() - loss.backward() - optimizer.step() - combination.on_step_end() - if cnt >= iters: - break - combination.on_epoch_end() - combination.on_train_end() - return model - - combination.train_func = train_func_for_nc - combination.eval_dataloader = dummy_dataloader - combination.train_dataloader = dummy_dataloader - scheduler.append(combination) - opt_model = scheduler() - logger.info(20 * "=" + "test_prune_qat_distillation_oneshot" + 20 * "=") - - try: - conv_weight = opt_model.model.layer1[0].conv1.weight().dequantize() - except: - conv_weight = opt_model.model.layer1[0].conv1.weight - self.assertAlmostEqual((conv_weight == 0).sum().item() / conv_weight.numel(), 0.64, delta=0.05) - self.assertEqual(combination.__repr__().lower(), "combination of pruning,quantization,distillation") - - def test_prune_qat_oneshot_fx(self): - from neural_compressor.experimental import Pruning, Quantization - - datasets = Datasets("pytorch_fx") - dummy_dataset = datasets["dummy"](shape=(16, 3, 224, 224), low=0.0, high=1.0, label=True) - dummy_dataloader = PyTorchDataLoader(dummy_dataset) - prune = Pruning("./fx_fake.yaml") - quantizer = Quantization("./fx_fake2.yaml") - scheduler = Scheduler() - model = copy.deepcopy(self.model) - scheduler.model = model - combination = scheduler.combine(prune, quantizer) - - def train_func_for_nc(model): - epochs = 3 - iters = 3 - criterion = nn.CrossEntropyLoss() - optimizer = torch.optim.SGD(model.parameters(), lr=0.0001) - combination.on_train_begin(dummy_dataloader) - model = combination.model.model - for nepoch in range(epochs): - model.train() - cnt = 0 - combination.on_epoch_begin(nepoch) - for image, target in dummy_dataloader: - combination.on_step_begin(cnt) - print(".", end="") - cnt += 1 - output = model(image) - loss = criterion(output, target) - optimizer.zero_grad() - loss.backward() - optimizer.step() - combination.on_step_end() - if cnt >= iters: - break - combination.on_epoch_end() - combination.on_train_end() - return model - - combination.train_func = train_func_for_nc - combination.eval_dataloader = dummy_dataloader - combination.train_dataloader = dummy_dataloader - scheduler.append(combination) - opt_model = scheduler() - opt_model.save("./saved") - logger.info(20 * "=" + "test_prune_qat_oneshot_fx" + 20 * "=") - conv_weight = opt_model.model.state_dict()["layer1.0.conv1.weight"] - self.assertAlmostEqual((conv_weight == 0).sum().item() / conv_weight.numel(), 0.64, delta=0.05) - self.assertEqual(combination.__repr__().lower(), "combination of pruning,quantization") - # reloading int8 model - reloaded_model = load("./saved", copy.deepcopy(self.model), dataloader=dummy_dataloader) - reloaded_conv_weight = reloaded_model.state_dict()["layer1.0.conv1.weight"] - self.assertTrue(torch.equal(reloaded_conv_weight, conv_weight)) - - @unittest.skipIf(PT_VERSION < Version("1.9.0-rc1"), "requires higher version of torch than 1.9.0") - def test_distillation_qat_oneshot_fx(self): - from neural_compressor.experimental import Distillation, Quantization - - datasets = Datasets("pytorch_fx") - dummy_dataset = datasets["dummy"](shape=(16, 3, 224, 224), low=0.0, high=1.0, label=True) - dummy_dataloader = PyTorchDataLoader(dummy_dataset) - model = DynamicControlModel() - distiller = Distillation("./fx_fake3.yaml") - quantizer = Quantization("./fx_fake2.yaml") - scheduler = Scheduler() - distiller.teacher_model = copy.deepcopy(model) - scheduler.model = model - combination = scheduler.combine(distiller, quantizer) - - def train_func_for_nc(model): - epochs = 3 - iters = 3 - criterion = nn.CrossEntropyLoss() - optimizer = torch.optim.SGD(model.parameters(), lr=0.0001) - combination.on_train_begin() - for nepoch in range(epochs): - model.train() - cnt = 0 - combination.on_epoch_begin(nepoch) - for image, target in dummy_dataloader: - combination.on_step_begin(cnt) - print(".", end="") - cnt += 1 - output = model(image) - loss = criterion(output, target) - loss = combination.on_after_compute_loss(image, output, loss) - optimizer.zero_grad() - loss.backward() - optimizer.step() - combination.on_step_end() - if cnt >= iters: - break - combination.on_epoch_end() - combination.on_train_end() - return model - - combination.train_func = train_func_for_nc - combination.eval_dataloader = dummy_dataloader - combination.train_dataloader = dummy_dataloader - scheduler.append(combination) - opt_model = scheduler() - opt_model.save("./saved") - logger.info(20 * "=" + "test_distillation_qat_oneshot_fx" + 20 * "=") - - self.assertEqual(combination.__repr__().lower(), "combination of distillation,quantization") - # reloading int8 model - model = DynamicControlModel() - reloaded_model = load("./saved", model, dataloader=dummy_dataloader) - - def test_distillation_prune_oneshot_fx(self): - from neural_compressor.experimental import Distillation, Pruning - - datasets = Datasets("pytorch_fx") - dummy_dataset = datasets["dummy"](shape=(16, 3, 224, 224), low=0.0, high=1.0, label=True) - dummy_dataloader = PyTorchDataLoader(dummy_dataset) - distiller = Distillation("./fx_fake3.yaml") - pruner = Pruning("./fx_fake.yaml") - scheduler = Scheduler() - model = copy.deepcopy(self.model) - distiller.teacher_model = copy.deepcopy(model) - scheduler.model = model - combination = scheduler.combine(distiller, pruner) - - def train_func_for_nc(model): - epochs = 3 - iters = 3 - criterion = nn.CrossEntropyLoss() - optimizer = torch.optim.SGD(model.parameters(), lr=0.0001) - combination.on_train_begin(dummy_dataloader) - for nepoch in range(epochs): - model.train() - cnt = 0 - combination.on_epoch_begin(nepoch) - for image, target in dummy_dataloader: - combination.on_step_begin(cnt) - print(".", end="") - cnt += 1 - output = model(image) - loss = criterion(output, target) - loss = combination.on_after_compute_loss(image, output, loss) - optimizer.zero_grad() - loss.backward() - optimizer.step() - combination.on_step_end() - if cnt >= iters: - break - combination.on_epoch_end() - combination.on_train_end() - return model - - combination.train_func = train_func_for_nc - combination.eval_dataloader = dummy_dataloader - combination.train_dataloader = dummy_dataloader - scheduler.append(combination) - opt_model = scheduler() - logger.info(20 * "=" + "test_distillation_prune_oneshot_fx" + 20 * "=") - - try: - conv_weight = dict(opt_model.model.layer1.named_modules())["0"].conv1.weight().dequantize() - except: - conv_weight = dict(opt_model.model.layer1.named_modules())["0"].conv1.weight - self.assertAlmostEqual((conv_weight == 0).sum().item() / conv_weight.numel(), 0.64, delta=0.05) - self.assertEqual(combination.__repr__().lower(), "combination of distillation,pruning") - - @unittest.skipIf(PT_VERSION < Version("1.9.0-rc1"), "requires higher version of torch than 1.9.0") - def test_prune_qat_distillation_oneshot_fx(self): - from neural_compressor.experimental import Distillation, Pruning, Quantization - - datasets = Datasets("pytorch_fx") - dummy_dataset = datasets["dummy"](shape=(16, 3, 224, 224), low=0.0, high=1.0, label=True) - dummy_dataloader = PyTorchDataLoader(dummy_dataset) - model = copy.deepcopy(self.model) - prune = Pruning("./fx_fake.yaml") - quantizer = Quantization("./fx_fake2.yaml") - distiller = Distillation("./fx_fake3.yaml") - scheduler = Scheduler() - distiller.teacher_model = copy.deepcopy(model) - scheduler.model = model - combination = scheduler.combine(prune, quantizer, distiller) - - def train_func_for_nc(model): - epochs = 3 - iters = 3 - criterion = nn.CrossEntropyLoss() - optimizer = torch.optim.SGD(model.parameters(), lr=0.0001) - combination.on_train_begin() - for nepoch in range(epochs): - model.train() - cnt = 0 - combination.on_epoch_begin(nepoch) - for image, target in dummy_dataloader: - combination.on_step_begin(cnt) - print(".", end="") - cnt += 1 - output = model(image) - loss = criterion(output, target) - loss = combination.on_after_compute_loss(image, output, loss) - optimizer.zero_grad() - loss.backward() - optimizer.step() - combination.on_step_end() - if cnt >= iters: - break - combination.on_epoch_end() - combination.on_train_end() - return model - - combination.train_func = train_func_for_nc - combination.eval_dataloader = dummy_dataloader - combination.train_dataloader = dummy_dataloader - scheduler.append(combination) - opt_model = scheduler() - logger.info(20 * "=" + "test_prune_qat_distillation_oneshot_fx" + 20 * "=") - - try: - conv_weight = dict(opt_model.model.layer1.named_modules())["0"].conv1.weight().dequantize() - except: - conv_weight = dict(opt_model.model.layer1.named_modules())["0"].conv1.weight - self.assertAlmostEqual((conv_weight == 0).sum().item() / conv_weight.numel(), 0.64, delta=0.05) - self.assertEqual(combination.__repr__().lower(), "combination of pruning,quantization,distillation") - - -if __name__ == "__main__": - unittest.main() diff --git a/test/scheduler/test_orchestration.py b/test/scheduler/test_orchestration.py deleted file mode 100644 index 60ecef37c5d..00000000000 --- a/test/scheduler/test_orchestration.py +++ /dev/null @@ -1,84 +0,0 @@ -import copy -import os -import shutil -import unittest - -import torch -import torch.nn as nn -import torchvision - -from neural_compressor.config import ( - DistillationConfig, - KnowledgeDistillationLossConfig, - QuantizationAwareTrainingConfig, - WeightPruningConfig, -) -from neural_compressor.data import Datasets -from neural_compressor.experimental.data.dataloaders.pytorch_dataloader import PyTorchDataLoader -from neural_compressor.training import prepare_compression - - -class TestPruning(unittest.TestCase): - model = torchvision.models.resnet18() - - def test_distillation_prune_qat_oneshot_with_new_API(self): - datasets = Datasets("pytorch") - dummy_dataset = datasets["dummy"](shape=(16, 3, 224, 224), low=0.0, high=1.0, label=True) - dummy_dataloader = PyTorchDataLoader(dummy_dataset) - device = torch.device("cuda" if torch.cuda.is_available() else "cpu") - self.model.to(device) - model = copy.deepcopy(self.model) - distillation_criterion = KnowledgeDistillationLossConfig(loss_types=["CE", "KL"]) - d_conf = DistillationConfig(copy.deepcopy(self.model), distillation_criterion) - p_conf = WeightPruningConfig([{"start_step": 0, "end_step": 2}], target_sparsity=0.64, pruning_scope="local") - q_conf = QuantizationAwareTrainingConfig() - compression_manager = prepare_compression(model=model, confs=[d_conf, p_conf, q_conf]) - compression_manager.callbacks.on_train_begin() - model = compression_manager.model - - def train_func_for_nc(model): - epochs = 3 - iters = 3 - criterion = nn.CrossEntropyLoss() - optimizer = torch.optim.SGD(model.parameters(), lr=0.001, momentum=0.1, nesterov=True, weight_decay=0.001) - for nepoch in range(epochs): - model.train() - cnt = 0 - compression_manager.callbacks.on_epoch_begin(nepoch) - for image, target in dummy_dataloader: - compression_manager.callbacks.on_step_begin(cnt) - print(".", end="") - cnt += 1 - image = image.to(device) - target = target.to(device) - output = model(image) - loss = criterion(output, target) - loss = compression_manager.callbacks.on_after_compute_loss(image, output, loss) - optimizer.zero_grad() - loss.backward() - compression_manager.callbacks.on_before_optimizer_step() - optimizer.step() - compression_manager.callbacks.on_after_optimizer_step() - compression_manager.callbacks.on_step_end() - if cnt >= iters: - break - compression_manager.callbacks.on_epoch_end() - compression_manager.callbacks.on_train_end() - return model - - train_func_for_nc(model) - print(20 * "=" + "test_distillation_prune_qat_oneshot" + 20 * "=") - try: - conv_weight = dict(model.model.layer1.named_modules())["0.conv1"].weight().dequantize() - except: - conv_weight = dict(model.model.layer1.named_modules())["0.conv1"].weight() - self.assertAlmostEqual((conv_weight == 0).sum().item() / conv_weight.numel(), 0.64, delta=0.05) - self.assertTrue("quantized" in str(type(dict(model.model.layer1.named_modules())["0.conv1"]))) - self.assertEqual( - str(compression_manager.callbacks.callbacks_list), - "[Distillation Callbacks, Pruning Callbacks, Quantization Aware Training Callbacks]", - ) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/scheduler/test_scheduler.py b/test/scheduler/test_scheduler.py deleted file mode 100644 index 4dc2b04ed5b..00000000000 --- a/test/scheduler/test_scheduler.py +++ /dev/null @@ -1,468 +0,0 @@ -import os -import shutil -import unittest - -import torch -import torch.nn as nn -import torchvision -from packaging.version import Version - -import neural_compressor.adaptor.pytorch as nc_torch -from neural_compressor.data import Datasets -from neural_compressor.experimental.data.dataloaders.pytorch_dataloader import PyTorchDataLoader -from neural_compressor.experimental.scheduler import Scheduler - -PT_VERSION = nc_torch.get_torch_version() - - -def build_fake_yaml(): - fake_yaml = """ - model: - name: imagenet_prune - framework: pytorch - - pruning: - approach: - weight_compression: - initial_sparsity: 0.0 - target_sparsity: 0.97 - start_epoch: 0 - end_epoch: 3 - pruners: - - !Pruner - start_epoch: 1 - end_epoch: 3 - prune_type: basic_magnitude - names: ['layer1.0.conv1.weight'] - - - !Pruner - target_sparsity: 0.6 - prune_type: basic_magnitude - update_frequency: 2 - names: ['layer1.0.conv2.weight'] - evaluation: - accuracy: - metric: - topk: 1 - """ - with open("fake.yaml", "w", encoding="utf-8") as f: - f.write(fake_yaml) - - -def build_fake_yaml2(): - fake_yaml = """ - model: - name: imagenet_prune - framework: pytorch - - pruning: - train: - start_epoch: 0 - end_epoch: 3 - iteration: 10 - dataloader: - batch_size: 1 - dataset: - dummy: - shape: [16, 3, 224, 224] - label: True - optimizer: - SGD: - learning_rate: 0.1 - momentum: 0.1 - nesterov: True - weight_decay: 0.1 - criterion: - CrossEntropyLoss: - reduction: sum - approach: - weight_compression: - initial_sparsity: 0.0 - target_sparsity: 0.97 - start_epoch: 0 - end_epoch: 3 - pruners: - - !Pruner - start_epoch: 1 - end_epoch: 3 - prune_type: basic_magnitude - names: ['layer1.0.conv1.weight'] - - - !Pruner - start_epoch: 0 - end_epoch: 3 - target_sparsity: 0.6 - prune_type: basic_magnitude - update_frequency: 2 - names: ['layer1.0.conv2.weight'] - - evaluation: - accuracy: - metric: - topk: 1 - dataloader: - batch_size: 1 - dataset: - dummy: - shape: [16, 3, 224, 224] - label: True - """ - with open("fake2.yaml", "w", encoding="utf-8") as f: - f.write(fake_yaml) - - -def build_fake_yaml3(): - fake_yaml = """ - model: - name: imagenet_qat - framework: pytorch - - quantization: - approach: quant_aware_training - train: - start_epoch: 0 - end_epoch: 3 - iteration: 10 - dataloader: - batch_size: 1 - dataset: - dummy: - shape: [16, 3, 224, 224] - label: True - optimizer: - SGD: - learning_rate: 0.1 - momentum: 0.1 - nesterov: True - weight_decay: 0.1 - criterion: - CrossEntropyLoss: - reduction: sum - evaluation: - accuracy: - metric: - topk: 1 - tuning: - accuracy_criterion: - relative: 0.01 - exit_policy: - timeout: 0 - random_seed: 9527 - """ - with open("fake3.yaml", "w", encoding="utf-8") as f: - f.write(fake_yaml) - - -def build_fake_yaml4(): - fake_yaml = """ - model: - name: imagenet_prune - framework: pytorch_fx - - pruning: - train: - start_epoch: 0 - end_epoch: 3 - iteration: 10 - dataloader: - batch_size: 1 - dataset: - dummy: - shape: [16, 3, 224, 224] - label: True - optimizer: - SGD: - learning_rate: 0.1 - momentum: 0.1 - nesterov: True - weight_decay: 0.1 - criterion: - CrossEntropyLoss: - reduction: sum - approach: - weight_compression: - initial_sparsity: 0.0 - target_sparsity: 0.97 - start_epoch: 0 - end_epoch: 3 - pruners: - - !Pruner - start_epoch: 1 - end_epoch: 3 - prune_type: basic_magnitude - names: ['layer1.0.conv1.weight'] - - - !Pruner - start_epoch: 0 - end_epoch: 3 - target_sparsity: 0.6 - prune_type: basic_magnitude - update_frequency: 2 - names: ['layer1.0.conv2.weight'] - - evaluation: - accuracy: - metric: - topk: 1 - dataloader: - batch_size: 1 - dataset: - dummy: - shape: [16, 3, 224, 224] - label: True - """ - with open("fake4.yaml", "w", encoding="utf-8") as f: - f.write(fake_yaml) - - -def build_fake_yaml5(): - fake_yaml = """ - model: - name: imagenet_qat - framework: pytorch_fx - - quantization: - approach: quant_aware_training - train: - start_epoch: 0 - end_epoch: 3 - iteration: 10 - dataloader: - batch_size: 1 - dataset: - dummy: - shape: [16, 3, 224, 224] - label: True - optimizer: - SGD: - learning_rate: 0.1 - momentum: 0.1 - nesterov: True - weight_decay: 0.1 - criterion: - CrossEntropyLoss: - reduction: sum - evaluation: - accuracy: - metric: - topk: 1 - tuning: - accuracy_criterion: - relative: 0.01 - exit_policy: - timeout: 0 - random_seed: 9527 - """ - with open("fake5.yaml", "w", encoding="utf-8") as f: - f.write(fake_yaml) - - -def build_fake_yaml6(): - fake_yaml = """ - model: - name: imagenet_distillation - framework: pytorch - - distillation: - train: - start_epoch: 0 - end_epoch: 3 - iteration: 10 - frequency: 1 - optimizer: - SGD: - learning_rate: 0.001 - momentum: 0.1 - nesterov: True - weight_decay: 0.001 - criterion: - KnowledgeDistillationLoss: - temperature: 1.0 - loss_types: ['CE', 'KL'] - loss_weights: [0.5, 0.5] - dataloader: - batch_size: 1 - dataset: - dummy: - shape: [16, 3, 224, 224] - label: True - evaluation: - accuracy: - metric: - topk: 1 - dataloader: - batch_size: 1 - dataset: - dummy: - shape: [16, 3, 224, 224] - label: True - """ - with open("fake6.yaml", "w", encoding="utf-8") as f: - f.write(fake_yaml) - - -class TestPruning(unittest.TestCase): - model = torchvision.models.resnet18() - q_model = torchvision.models.quantization.resnet18() - q_model_teacher = torchvision.models.quantization.resnet50() - - @classmethod - def setUpClass(cls): - build_fake_yaml() - build_fake_yaml2() - build_fake_yaml3() - build_fake_yaml4() - build_fake_yaml5() - build_fake_yaml6() - - @classmethod - def tearDownClass(cls): - os.remove("fake.yaml") - os.remove("fake2.yaml") - os.remove("fake3.yaml") - os.remove("fake4.yaml") - os.remove("fake5.yaml") - os.remove("fake6.yaml") - shutil.rmtree("./saved", ignore_errors=True) - shutil.rmtree("runs", ignore_errors=True) - shutil.rmtree("nc_workspace", ignore_errors=True) - - def test_pruning(self): - from neural_compressor.experimental import Pruning, common - - prune = Pruning("fake.yaml") - scheduler = Scheduler() - scheduler.model = self.model - datasets = Datasets("pytorch") - dummy_dataset = datasets["dummy"](shape=(16, 3, 224, 224), low=0.0, high=1.0, label=True) - dummy_dataloader = PyTorchDataLoader(dummy_dataset) - - def training_func_for_nc(model): - epochs = 2 - iters = 2 - criterion = nn.CrossEntropyLoss() - optimizer = torch.optim.SGD(model.parameters(), lr=0.0001) - for nepoch in range(epochs): - model.train() - cnt = 0 - prune.on_epoch_begin(nepoch) - for image, target in dummy_dataloader: - prune.on_step_begin(cnt) - print(".", end="") - cnt += 1 - output = model(image) - loss = criterion(output, target) - optimizer.zero_grad() - loss.backward() - optimizer.step() - prune.on_step_end() - if cnt >= iters: - break - prune.on_epoch_end() - - prune.pruning_func = training_func_for_nc - prune.eval_dataloader = dummy_dataloader - prune.train_dataloader = dummy_dataloader - scheduler.append(prune) - opt_model = scheduler.fit() - - def test_pure_yaml_pruning(self): - from neural_compressor.experimental import Pruning, common - - prune = Pruning("fake2.yaml") - scheduler = Scheduler() - scheduler.model = self.model - scheduler.append(prune) - opt_model = scheduler.fit() - opt_model.report_sparsity() - try: - conv_weight = opt_model.model.layer1[0].conv1.weight.dequantize() - except: - conv_weight = opt_model.model.layer1[0].conv1.weight - self.assertAlmostEqual((conv_weight == 0).sum().item() / conv_weight.numel(), 0.64, delta=0.05) - - def test_scheduler_qat_distillation(self): - from neural_compressor.experimental import Distillation, Quantization, common - - self.q_model = torchvision.models.quantization.resnet18() - self.q_model.fuse_model() - quantizer = Quantization("./fake3.yaml") - distiller = Distillation("./fake6.yaml") - scheduler = Scheduler() - scheduler.model = self.q_model - distiller.teacher_model = self.q_model_teacher - scheduler.append(distiller) - scheduler.append(quantizer) - opt_model = scheduler.fit() - opt_model.report_sparsity() - try: - conv_weight = opt_model.model.layer1[0].conv1.weight().dequantize() - except: - conv_weight = opt_model.model.layer1[0].conv1.weight - self.assertAlmostEqual((conv_weight == 0).sum().item() / conv_weight.numel(), 0.01, delta=0.01) - - def test_combine_qat_pruning(self): - from neural_compressor.experimental import Pruning, Quantization, common - - self.q_model = torchvision.models.quantization.resnet18() - self.q_model.fuse_model() - quantizer = Quantization("./fake3.yaml") - prune = Pruning("./fake2.yaml") - scheduler = Scheduler() - scheduler.model = self.q_model - combination = scheduler.combine(prune, quantizer) - scheduler.append(combination) - opt_model = scheduler.fit() - opt_model.report_sparsity() - try: - conv_weight = opt_model.model.layer1[0].conv1.weight().dequantize() - except: - conv_weight = opt_model.model.layer1[0].conv1.weight - self.assertAlmostEqual((conv_weight == 0).sum().item() / conv_weight.numel(), 0.64, delta=0.05) - self.assertEqual(combination.__repr__().lower(), "combination of pruning,quantization") - - def test_combine_qat_distillation(self): - from neural_compressor.experimental import Distillation, Quantization, common - - self.q_model.fuse_model() - quantizer = Quantization("./fake3.yaml") - distiller = Distillation("./fake6.yaml") - scheduler = Scheduler() - scheduler.model = self.q_model - distiller.teacher_model = self.q_model_teacher - combination = scheduler.combine(distiller, quantizer) - scheduler.append(combination) - opt_model = scheduler.fit() - opt_model.report_sparsity() - try: - conv_weight = opt_model.model.layer1[0].conv1.weight().dequantize() - except: - conv_weight = opt_model.model.layer1[0].conv1.weight - self.assertAlmostEqual((conv_weight == 0).sum().item() / conv_weight.numel(), 0.01, delta=0.01) - self.assertEqual(combination.__repr__().lower(), "combination of distillation,quantization") - - @unittest.skipIf( - PT_VERSION < Version("1.9.0-rc1"), - "Please use PyTroch 1.9 or higher version for Quantization & Pruning with pytorch_fx backend", - ) - def test_combine_fx(self): - from neural_compressor.experimental import Pruning, Quantization, common - - quantizer = Quantization("./fake5.yaml") - prune = Pruning("./fake4.yaml") - scheduler = Scheduler() - scheduler.model = self.model - combination = scheduler.combine(prune, quantizer) - scheduler.append(combination) - opt_model = scheduler.fit() - opt_model.report_sparsity() - try: - conv_weight = dict(opt_model.model.layer1.named_modules())["0"].conv1.weight().dequantize() - except: - conv_weight = dict(opt_model.model.layer1.named_modules())["0"].conv1.weight - self.assertAlmostEqual((conv_weight == 0).sum().item() / conv_weight.numel(), 0.64, delta=0.05) - self.assertEqual(combination.__repr__().lower(), "combination of pruning,quantization") - - -if __name__ == "__main__": - unittest.main() diff --git a/test/strategy/test_basic_1x.py b/test/strategy/test_basic_1x.py deleted file mode 100644 index f327c55aad2..00000000000 --- a/test/strategy/test_basic_1x.py +++ /dev/null @@ -1,282 +0,0 @@ -"""Tests for quantization.""" - -import os -import shutil -import unittest - -import numpy as np -import yaml - - -def build_fake_yaml(): - fake_yaml = """ - model: - name: fake_yaml - framework: tensorflow - inputs: x - outputs: op2_to_store - device: cpu - evaluation: - accuracy: - metric: - topk: 1 - tuning: - strategy: - name: basic - accuracy_criterion: - relative: 0.01 - workspace: - path: saved - """ - y = yaml.load(fake_yaml, Loader=yaml.SafeLoader) - with open("fake_yaml.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - f.close() - - -def build_fake_yaml_recipe(): - fake_yaml = """ - model: - name: fake_yaml - framework: tensorflow - inputs: x - outputs: op2_to_store - device: cpu - evaluation: - accuracy: - metric: - topk: 1 - quantization: - approach: - post_training_auto_quant - tuning: - strategy: - name: basic - exit_policy: - max_trials: 10 - accuracy_criterion: - absolute: -1 - workspace: - path: saved - """ - y = yaml.load(fake_yaml, Loader=yaml.SafeLoader) - with open("fake_yaml_recipe.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - f.close() - - -def build_fake_yaml2(): - fake_yaml = """ - model: - name: fake_yaml - framework: tensorflow - inputs: x - outputs: op2_to_store - device: cpu - evaluation: - accuracy: - metric: - topk: 1 - tuning: - strategy: - name: basic - exit_policy: - max_trials: 3 - accuracy_criterion: - relative: -0.01 - workspace: - path: saved - """ - y = yaml.load(fake_yaml, Loader=yaml.SafeLoader) - with open("fake_yaml2.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - f.close() - - -def build_fake_yaml3(): - fake_yaml = """ - model: - name: fake_yaml - framework: tensorflow - inputs: x - outputs: op2_to_store - device: cpu - evaluation: - accuracy: - multi_metrics: - topk: 1 - MSE: - compare_label: False - tuning: - strategy: - name: basic - exit_policy: - max_trials: 3 - timeout: 50 - accuracy_criterion: - relative: -0.01 - workspace: - path: saved - """ - y = yaml.load(fake_yaml, Loader=yaml.SafeLoader) - with open("fake_yaml3.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - f.close() - - -def build_fake_yaml4(): - fake_yaml = """ - model: - name: fake_yaml - framework: tensorflow - inputs: x - outputs: op2_to_store - device: cpu - evaluation: - accuracy: - multi_metrics: - topk: 1 - MSE: - compare_label: False - weight: [1, 0] - tuning: - strategy: - name: basic - exit_policy: - max_trials: 3 - timeout: 50 - accuracy_criterion: - relative: -0.01 - workspace: - path: saved - """ - y = yaml.load(fake_yaml, Loader=yaml.SafeLoader) - with open("fake_yaml4.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - f.close() - - -def build_fake_model(): - import tensorflow as tf - - try: - graph = tf.Graph() - graph_def = tf.compat.v1.GraphDef() - with tf.compat.v1.Session() as sess: - x = tf.compat.v1.placeholder(tf.float32, shape=(1, 3, 3, 1), name="x") - y = tf.constant(np.random.random((2, 2, 1, 1)).astype(np.float32), name="y") - z = tf.constant(np.random.random((1, 1, 1, 1)).astype(np.float32), name="z") - op = tf.nn.conv2d(input=x, filters=y, strides=[1, 1, 1, 1], padding="VALID", name="op_to_store") - op2 = tf.nn.conv2d( - input=op, - filters=z, - strides=[1, 1, 1, 1], - padding="VALID", - ) - last_identity = tf.identity(op2, name="op2_to_store") - sess.run(tf.compat.v1.global_variables_initializer()) - constant_graph = tf.compat.v1.graph_util.convert_variables_to_constants( - sess, sess.graph_def, ["op2_to_store"] - ) - - graph_def.ParseFromString(constant_graph.SerializeToString()) - with graph.as_default(): - tf.import_graph_def(graph_def, name="") - except: - graph = tf.Graph() - graph_def = tf.compat.v1.GraphDef() - with tf.compat.v1.Session() as sess: - x = tf.compat.v1.placeholder(tf.float32, shape=(1, 3, 3, 1), name="x") - y = tf.constant(np.random.random((2, 2, 1, 1)).astype(np.float32), name="y") - z = tf.constant(np.random.random((1, 1, 1, 1)).astype(np.float32), name="z") - op = tf.nn.conv2d(input=x, filters=y, strides=[1, 1, 1, 1], padding="VALID", name="op_to_store") - op2 = tf.nn.conv2d(input=op, filters=z, strides=[1, 1, 1, 1], padding="VALID") - last_identity = tf.identity(op2, name="op2_to_store") - - sess.run(tf.compat.v1.global_variables_initializer()) - constant_graph = tf.compat.v1.graph_util.convert_variables_to_constants( - sess, sess.graph_def, ["op2_to_store"] - ) - - graph_def.ParseFromString(constant_graph.SerializeToString()) - with graph.as_default(): - tf.import_graph_def(graph_def, name="") - return graph - - -class TestBasicTuningStrategy(unittest.TestCase): - @classmethod - def setUpClass(self): - self.constant_graph = build_fake_model() - build_fake_yaml() - build_fake_yaml2() - build_fake_yaml3() - build_fake_yaml4() - build_fake_yaml_recipe() - - @classmethod - def tearDownClass(self): - os.remove("fake_yaml.yaml") - os.remove("fake_yaml2.yaml") - os.remove("fake_yaml3.yaml") - os.remove("fake_yaml4.yaml") - os.remove("fake_yaml_recipe.yaml") - shutil.rmtree("saved", ignore_errors=True) - - def test_run_basic_one_trial(self): - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", (100, 3, 3, 1), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.model = self.constant_graph - quantizer.fit() - - # resume tuning history - quantizer.conf.usr_cfg.tuning.workspace.resume = "saved/history.snapshot" - quantizer.fit() - - def test_run_basic_max_trials(self): - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml2.yaml") - dataset = quantizer.dataset("dummy", (100, 3, 3, 1), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.model = self.constant_graph - quantizer.fit() - - def test_run_basic_recipe(self): - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml_recipe.yaml") - dataset = quantizer.dataset("dummy", (100, 3, 3, 1), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.model = self.constant_graph - quantizer.fit() - - def test_run_basic_max_trials_multimetric(self): - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml3.yaml") - dataset = quantizer.dataset("dummy", (100, 3, 3, 1), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.model = self.constant_graph - quantizer.fit() - - def test_run_basic_max_trials_multimetric_weight(self): - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml4.yaml") - dataset = quantizer.dataset("dummy", (100, 3, 3, 1), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.model = self.constant_graph - quantizer.fit() - - -if __name__ == "__main__": - unittest.main() diff --git a/test/strategy/test_bayesian_1x.py b/test/strategy/test_bayesian_1x.py deleted file mode 100644 index 51f25e45dc1..00000000000 --- a/test/strategy/test_bayesian_1x.py +++ /dev/null @@ -1,340 +0,0 @@ -"""Tests for quantization.""" - -import os -import shutil -import unittest - -import numpy as np -import yaml - - -def build_fake_yaml(): - fake_yaml = """ - model: - name: fake_yaml - framework: tensorflow - inputs: x - outputs: op_to_store - device: cpu - quantization: - calibration: - sampling_size: 10 - evaluation: - accuracy: - metric: - topk: 1 - tuning: - strategy: - name: bayesian - exit_policy: - max_trials: 1 - accuracy_criterion: - relative: 0.01 - workspace: - path: saved - """ - y = yaml.load(fake_yaml, Loader=yaml.SafeLoader) - with open("fake_yaml.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - f.close() - - -def build_fake_yaml2(): - fake_yaml = """ - model: - name: fake_yaml - framework: tensorflow - inputs: input - outputs: final - device: cpu - quantization: - calibration: - sampling_size: 10, 20 - op_wise: { - \"conv1\": { - \"activation\": {\"dtype\": [\"fp32\"]}, - }, - } - evaluation: - accuracy: - metric: - topk: 1 - tuning: - strategy: - name: bayesian - exit_policy: - max_trials: 3 - accuracy_criterion: - relative: 0.01 - workspace: - path: saved - """ - with open("fake_yaml2.yaml", "w", encoding="utf-8") as f: - f.write(fake_yaml) - f.close() - - -def build_fake_model(): - import tensorflow as tf - - try: - graph = tf.Graph() - graph_def = tf.GraphDef() - with tf.Session() as sess: - x = tf.placeholder(tf.float64, shape=(1, 3, 3, 1), name="x") - y = tf.constant(np.random.random((2, 2, 1, 1)), name="y") - op = tf.nn.conv2d(input=x, filter=y, strides=[1, 1, 1, 1], padding="VALID", name="op_to_store") - - sess.run(tf.global_variables_initializer()) - constant_graph = tf.graph_util.convert_variables_to_constants(sess, sess.graph_def, ["op_to_store"]) - - graph_def.ParseFromString(constant_graph.SerializeToString()) - with graph.as_default(): - tf.import_graph_def(graph_def, name="") - except: - graph = tf.Graph() - graph_def = tf.compat.v1.GraphDef() - with tf.compat.v1.Session() as sess: - x = tf.compat.v1.placeholder(tf.float64, shape=(1, 3, 3, 1), name="x") - y = tf.compat.v1.constant(np.random.random((2, 2, 1, 1)), name="y") - op = tf.nn.conv2d(input=x, filters=y, strides=[1, 1, 1, 1], padding="VALID", name="op_to_store") - - sess.run(tf.compat.v1.global_variables_initializer()) - constant_graph = tf.compat.v1.graph_util.convert_variables_to_constants( - sess, sess.graph_def, ["op_to_store"] - ) - - graph_def.ParseFromString(constant_graph.SerializeToString()) - with graph.as_default(): - tf.import_graph_def(graph_def, name="") - return graph - - -def create_test_graph(): - from tensorflow.core.framework import attr_value_pb2, graph_pb2, node_def_pb2 - from tensorflow.python.framework import dtypes, tensor_util - - input_node = node_def_pb2.NodeDef() - input_node.name = "input" - input_node.op = "Placeholder" - input_node.attr["dtype"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - - conv1_weight_node = node_def_pb2.NodeDef() - conv1_weight_node.name = "conv1_weights" - conv1_weight_node.op = "Const" - conv1_weight_value = np.float32(np.abs(np.random.randn(3, 3, 3, 32))) - conv1_weight_node.attr["dtype"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - conv1_weight_node.attr["value"].CopyFrom( - attr_value_pb2.AttrValue( - tensor=tensor_util.make_tensor_proto( - conv1_weight_value, conv1_weight_value.dtype.type, conv1_weight_value.shape - ) - ) - ) - - conv1_node = node_def_pb2.NodeDef() - conv1_node.name = "conv1" - conv1_node.op = "Conv2D" - conv1_node.attr["T"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - conv1_node.input.extend([input_node.name, conv1_weight_node.name]) - conv1_node.attr["strides"].CopyFrom( - attr_value_pb2.AttrValue(list=attr_value_pb2.AttrValue.ListValue(i=[1, 1, 1, 1])) - ) - conv1_node.attr["dilations"].CopyFrom( - attr_value_pb2.AttrValue(list=attr_value_pb2.AttrValue.ListValue(i=[1, 1, 1, 1])) - ) - conv1_node.attr["padding"].CopyFrom(attr_value_pb2.AttrValue(s=b"SAME")) - conv1_node.attr["data_format"].CopyFrom(attr_value_pb2.AttrValue(s=b"NHWC")) - - bias_node = node_def_pb2.NodeDef() - bias_node.name = "conv1_bias" - bias_node.op = "Const" - bias_value = np.float32(np.abs(np.random.randn(32))) - bias_node.attr["dtype"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - bias_node.attr["value"].CopyFrom( - attr_value_pb2.AttrValue( - tensor=tensor_util.make_tensor_proto(bias_value, bias_value.dtype.type, bias_value.shape) - ) - ) - - bias_add_node = node_def_pb2.NodeDef() - bias_add_node.name = "conv1_bias_add" - bias_add_node.op = "BiasAdd" - bias_add_node.attr["T"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - bias_add_node.input.extend([conv1_node.name, bias_node.name]) - bias_add_node.attr["data_format"].CopyFrom(attr_value_pb2.AttrValue(s=b"NHWC")) - - relu_node = node_def_pb2.NodeDef() - relu_node.op = "Relu" - relu_node.name = "relu" - relu_node.attr["T"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - relu_node.input.extend([bias_add_node.name]) - - conv2_weight_node = node_def_pb2.NodeDef() - conv2_weight_node.name = "conv2_weights" - conv2_weight_node.op = "Const" - conv2_weight_value = np.float32(np.abs(np.random.randn(3, 3, 32, 32))) - conv2_weight_node.attr["dtype"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - conv2_weight_node.attr["value"].CopyFrom( - attr_value_pb2.AttrValue( - tensor=tensor_util.make_tensor_proto( - conv2_weight_value, conv2_weight_value.dtype.type, conv2_weight_value.shape - ) - ) - ) - - conv2_node = node_def_pb2.NodeDef() - conv2_node.name = "conv2" - conv2_node.op = "Conv2D" - conv2_node.attr["T"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - conv2_node.input.extend([relu_node.name, conv2_weight_node.name]) - conv2_node.attr["strides"].CopyFrom( - attr_value_pb2.AttrValue(list=attr_value_pb2.AttrValue.ListValue(i=[1, 1, 1, 1])) - ) - conv2_node.attr["dilations"].CopyFrom( - attr_value_pb2.AttrValue(list=attr_value_pb2.AttrValue.ListValue(i=[1, 1, 1, 1])) - ) - conv2_node.attr["padding"].CopyFrom(attr_value_pb2.AttrValue(s=b"SAME")) - conv2_node.attr["data_format"].CopyFrom(attr_value_pb2.AttrValue(s=b"NHWC")) - - bias_node2 = node_def_pb2.NodeDef() - bias_node2.name = "conv2_bias" - bias_node2.op = "Const" - bias_value2 = np.float32(np.abs(np.random.randn(32))) - bias_node2.attr["dtype"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - bias_node2.attr["value"].CopyFrom( - attr_value_pb2.AttrValue( - tensor=tensor_util.make_tensor_proto(bias_value2, bias_value2.dtype.type, bias_value2.shape) - ) - ) - - bias_add_node2 = node_def_pb2.NodeDef() - bias_add_node2.name = "conv2_bias_add" - bias_add_node2.op = "BiasAdd" - bias_add_node2.attr["T"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - bias_add_node2.input.extend([conv2_node.name, bias_node2.name]) - bias_add_node2.attr["data_format"].CopyFrom(attr_value_pb2.AttrValue(s=b"NHWC")) - - relu_node2 = node_def_pb2.NodeDef() - relu_node2.op = "Relu" - relu_node2.name = "relu2" - relu_node2.attr["T"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - relu_node2.input.extend([bias_add_node2.name]) - - conv3_weight_node = node_def_pb2.NodeDef() - conv3_weight_node.name = "conv3_weights" - conv3_weight_node.op = "Const" - conv3_weight_value = np.float32(np.abs(np.random.randn(3, 3, 32, 32))) - conv3_weight_node.attr["dtype"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - conv3_weight_node.attr["value"].CopyFrom( - attr_value_pb2.AttrValue( - tensor=tensor_util.make_tensor_proto( - conv3_weight_value, conv3_weight_value.dtype.type, conv3_weight_value.shape - ) - ) - ) - - conv3_node = node_def_pb2.NodeDef() - conv3_node.name = "conv3" - conv3_node.op = "Conv2D" - conv3_node.attr["T"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - conv3_node.input.extend([relu_node2.name, conv3_weight_node.name]) - conv3_node.attr["strides"].CopyFrom( - attr_value_pb2.AttrValue(list=attr_value_pb2.AttrValue.ListValue(i=[1, 1, 1, 1])) - ) - conv3_node.attr["dilations"].CopyFrom( - attr_value_pb2.AttrValue(list=attr_value_pb2.AttrValue.ListValue(i=[1, 1, 1, 1])) - ) - conv3_node.attr["padding"].CopyFrom(attr_value_pb2.AttrValue(s=b"SAME")) - conv3_node.attr["data_format"].CopyFrom(attr_value_pb2.AttrValue(s=b"NHWC")) - - identity_node = node_def_pb2.NodeDef() - identity_node.name = "final" - identity_node.op = "Identity" - identity_node.attr["T"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - identity_node.input.extend([conv3_node.name]) - - test_graph = graph_pb2.GraphDef() - - test_graph.node.extend( - [ - input_node, - conv1_weight_node, - conv1_node, - bias_node, - bias_add_node, - relu_node, - conv2_weight_node, - conv2_node, - bias_node2, - bias_add_node2, - relu_node2, - conv3_weight_node, - conv3_node, - identity_node, - ] - ) - return test_graph - - -def objective_func(params): - return params["x1"] ** 2 + params["x2"] - - -class TestQuantization(unittest.TestCase): - @classmethod - def setUpClass(self): - self.constant_graph = build_fake_model() - self.test_graph = create_test_graph() - build_fake_yaml() - build_fake_yaml2() - - @classmethod - def tearDownClass(self): - os.remove("fake_yaml.yaml") - os.remove("fake_yaml2.yaml") - - shutil.rmtree("saved", ignore_errors=True) - - def test_run_bayesian_one_trial(self): - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 3, 3, 1), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = self.constant_graph - output_graph = quantizer.fit() - self.assertNotEqual(output_graph, None) - - def test_run_bayesian_max_trials(self): - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml2.yaml") - dataset = quantizer.dataset("dummy", shape=(1, 224, 224, 3), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = self.test_graph - output_graph = quantizer.fit() - self.assertNotEqual(output_graph, None) - - def test_bayesian_opt_class(self): - from neural_compressor.experimental.strategy.bayesian import BayesianOptimization - - pbounds = {} - pbounds["x1"] = (0, 1) - pbounds["x2"] = (0, 1) - np.random.seed(9527) - bayes_opt = BayesianOptimization(pbounds=pbounds, random_seed=9527) - for i in range(10): - params = bayes_opt.gen_next_params() - try: - bayes_opt._space.register(params, objective_func(params)) - except KeyError: - pass - self.assertTrue(bayes_opt._space.max()["target"] == 2.0) - self.assertTrue(len(bayes_opt._space.res()) == 8) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/strategy/test_exhaustive_1x.py b/test/strategy/test_exhaustive_1x.py deleted file mode 100644 index 53ccf237be1..00000000000 --- a/test/strategy/test_exhaustive_1x.py +++ /dev/null @@ -1,137 +0,0 @@ -"""Tests for quantization.""" - -import os -import shutil -import unittest - -import numpy as np -import yaml - - -def build_fake_yaml(): - fake_yaml = """ - model: - name: fake_yaml - framework: tensorflow - inputs: x - outputs: op_to_store - device: cpu - evaluation: - accuracy: - metric: - topk: 1 - tuning: - strategy: - name: exhaustive - accuracy_criterion: - relative: 0.01 - workspace: - path: saved - """ - y = yaml.load(fake_yaml, Loader=yaml.SafeLoader) - with open("fake_yaml.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - f.close() - - -def build_fake_yaml2(): - fake_yaml = """ - model: - name: fake_yaml - framework: tensorflow - inputs: x - outputs: op_to_store - device: cpu - evaluation: - accuracy: - metric: - topk: 1 - tuning: - strategy: - name: exhaustive - exit_policy: - max_trials: 5 - accuracy_criterion: - relative: -0.01 - workspace: - path: saved - """ - y = yaml.load(fake_yaml, Loader=yaml.SafeLoader) - with open("fake_yaml2.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - f.close() - - -def build_fake_model(): - import tensorflow as tf - - try: - graph = tf.Graph() - graph_def = tf.GraphDef() - with tf.Session() as sess: - x = tf.placeholder(tf.float64, shape=(1, 3, 3, 1), name="x") - y = tf.constant(np.random.random((2, 2, 1, 1)), name="y") - op = tf.nn.conv2d(input=x, filter=y, strides=[1, 1, 1, 1], padding="VALID", name="op_to_store") - - sess.run(tf.global_variables_initializer()) - constant_graph = tf.graph_util.convert_variables_to_constants(sess, sess.graph_def, ["op_to_store"]) - - graph_def.ParseFromString(constant_graph.SerializeToString()) - with graph.as_default(): - tf.import_graph_def(graph_def, name="") - except: - graph = tf.Graph() - graph_def = tf.compat.v1.GraphDef() - with tf.compat.v1.Session() as sess: - x = tf.compat.v1.placeholder(tf.float64, shape=(1, 3, 3, 1), name="x") - y = tf.compat.v1.constant(np.random.random((2, 2, 1, 1)), name="y") - op = tf.nn.conv2d(input=x, filters=y, strides=[1, 1, 1, 1], padding="VALID", name="op_to_store") - - sess.run(tf.compat.v1.global_variables_initializer()) - constant_graph = tf.compat.v1.graph_util.convert_variables_to_constants( - sess, sess.graph_def, ["op_to_store"] - ) - - graph_def.ParseFromString(constant_graph.SerializeToString()) - with graph.as_default(): - tf.import_graph_def(graph_def, name="") - return graph - - -class TestQuantization(unittest.TestCase): - @classmethod - def setUpClass(self): - self.constant_graph = build_fake_model() - build_fake_yaml() - build_fake_yaml2() - - @classmethod - def tearDownClass(self): - os.remove("fake_yaml.yaml") - os.remove("fake_yaml2.yaml") - - shutil.rmtree("saved", ignore_errors=True) - - def test_ru_exhaustive_one_trial(self): - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", (100, 3, 3, 1), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = self.constant_graph - quantizer.fit() - - def test_ru_exhaustive_max_trials(self): - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml2.yaml") - dataset = quantizer.dataset("dummy", (100, 3, 3, 1), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = self.constant_graph - quantizer.fit() - - -if __name__ == "__main__": - unittest.main() diff --git a/test/strategy/test_mse_1x.py b/test/strategy/test_mse_1x.py deleted file mode 100644 index 09f8a34641e..00000000000 --- a/test/strategy/test_mse_1x.py +++ /dev/null @@ -1,323 +0,0 @@ -"""Tests for quantization.""" - -import os -import shutil -import unittest - -import numpy as np -import yaml - - -def build_fake_yaml(): - fake_yaml = """ - model: - name: fake_yaml - framework: tensorflow - inputs: x - outputs: op_to_store - device: cpu - quantization: - calibration: - sampling_size: 10 - evaluation: - accuracy: - metric: - topk: 1 - tuning: - strategy: - name: mse - exit_policy: - max_trials: 1 - accuracy_criterion: - relative: 0.01 - workspace: - path: saved - """ - y = yaml.load(fake_yaml, Loader=yaml.SafeLoader) - with open("fake_yaml.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - f.close() - - -def build_fake_yaml2(): - fake_yaml = """ - model: - name: fake_yaml - framework: tensorflow - inputs: input - outputs: final - device: cpu - quantization: - calibration: - sampling_size: 10, 20 - op_wise: { - \"conv1\": { - \"activation\": {\"dtype\": [\"fp32\"]}, - }, - } - evaluation: - accuracy: - metric: - topk: 1 - tuning: - strategy: - name: mse - exit_policy: - max_trials: 3 - accuracy_criterion: - relative: 0.01 - workspace: - path: saved - """ - with open("fake_yaml2.yaml", "w", encoding="utf-8") as f: - f.write(fake_yaml) - f.close() - - -def build_fake_model(): - import tensorflow as tf - - try: - graph = tf.Graph() - graph_def = tf.GraphDef() - with tf.Session() as sess: - x = tf.placeholder(tf.float64, shape=(1, 3, 3, 1), name="x") - y = tf.constant(np.random.random((2, 2, 1, 1)), name="y") - op = tf.nn.conv2d(input=x, filter=y, strides=[1, 1, 1, 1], padding="VALID", name="op_to_store") - - sess.run(tf.global_variables_initializer()) - constant_graph = tf.graph_util.convert_variables_to_constants(sess, sess.graph_def, ["op_to_store"]) - - graph_def.ParseFromString(constant_graph.SerializeToString()) - with graph.as_default(): - tf.import_graph_def(graph_def, name="") - except: - graph = tf.Graph() - graph_def = tf.compat.v1.GraphDef() - with tf.compat.v1.Session() as sess: - x = tf.compat.v1.placeholder(tf.float64, shape=(1, 3, 3, 1), name="x") - y = tf.compat.v1.constant(np.random.random((2, 2, 1, 1)), name="y") - op = tf.nn.conv2d(input=x, filters=y, strides=[1, 1, 1, 1], padding="VALID", name="op_to_store") - - sess.run(tf.compat.v1.global_variables_initializer()) - constant_graph = tf.compat.v1.graph_util.convert_variables_to_constants( - sess, sess.graph_def, ["op_to_store"] - ) - - graph_def.ParseFromString(constant_graph.SerializeToString()) - with graph.as_default(): - tf.import_graph_def(graph_def, name="") - return graph - - -def create_test_graph(): - from tensorflow.core.framework import attr_value_pb2, graph_pb2, node_def_pb2 - from tensorflow.python.framework import dtypes, tensor_util - - input_node = node_def_pb2.NodeDef() - input_node.name = "input" - input_node.op = "Placeholder" - input_node.attr["dtype"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - - conv1_weight_node = node_def_pb2.NodeDef() - conv1_weight_node.name = "conv1_weights" - conv1_weight_node.op = "Const" - conv1_weight_value = np.float32(np.abs(np.random.randn(3, 3, 3, 32))) - conv1_weight_node.attr["dtype"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - conv1_weight_node.attr["value"].CopyFrom( - attr_value_pb2.AttrValue( - tensor=tensor_util.make_tensor_proto( - conv1_weight_value, conv1_weight_value.dtype.type, conv1_weight_value.shape - ) - ) - ) - - conv1_node = node_def_pb2.NodeDef() - conv1_node.name = "conv1" - conv1_node.op = "Conv2D" - conv1_node.attr["T"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - conv1_node.input.extend([input_node.name, conv1_weight_node.name]) - conv1_node.attr["strides"].CopyFrom( - attr_value_pb2.AttrValue(list=attr_value_pb2.AttrValue.ListValue(i=[1, 1, 1, 1])) - ) - conv1_node.attr["dilations"].CopyFrom( - attr_value_pb2.AttrValue(list=attr_value_pb2.AttrValue.ListValue(i=[1, 1, 1, 1])) - ) - conv1_node.attr["padding"].CopyFrom(attr_value_pb2.AttrValue(s=b"SAME")) - conv1_node.attr["data_format"].CopyFrom(attr_value_pb2.AttrValue(s=b"NHWC")) - - bias_node = node_def_pb2.NodeDef() - bias_node.name = "conv1_bias" - bias_node.op = "Const" - bias_value = np.float32(np.abs(np.random.randn(32))) - bias_node.attr["dtype"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - bias_node.attr["value"].CopyFrom( - attr_value_pb2.AttrValue( - tensor=tensor_util.make_tensor_proto(bias_value, bias_value.dtype.type, bias_value.shape) - ) - ) - - bias_add_node = node_def_pb2.NodeDef() - bias_add_node.name = "conv1_bias_add" - bias_add_node.op = "BiasAdd" - bias_add_node.attr["T"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - bias_add_node.input.extend([conv1_node.name, bias_node.name]) - bias_add_node.attr["data_format"].CopyFrom(attr_value_pb2.AttrValue(s=b"NHWC")) - - relu_node = node_def_pb2.NodeDef() - relu_node.op = "Relu" - relu_node.name = "relu" - relu_node.attr["T"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - relu_node.input.extend([bias_add_node.name]) - - conv2_weight_node = node_def_pb2.NodeDef() - conv2_weight_node.name = "conv2_weights" - conv2_weight_node.op = "Const" - conv2_weight_value = np.float32(np.abs(np.random.randn(3, 3, 32, 32))) - conv2_weight_node.attr["dtype"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - conv2_weight_node.attr["value"].CopyFrom( - attr_value_pb2.AttrValue( - tensor=tensor_util.make_tensor_proto( - conv2_weight_value, conv2_weight_value.dtype.type, conv2_weight_value.shape - ) - ) - ) - - conv2_node = node_def_pb2.NodeDef() - conv2_node.name = "conv2" - conv2_node.op = "Conv2D" - conv2_node.attr["T"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - conv2_node.input.extend([relu_node.name, conv2_weight_node.name]) - conv2_node.attr["strides"].CopyFrom( - attr_value_pb2.AttrValue(list=attr_value_pb2.AttrValue.ListValue(i=[1, 1, 1, 1])) - ) - conv2_node.attr["dilations"].CopyFrom( - attr_value_pb2.AttrValue(list=attr_value_pb2.AttrValue.ListValue(i=[1, 1, 1, 1])) - ) - conv2_node.attr["padding"].CopyFrom(attr_value_pb2.AttrValue(s=b"SAME")) - conv2_node.attr["data_format"].CopyFrom(attr_value_pb2.AttrValue(s=b"NHWC")) - - bias_node2 = node_def_pb2.NodeDef() - bias_node2.name = "conv2_bias" - bias_node2.op = "Const" - bias_value2 = np.float32(np.abs(np.random.randn(32))) - bias_node2.attr["dtype"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - bias_node2.attr["value"].CopyFrom( - attr_value_pb2.AttrValue( - tensor=tensor_util.make_tensor_proto(bias_value2, bias_value2.dtype.type, bias_value2.shape) - ) - ) - - bias_add_node2 = node_def_pb2.NodeDef() - bias_add_node2.name = "conv2_bias_add" - bias_add_node2.op = "BiasAdd" - bias_add_node2.attr["T"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - bias_add_node2.input.extend([conv2_node.name, bias_node2.name]) - bias_add_node2.attr["data_format"].CopyFrom(attr_value_pb2.AttrValue(s=b"NHWC")) - - relu_node2 = node_def_pb2.NodeDef() - relu_node2.op = "Relu" - relu_node2.name = "relu2" - relu_node2.attr["T"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - relu_node2.input.extend([bias_add_node2.name]) - - conv3_weight_node = node_def_pb2.NodeDef() - conv3_weight_node.name = "conv3_weights" - conv3_weight_node.op = "Const" - conv3_weight_value = np.float32(np.abs(np.random.randn(3, 3, 32, 32))) - conv3_weight_node.attr["dtype"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - conv3_weight_node.attr["value"].CopyFrom( - attr_value_pb2.AttrValue( - tensor=tensor_util.make_tensor_proto( - conv3_weight_value, conv3_weight_value.dtype.type, conv3_weight_value.shape - ) - ) - ) - - conv3_node = node_def_pb2.NodeDef() - conv3_node.name = "conv3" - conv3_node.op = "Conv2D" - conv3_node.attr["T"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - conv3_node.input.extend([relu_node2.name, conv3_weight_node.name]) - conv3_node.attr["strides"].CopyFrom( - attr_value_pb2.AttrValue(list=attr_value_pb2.AttrValue.ListValue(i=[1, 1, 1, 1])) - ) - conv3_node.attr["dilations"].CopyFrom( - attr_value_pb2.AttrValue(list=attr_value_pb2.AttrValue.ListValue(i=[1, 1, 1, 1])) - ) - conv3_node.attr["padding"].CopyFrom(attr_value_pb2.AttrValue(s=b"SAME")) - conv3_node.attr["data_format"].CopyFrom(attr_value_pb2.AttrValue(s=b"NHWC")) - - identity_node = node_def_pb2.NodeDef() - identity_node.name = "final" - identity_node.op = "Identity" - identity_node.attr["T"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - identity_node.input.extend([conv3_node.name]) - - test_graph = graph_pb2.GraphDef() - - test_graph.node.extend( - [ - input_node, - conv1_weight_node, - conv1_node, - bias_node, - bias_add_node, - relu_node, - conv2_weight_node, - conv2_node, - bias_node2, - bias_add_node2, - relu_node2, - conv3_weight_node, - conv3_node, - identity_node, - ] - ) - return test_graph - - -def objective_func(params): - return params["x1"] ** 2 + params["x2"] - - -class TestQuantization(unittest.TestCase): - @classmethod - def setUpClass(self): - self.constant_graph = build_fake_model() - self.test_graph = create_test_graph() - build_fake_yaml() - build_fake_yaml2() - - @classmethod - def tearDownClass(self): - os.remove("fake_yaml.yaml") - os.remove("fake_yaml2.yaml") - - shutil.rmtree("saved", ignore_errors=True) - - def test_run_mse_one_trial(self): - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 3, 3, 1), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = self.constant_graph - output_graph = quantizer.fit() - self.assertNotEqual(output_graph, None) - - def test_run_mse_max_trials(self): - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml2.yaml") - dataset = quantizer.dataset("dummy", shape=(1, 224, 224, 3), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = self.test_graph - output_graph = quantizer.fit() - self.assertNotEqual(output_graph, None) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/strategy/test_mse_v2.py b/test/strategy/test_mse_v2.py deleted file mode 100644 index dd5d1bc2c82..00000000000 --- a/test/strategy/test_mse_v2.py +++ /dev/null @@ -1,171 +0,0 @@ -import copy -import os -import shutil -import unittest - -import numpy as np -import tensorflow as tf -import torchvision - -from neural_compressor.experimental import Quantization, common - - -def build_mse_yaml_tf(): - mse_yaml = """ - model: - name: fake_yaml - framework: tensorflow - inputs: x - outputs: op2_to_store - device: cpu - evaluation: - accuracy: - metric: - topk: 1 - tuning: - strategy: - name: mse_v2 - accuracy_criterion: - relative: 0.01 - exit_policy: - max_trials: 10 - timeout: 3600 - random_seed: 9527 - """ - with open("mse_yaml_tf.yaml", "w", encoding="utf-8") as f: - f.write(mse_yaml) - - -def build_mse_yaml_pytorch(): - mse_yaml = """ - model: - name: resnet18 - framework: pytorch_fx - - tuning: - strategy: - name: mse_v2 - accuracy_criterion: - relative: 0.01 - exit_policy: - timeout: 0 - """ - with open("mse_yaml_pytorch.yaml", "w", encoding="utf-8") as f: - f.write(mse_yaml) - - -def build_fake_model(): - try: - graph = tf.Graph() - graph_def = tf.compat.v1.GraphDef() - with tf.compat.v1.Session() as sess: - x = tf.compat.v1.placeholder(tf.float32, shape=(1, 3, 3, 1), name="x") - y = tf.constant(np.random.random((2, 2, 1, 1)).astype(np.float32), name="y") - z = tf.constant(np.random.random((1, 1, 1, 1)).astype(np.float32), name="z") - op = tf.nn.conv2d(input=x, filters=y, strides=[1, 1, 1, 1], padding="VALID", name="op_to_store") - op2 = tf.nn.conv2d( - input=op, - filters=z, - strides=[1, 1, 1, 1], - padding="VALID", - ) - last_identity = tf.identity(op2, name="op2_to_store") - sess.run(tf.compat.v1.global_variables_initializer()) - constant_graph = tf.compat.v1.graph_util.convert_variables_to_constants( - sess, sess.graph_def, ["op2_to_store"] - ) - - graph_def.ParseFromString(constant_graph.SerializeToString()) - with graph.as_default(): - tf.import_graph_def(graph_def, name="") - except: - graph = tf.Graph() - graph_def = tf.compat.v1.GraphDef() - with tf.compat.v1.Session() as sess: - x = tf.compat.v1.placeholder(tf.float32, shape=(1, 3, 3, 1), name="x") - y = tf.constant(np.random.random((2, 2, 1, 1)).astype(np.float32), name="y") - z = tf.constant(np.random.random((1, 1, 1, 1)).astype(np.float32), name="z") - op = tf.nn.conv2d(input=x, filters=y, strides=[1, 1, 1, 1], padding="VALID", name="op_to_store") - op2 = tf.nn.conv2d(input=op, filters=z, strides=[1, 1, 1, 1], padding="VALID") - last_identity = tf.identity(op2, name="op2_to_store") - - sess.run(tf.compat.v1.global_variables_initializer()) - constant_graph = tf.compat.v1.graph_util.convert_variables_to_constants( - sess, sess.graph_def, ["op2_to_store"] - ) - - graph_def.ParseFromString(constant_graph.SerializeToString()) - with graph.as_default(): - tf.import_graph_def(graph_def, name="") - return graph - - -class Test_MSEV2Strategy_Tensorflow(unittest.TestCase): - @classmethod - def setUpClass(self): - build_mse_yaml_tf() - self.model = build_fake_model() - - @classmethod - def tearDownClass(self): - os.remove("mse_yaml_tf.yaml") - shutil.rmtree("./saved", ignore_errors=True) - shutil.rmtree("runs", ignore_errors=True) - shutil.rmtree("nc_workspace", ignore_errors=True) - - def test_quantization_saved(self): - i = [0] # use a mutable type (list) to wrap the int object - - def fake_eval_func(_): - # 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 - eval_list = [0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1] - i[0] += 1 - return eval_list[i[0]] - - quantizer = Quantization("mse_yaml_tf.yaml") - - quantizer.model = self.model - dataset = quantizer.dataset("dummy", (100, 3, 3, 1), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.eval_func = fake_eval_func - q_model = quantizer.fit() - self.assertIsNotNone(q_model) - q_model.save("./saved") - - -class Test_MSEV2Strategy_PyTorch(unittest.TestCase): - @classmethod - def setUpClass(self): - build_mse_yaml_pytorch() - self.model = torchvision.models.resnet18() - - @classmethod - def tearDownClass(self): - os.remove("mse_yaml_pytorch.yaml") - shutil.rmtree("./saved", ignore_errors=True) - shutil.rmtree("runs", ignore_errors=True) - shutil.rmtree("nc_workspace", ignore_errors=True) - - def test_quantization_saved(self): - i = [0] - - def fake_eval_func(model): - acc_lst = [1, 1, 0, 0, 0, 0, 1, 1.1, 1.5, 1.1] - - i[0] += 1 - return acc_lst[i[0]] - - model = copy.deepcopy(self.model) - quantizer = Quantization("mse_yaml_pytorch.yaml") - dataset = quantizer.dataset("dummy", (1, 3, 224, 224)) - quantizer.model = model - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.eval_func = fake_eval_func - q_model = quantizer.fit() - self.assertIsNotNone(q_model) - q_model.save("./saved") - - -if __name__ == "__main__": - unittest.main() diff --git a/test/strategy/test_random_1x.py b/test/strategy/test_random_1x.py deleted file mode 100644 index 9f4d7f4fa95..00000000000 --- a/test/strategy/test_random_1x.py +++ /dev/null @@ -1,137 +0,0 @@ -"""Tests for quantization.""" - -import os -import shutil -import unittest - -import numpy as np -import yaml - - -def build_fake_yaml(): - fake_yaml = """ - model: - name: fake_yaml - framework: tensorflow - inputs: x - outputs: op_to_store - device: cpu - evaluation: - accuracy: - metric: - topk: 1 - tuning: - strategy: - name: random - accuracy_criterion: - relative: 0.01 - workspace: - path: saved - """ - y = yaml.load(fake_yaml, Loader=yaml.SafeLoader) - with open("fake_yaml.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - f.close() - - -def build_fake_yaml2(): - fake_yaml = """ - model: - name: fake_yaml - framework: tensorflow - inputs: x - outputs: op_to_store - device: cpu - evaluation: - accuracy: - metric: - topk: 1 - tuning: - strategy: - name: random - exit_policy: - max_trials: 3 - accuracy_criterion: - relative: -0.01 - workspace: - path: saved - """ - y = yaml.load(fake_yaml, Loader=yaml.SafeLoader) - with open("fake_yaml2.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - f.close() - - -def build_fake_model(): - import tensorflow as tf - - try: - graph = tf.Graph() - graph_def = tf.GraphDef() - with tf.Session() as sess: - x = tf.placeholder(tf.float64, shape=(1, 3, 3, 1), name="x") - y = tf.constant(np.random.random((2, 2, 1, 1)), name="y") - op = tf.nn.conv2d(input=x, filter=y, strides=[1, 1, 1, 1], padding="VALID", name="op_to_store") - - sess.run(tf.global_variables_initializer()) - constant_graph = tf.graph_util.convert_variables_to_constants(sess, sess.graph_def, ["op_to_store"]) - - graph_def.ParseFromString(constant_graph.SerializeToString()) - with graph.as_default(): - tf.import_graph_def(graph_def, name="") - except: - graph = tf.Graph() - graph_def = tf.compat.v1.GraphDef() - with tf.compat.v1.Session() as sess: - x = tf.compat.v1.placeholder(tf.float64, shape=(1, 3, 3, 1), name="x") - y = tf.compat.v1.constant(np.random.random((2, 2, 1, 1)), name="y") - op = tf.nn.conv2d(input=x, filters=y, strides=[1, 1, 1, 1], padding="VALID", name="op_to_store") - - sess.run(tf.compat.v1.global_variables_initializer()) - constant_graph = tf.compat.v1.graph_util.convert_variables_to_constants( - sess, sess.graph_def, ["op_to_store"] - ) - - graph_def.ParseFromString(constant_graph.SerializeToString()) - with graph.as_default(): - tf.import_graph_def(graph_def, name="") - return graph - - -class TestQuantization(unittest.TestCase): - @classmethod - def setUpClass(self): - self.constant_graph = build_fake_model() - build_fake_yaml() - build_fake_yaml2() - - @classmethod - def tearDownClass(self): - os.remove("fake_yaml.yaml") - os.remove("fake_yaml2.yaml") - - shutil.rmtree("saved", ignore_errors=True) - - def test_ru_random_one_trial(self): - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", (100, 3, 3, 1), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.model = self.constant_graph - quantizer.fit() - - def test_ru_random_max_trials(self): - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml2.yaml") - dataset = quantizer.dataset("dummy", (100, 3, 3, 1), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.model = self.constant_graph - quantizer.fit() - - -if __name__ == "__main__": - unittest.main() diff --git a/test/strategy/test_sigopt_1x.py b/test/strategy/test_sigopt_1x.py deleted file mode 100644 index 964add7d80a..00000000000 --- a/test/strategy/test_sigopt_1x.py +++ /dev/null @@ -1,159 +0,0 @@ -"""Tests for quantization.""" - -import os -import shutil -import unittest - -import numpy as np -import yaml - -CONDITION = False - - -def build_fake_yaml(sigopt_api_token, sigopt_project_id): - fake_yaml = """ - model: - name: fake_yaml - framework: tensorflow - inputs: x - outputs: op2_to_store - device: cpu - evaluation: - accuracy: - metric: - topk: 1 - tuning: - strategy: - name: sigopt - sigopt_api_token: {} - sigopt_project_id: {} - sigopt_experiment_name: nc-tune - accuracy_criterion: - relative: 0.01 - workspace: - path: saved - """.format( - sigopt_api_token, sigopt_project_id - ) - y = yaml.load(fake_yaml, Loader=yaml.SafeLoader) - with open("fake_yaml.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - f.close() - - -def build_fake_yaml2(sigopt_api_token, sigopt_project_id): - fake_yaml = """ - model: - name: fake_yaml - framework: tensorflow - inputs: x - outputs: op2_to_store - device: cpu - evaluation: - accuracy: - metric: - topk: 1 - tuning: - strategy: - name: sigopt - sigopt_api_token: {} - sigopt_project_id: {} - sigopt_experiment_name: nc-tune - exit_policy: - max_trials: 3 - accuracy_criterion: - relative: -0.01 - workspace: - path: saved - """.format( - sigopt_api_token, sigopt_project_id - ) - y = yaml.load(fake_yaml, Loader=yaml.SafeLoader) - with open("fake_yaml2.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - f.close() - - -def build_fake_model(): - import tensorflow as tf - - try: - graph = tf.Graph() - graph_def = tf.compat.v1.GraphDef() - with tf.compat.v1.Session() as sess: - x = tf.compat.v1.placeholder(tf.float32, shape=(1, 3, 3, 1), name="x") - y = tf.constant(np.random.random((2, 2, 1, 1)).astype(np.float32), name="y") - z = tf.constant(np.random.random((1, 1, 1, 1)).astype(np.float32), name="z") - op = tf.nn.conv2d(input=tf.nn.relu(x), filters=y, strides=[1, 1, 1, 1], padding="VALID", name="op_to_store") - op2 = tf.nn.conv2d( - input=tf.nn.relu(op), filters=z, strides=[1, 1, 1, 1], padding="VALID", name="op2_to_store" - ) - - sess.run(tf.compat.v1.global_variables_initializer()) - constant_graph = tf.compat.v1.graph_util.convert_variables_to_constants( - sess, sess.graph_def, ["op2_to_store"] - ) - - graph_def.ParseFromString(constant_graph.SerializeToString()) - with graph.as_default(): - tf.import_graph_def(graph_def, name="") - except: - graph = tf.Graph() - graph_def = tf.compat.v1.GraphDef() - with tf.compat.v1.Session() as sess: - x = tf.compat.v1.placeholder(tf.float32, shape=(1, 3, 3, 1), name="x") - y = tf.constant(np.random.random((2, 2, 1, 1)).astype(np.float32), name="y") - z = tf.constant(np.random.random((1, 1, 1, 1)).astype(np.float32), name="z") - op = tf.nn.conv2d(input=x, filters=y, strides=[1, 1, 1, 1], padding="VALID", name="op_to_store") - op2 = tf.nn.conv2d(input=op, filters=z, strides=[1, 1, 1, 1], padding="VALID", name="op2_to_store") - - sess.run(tf.compat.v1.global_variables_initializer()) - constant_graph = tf.compat.v1.graph_util.convert_variables_to_constants( - sess, sess.graph_def, ["op2_to_store"] - ) - - graph_def.ParseFromString(constant_graph.SerializeToString()) - with graph.as_default(): - tf.import_graph_def(graph_def, name="") - return graph - - -@unittest.skipIf(CONDITION, "missing the env variables 'SIGOPT_API_TOKEN' or 'SIGOPT_PROJECT_ID'") -class TestSigoptTuningStrategy(unittest.TestCase): - @classmethod - def setUpClass(self): - sigopt_api_token = os.getenv("SIGOPT_API_TOKEN") - sigopt_project_id = os.getenv("SIGOPT_PROJECT_ID") - self.constant_graph = build_fake_model() - build_fake_yaml(sigopt_api_token, sigopt_project_id) - build_fake_yaml2(sigopt_api_token, sigopt_project_id) - - @classmethod - def tearDownClass(self): - os.remove("fake_yaml.yaml") - os.remove("fake_yaml2.yaml") - shutil.rmtree("saved", ignore_errors=True) - - def test_run_basic_one_trial(self): - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", (100, 3, 3, 1), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.model = self.constant_graph - quantizer.fit() - - def test_run_basic_max_trials(self): - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml2.yaml") - dataset = quantizer.dataset("dummy", (100, 3, 3, 1), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.model = self.constant_graph - quantizer.fit() - - -if __name__ == "__main__": - unittest.main() diff --git a/test/strategy/test_tpe_1x.py b/test/strategy/test_tpe_1x.py deleted file mode 100644 index 9f5b8eec8a4..00000000000 --- a/test/strategy/test_tpe_1x.py +++ /dev/null @@ -1,162 +0,0 @@ -"""Tests for quantization.""" - -import os -import shutil -import unittest - -import numpy as np -import yaml - - -def build_fake_yaml(): - fake_yaml = """ - model: - name: fake_yaml - framework: tensorflow - inputs: x - outputs: op_to_store - device: cpu - evaluation: - accuracy: - metric: - topk: 1 - tuning: - strategy: - name: tpe - accuracy_criterion: - relative: 0.01 - workspace: - path: saved - """ - y = yaml.load(fake_yaml, Loader=yaml.SafeLoader) - with open("fake_yaml.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - f.close() - - -def build_fake_yaml2(): - fake_yaml = """ - model: - name: fake_yaml - framework: tensorflow - inputs: x - outputs: op_to_store - device: cpu - evaluation: - accuracy: - metric: - topk: 1 - tuning: - strategy: - name: tpe - exit_policy: - max_trials: 5 - accuracy_criterion: - relative: -0.01 - workspace: - path: saved - """ - y = yaml.load(fake_yaml, Loader=yaml.SafeLoader) - with open("fake_yaml2.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - f.close() - - -def build_fake_model(): - import tensorflow as tf - - try: - graph = tf.Graph() - graph_def = tf.GraphDef() - - with tf.Session() as sess: - x = tf.placeholder(tf.float32, shape=(1, 3, 3, 1), name="x") - y = tf.constant(np.random.random((2, 2, 1, 1)), name="y", dtype=tf.float32) - op = tf.nn.conv2d(input=x, filter=y, strides=[1, 1, 1, 1], padding="VALID", name="op_to_store") - - sess.run(tf.global_variables_initializer()) - constant_graph = tf.graph_util.convert_variables_to_constants(sess, sess.graph_def, ["op_to_store"]) - - graph_def.ParseFromString(constant_graph.SerializeToString()) - with graph.as_default(): - tf.import_graph_def(graph_def, name="") - except: - graph = tf.Graph() - graph_def = tf.compat.v1.GraphDef() - with tf.compat.v1.Session() as sess: - x = tf.compat.v1.placeholder(tf.float32, shape=(1, 3, 3, 1), name="x") - y = tf.compat.v1.constant(np.random.random((2, 2, 1, 1)), name="y", dtype=tf.float32) - op = tf.nn.conv2d(input=x, filters=y, strides=[1, 1, 1, 1], padding="VALID", name="op_to_store") - - sess.run(tf.compat.v1.global_variables_initializer()) - constant_graph = tf.compat.v1.graph_util.convert_variables_to_constants( - sess, sess.graph_def, ["op_to_store"] - ) - - graph_def.ParseFromString(constant_graph.SerializeToString()) - with graph.as_default(): - tf.import_graph_def(graph_def, name="") - return graph - - -class TestQuantization(unittest.TestCase): - @classmethod - def setUpClass(self): - self.constant_graph = build_fake_model() - build_fake_yaml() - build_fake_yaml2() - - @classmethod - def tearDownClass(self): - try: - os.remove("fake_yaml.yaml") - os.remove("fake_yaml2.yaml") - - shutil.rmtree("saved", ignore_errors=True) - except: - print("Error while deleting file ") - - def test_run_tpe_one_trial(self): - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", (100, 3, 3, 1), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.model = self.constant_graph - quantizer.fit() - - def test_run_tpe_max_trials(self): - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml2.yaml") - dataset = quantizer.dataset("dummy", (100, 3, 3, 1), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.model = self.constant_graph - quantizer.fit() - - def test_loss_calculation(self): - from neural_compressor.experimental import Quantization, common - from neural_compressor.experimental.contrib.strategy.tpe import TpeTuneStrategy - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", (100, 3, 3, 1), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.model = self.constant_graph - - testObject = TpeTuneStrategy(quantizer.model, quantizer.conf, quantizer.calib_dataloader) - testObject._calculate_loss_function_scaling_components(0.01, 2, testObject.loss_function_config) - # check if latency difference between min and max corresponds to 10 points of loss function - tmp_val = testObject.calculate_loss(0.01, 2, testObject.loss_function_config) - tmp_val2 = testObject.calculate_loss(0.01, 1, testObject.loss_function_config) - self.assertTrue(True if int(tmp_val2 - tmp_val) == 10 else False) - # check if 1% of acc difference corresponds to 10 points of loss function - tmp_val = testObject.calculate_loss(0.02, 2, testObject.loss_function_config) - tmp_val2 = testObject.calculate_loss(0.03, 2, testObject.loss_function_config) - self.assertTrue(True if int(tmp_val2 - tmp_val) == 10 else False) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/strategy/test_tuning_sampler_1x.py b/test/strategy/test_tuning_sampler_1x.py deleted file mode 100644 index d17188c5b10..00000000000 --- a/test/strategy/test_tuning_sampler_1x.py +++ /dev/null @@ -1,200 +0,0 @@ -import unittest -from collections import OrderedDict -from copy import deepcopy - -from neural_compressor.experimental.strategy.utils.tuning_sampler import ( - FallbackTuningSampler, - ModelWiseTuningSampler, - OpTypeWiseTuningSampler, - OpWiseTuningSampler, -) -from neural_compressor.experimental.strategy.utils.tuning_space import TuningSpace -from neural_compressor.experimental.strategy.utils.tuning_structs import OpTuningConfig - -op_cap = { - ("op_name1", "op_type1"): [ - { - "activation": { - "dtype": ["int8"], - "quant_mode": "static", - "scheme": ["sym"], - "granularity": ["per_channel", "per_tensor"], - "algorithm": ["minmax", "kl"], - }, - "weight": {"dtype": ["int8"], "scheme": ["sym"], "granularity": ["per_channel", "per_tensor"]}, - }, - { - "activation": { - "dtype": ["int8"], - "quant_mode": "dynamic", - "scheme": ["sym"], - "granularity": ["per_channel", "per_tensor"], - "algorithm": ["minmax", "kl"], - }, - "weight": {"dtype": ["int8"], "scheme": ["sym"], "granularity": ["per_channel", "per_tensor"]}, - }, - {"activation": {"dtype": "fp32"}, "weight": {"dtype": "fp32"}}, - ], - ("op_name2", "op_type1"): [ - { - "activation": { - "dtype": ["int8"], - "quant_mode": "static", - "scheme": ["sym"], - "granularity": ["per_channel", "per_tensor"], - "algorithm": ["minmax", "kl"], - }, - "weight": {"dtype": ["int8"], "scheme": ["sym"], "granularity": ["per_channel", "per_tensor"]}, - }, - { - "activation": { - "dtype": ["int8"], - "quant_mode": "dynamic", - "scheme": ["sym"], - "granularity": ["per_channel", "per_tensor"], - "algorithm": ["minmax", "kl"], - }, - "weight": {"dtype": ["int8"], "scheme": ["sym"], "granularity": ["per_channel", "per_tensor"]}, - }, - {"activation": {"dtype": "fp32"}, "weight": {"dtype": "fp32"}}, - ], - ("op_name3", "op_type2"): [ - { - "activation": { - "dtype": ["int8"], - "quant_mode": "static", - "scheme": ["sym"], - "granularity": ["per_channel"], - }, - "weight": {"dtype": ["int8"], "scheme": ["sym"], "granularity": ["per_channel"]}, - }, - {"activation": {"dtype": "fp32"}, "weight": {"dtype": "fp32"}}, - ], - ("op_name4", "op_type3"): [ - { - "activation": { - "dtype": ["int8"], - "quant_mode": "static", - "scheme": ["sym"], - "granularity": ["per_channel", "per_tensor"], - }, - }, - { - "activation": { - "dtype": ["int8"], - "quant_mode": "dynamic", - "scheme": ["sym"], - "granularity": ["per_channel", "per_tensor"], - }, - }, - { - "activation": {"dtype": "fp32"}, - }, - ], -} - - -class TestTuningSampler(unittest.TestCase): - def test_tuning_sampler(self): - capability = {"calib": {"calib_sampling_size": [1, 10, 50]}, "op": op_cap} - conf = None - tuning_space = TuningSpace(capability, conf) - - initial_op_tuning_cfg = {} - for item in tuning_space.root_item.options: - if item.item_type == "op": - op_name, op_type = item.name - initial_op_tuning_cfg[item.name] = OpTuningConfig(op_name, op_type, "fp32", tuning_space) - print(initial_op_tuning_cfg[item.name]) - quant_mode_wise_items = OrderedDict() - from neural_compressor.experimental.strategy.utils.constant import auto_query_order as query_order - - pre_items = set() - for quant_mode in query_order: - items = tuning_space.query_items_by_quant_mode(quant_mode) - filtered_items = [item for item in items if item not in pre_items] - pre_items = pre_items.union(set(items)) - quant_mode_wise_items[quant_mode] = filtered_items - - def initial_op_quant_mode(items_lst, target_quant_mode, op_item_dtype_dict): - for item in items_lst: - op_item_dtype_dict[item.name] = target_quant_mode - - op_item_dtype_dict = OrderedDict() - for quant_mode, quant_mode_items in quant_mode_wise_items.items(): - initial_op_quant_mode(quant_mode_items, quant_mode, op_item_dtype_dict) - - op_wise_tuning_sampler = OpWiseTuningSampler( - deepcopy(tuning_space), [], [], op_item_dtype_dict, initial_op_tuning_cfg - ) - self.assertEqual(len(list(op_wise_tuning_sampler)), 128) - optype_wise_tuning_sampler = OpTypeWiseTuningSampler( - deepcopy(tuning_space), [], [], op_item_dtype_dict, initial_op_tuning_cfg - ) - cfg_lst = list(optype_wise_tuning_sampler) - self.assertEqual(len(cfg_lst), 16) - model_wise_tuning_sampler = ModelWiseTuningSampler( - deepcopy(tuning_space), [], [], op_item_dtype_dict, initial_op_tuning_cfg - ) - model_wise_pool = [] - best_tune_cfg = None - for tune_cfg in model_wise_tuning_sampler: - best_tune_cfg = tune_cfg - model_wise_pool.append(tune_cfg) - self.assertEqual(len(model_wise_pool), 8) - - # fallback test - quant_ops = quant_mode_wise_items.get("static", []) - quant_ops += quant_mode_wise_items.get("dynamic", []) - target_dtype = "fp32" - target_type_lst = tuning_space.query_items_by_quant_mode(target_dtype) - fallback_items_lst = [item for item in quant_ops if item in target_type_lst] - if fallback_items_lst: - print(f"Start to fallback op to {target_dtype} one by one.") - fallback_items_name_lst = [item.name for item in fallback_items_lst] - op_dtypes = OrderedDict(zip(fallback_items_name_lst[::-1], [target_dtype] * len(fallback_items_name_lst))) - initial_op_tuning_cfg = deepcopy(best_tune_cfg) - fallback_sampler = FallbackTuningSampler( - tuning_space, - tuning_order_lst=[], - initial_op_tuning_cfg=initial_op_tuning_cfg, - op_dtypes=op_dtypes, - accumulate=False, - ) - fallback_cnt = [] - fp32_lst = [] - for op_cfgs in fallback_sampler: - cnt = 0 - for op_name, op_cfg in op_cfgs.items(): - op_state = op_cfg.get_state() - if "fp32" == op_state["activation"]["dtype"] and ( - "fp32" == op_state["weight"]["dtype"] if "weight" in op_state else True - ): - cnt = cnt + 1 - fp32_lst.append(op_name) - fallback_cnt.append(cnt) - self.assertListEqual(fallback_cnt, [1, 1, 1, 1]) - self.assertListEqual(fp32_lst, fallback_items_name_lst[::-1]) - - fallback_sampler_acc = FallbackTuningSampler( - tuning_space, - tuning_order_lst=[], - initial_op_tuning_cfg=initial_op_tuning_cfg, - op_dtypes=op_dtypes, - accumulate=True, - ) - fallback_cnt = [] - for op_cfgs in fallback_sampler_acc: - cnt = 0 - for op_name, op_cfg in op_cfgs.items(): - op_state = op_cfg.get_state() - if "fp32" == op_state["activation"]["dtype"] and ( - "fp32" == op_state["weight"]["dtype"] if "weight" in op_state else True - ): - cnt = cnt + 1 - fallback_cnt.append(cnt) - self.assertListEqual(fallback_cnt, [2, 3, 4]) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/strategy/test_tuning_space.py b/test/strategy/test_tuning_space.py index 9c4dbff6ecb..9a09f0e8829 100644 --- a/test/strategy/test_tuning_space.py +++ b/test/strategy/test_tuning_space.py @@ -1,9 +1,9 @@ import unittest from copy import deepcopy -from neural_compressor.conf.dotdict import DotDict from neural_compressor.strategy.utils.tuning_space import TuningItem, TuningSpace from neural_compressor.utils import logger +from neural_compressor.utils.utility import DotDict op_cap = { # op have both weight and activation and support static/dynamic/fp32 diff --git a/test/strategy/test_tuning_space_1x.py b/test/strategy/test_tuning_space_1x.py deleted file mode 100644 index ab7fb6ee3c6..00000000000 --- a/test/strategy/test_tuning_space_1x.py +++ /dev/null @@ -1,297 +0,0 @@ -import unittest -from copy import deepcopy - -from neural_compressor.conf.dotdict import DotDict -from neural_compressor.experimental.strategy.utils.tuning_space import TuningSpace -from neural_compressor.utils import logger - -op_cap = { - # op have both weight and activation and support static/dynamic/fp32 - ("op_name1", "op_type1"): [ - { - "activation": { - "dtype": ["int8"], - "quant_mode": "static", - "scheme": ["sym"], - "granularity": ["per_channel", "per_tensor"], - "algorithm": ["minmax", "kl"], - }, - "weight": {"dtype": ["int8"], "scheme": ["sym"], "granularity": ["per_channel", "per_tensor"]}, - }, - { - "activation": { - "dtype": ["int8"], - "quant_mode": "dynamic", - "scheme": ["sym"], - "granularity": ["per_channel", "per_tensor"], - "algorithm": ["minmax", "kl"], - }, - "weight": {"dtype": ["int8"], "scheme": ["sym"], "granularity": ["per_channel", "per_tensor"]}, - }, - {"activation": {"dtype": "fp32"}, "weight": {"dtype": "fp32"}}, - ], - # op have both weight and activation and support static/dynamic/fp32 - ("op_name2", "op_type1"): [ - { - "activation": { - "dtype": ["int8"], - "quant_mode": "static", - "scheme": ["sym"], - "granularity": ["per_channel", "per_tensor"], - "algorithm": ["minmax", "kl"], - }, - "weight": {"dtype": ["int8"], "scheme": ["sym"], "granularity": ["per_channel", "per_tensor"]}, - }, - { - "activation": { - "dtype": ["int8"], - "quant_mode": "dynamic", - "scheme": ["sym"], - "granularity": ["per_channel", "per_tensor"], - "algorithm": ["minmax", "kl"], - }, - "weight": {"dtype": ["int8"], "scheme": ["sym"], "granularity": ["per_channel", "per_tensor"]}, - }, - {"activation": {"dtype": "fp32"}, "weight": {"dtype": "fp32"}}, - ], - # op have both weight and activation and support static/fp32 - ("op_name3", "op_type2"): [ - { - "activation": { - "dtype": ["int8"], - "quant_mode": "static", - "scheme": ["sym"], - "granularity": ["per_channel"], - "algorithm": ["minmax", "kl"], - }, - "weight": { - "dtype": ["int8"], - "scheme": ["sym"], - "granularity": ["per_channel"], - "algorithm": ["minmax", "kl"], - }, - }, - {"activation": {"dtype": "fp32"}, "weight": {"dtype": "fp32"}}, - ], - # op only have activation and support dynamic/fp32 - ("op_name4", "op_type3"): [ - { - "activation": { - "dtype": ["int8"], - "quant_mode": "static", - "scheme": ["sym"], - "granularity": ["per_channel", "per_tensor"], - "algorithm": ["minmax", "kl"], - }, - }, - { - "activation": { - "dtype": ["int8"], - "quant_mode": "dynamic", - "scheme": ["sym"], - "granularity": ["per_channel", "per_tensor"], - "algorithm": ["minmax"], - }, - }, - { - "activation": {"dtype": "fp32"}, - }, - ], -} - - -op_cap2 = { - # The granularity of op activation do not support per_tensor. - ("op_name4", "op_type1"): [ - { - "activation": { - "dtype": ["int8"], - "quant_mode": "static", - "scheme": ["sym"], - "granularity": ["per_channel"], - "algorithm": ["minmax", "kl"], - }, - "weight": {"dtype": ["int8"], "scheme": ["sym"], "granularity": ["per_channel", "per_tensor"]}, - }, - ] -} - - -class TestTuningSampler(unittest.TestCase): - def setUp(self) -> None: - self.capability = {"calib": {"calib_sampling_size": [1, 10, 50]}, "op": deepcopy(op_cap)} - # for optype1,'algorithm': ['minmax', 'kl'] -> ['minmax'] - self.optype_wise_user_config = { - "op_type1": { - "activation": { - "algorithm": ["minmax"], - "granularity": ["per_channel", "per_tensor"], - } - } - } - self.model_wise_user_config = { - "activation": { - "granularity": ["per_channel"], - } - } - # fallback op_name4 - self.op_wise_user_config = { - "op_name4": { - "activation": { - "dtype": ["fp32"], - } - } - } - - self.op_wise_user_config2 = { - "op_name4": { - "activation": { - "granularity": ["per_tensor"], - } - } - } - - self.capability2 = {"calib": {"calib_sampling_size": [1, 10]}, "op": deepcopy(op_cap2)} - - def test_tuning_space_merge_op_wise_not_exist(self): - # op-wise - conf = { - "usr_cfg": { - "quantization": { - "op_wise": deepcopy(self.op_wise_user_config2), - } - } - } - conf = DotDict(conf) - tuning_space2 = TuningSpace(deepcopy(self.capability2), deepcopy(conf)) - logger.debug(tuning_space2.root_item.get_details()) - - def test_tuning_space_creation(self): - conf = None - # Test the creation of tuning space - tuning_space = TuningSpace(self.capability, conf) - logger.debug(tuning_space.root_item.get_details()) - # ops supported static - static_items = tuning_space.query_items_by_quant_mode("static") - static_items_name = [item.name for item in static_items] - self.assertEqual(set(static_items_name), set(op_cap.keys())) - # ops supported dynamic - dynamic_items = tuning_space.query_items_by_quant_mode("dynamic") - dynamic_items_name = [item.name for item in dynamic_items] - all_items_name = list(op_cap.keys()) - all_items_name.remove(("op_name3", "op_type2")) - self.assertEqual(set(dynamic_items_name), set(all_items_name)) - # ops supported fp32 - fp32_items = tuning_space.query_items_by_quant_mode("fp32") - fp32_items_name = [item.name for item in fp32_items] - self.assertEqual(set(fp32_items_name), set(op_cap.keys())) - # all optype - self.assertEqual(list(tuning_space.op_type_wise_items.keys()), ["op_type1", "op_type2", "op_type3"]) - - def test_tuning_space_merge_model_wise(self): - # Test merge with user config, model-wise, optype-wise, op-wise - # model-wise - self.capability = {"calib": {"calib_sampling_size": [1, 10, 50]}, "op": op_cap} - conf = { - "usr_cfg": { - "quantization": { - "model_wise": self.model_wise_user_config, - } - } - } - conf = DotDict(conf) - tuning_space2 = TuningSpace(deepcopy(self.capability), deepcopy(conf)) - logger.debug(tuning_space2.root_item.get_details()) - found_per_tensor = False - for quant_mode in ["static", "dynamic"]: - for op_item in tuning_space2.query_items_by_quant_mode(quant_mode): - for path in tuning_space2.ops_path_set[op_item.name]: - mode_item = tuning_space2.query_quant_mode_item_by_full_path(op_item.name, path) - act_algo_item = mode_item.get_option_by_name(("activation", "granularity")) - if act_algo_item and "per_tensor" in act_algo_item.options: - found_per_tensor = True - break - self.assertFalse(found_per_tensor) - - def test_tuning_space_merge_optype_wise(self): - # optype-wise - conf = { - "usr_cfg": { - "quantization": { - "optype_wise": self.optype_wise_user_config, - } - } - } - conf = DotDict(conf) - tuning_space2 = TuningSpace(deepcopy(self.capability), deepcopy(conf)) - logger.debug(tuning_space2.root_item.get_details()) - found_act_algo_kl_optype1 = False - found_act_algo_kl_others = False - for quant_mode in ["static", "dynamic"]: - for op_item in tuning_space2.query_items_by_quant_mode(quant_mode): - for path in tuning_space2.ops_path_set[op_item.name]: - mode_item = tuning_space2.query_quant_mode_item_by_full_path(op_item.name, path) - act_algo_item = mode_item.get_option_by_name(("activation", "algorithm")) - if act_algo_item and op_item.name[1] == "op_type1" and "kl" in act_algo_item.options: - found_act_algo_kl_optype1 = True - break - if act_algo_item and op_item.name[1] != "op_type1" and "kl" in act_algo_item.options: - found_act_algo_kl_others = True - self.assertFalse(found_act_algo_kl_optype1) - self.assertTrue(found_act_algo_kl_others) - - def test_tuning_space_merge_op_wise(self): - # op-wise - conf = { - "usr_cfg": { - "quantization": { - "op_wise": self.op_wise_user_config, - } - } - } - conf = DotDict(conf) - tuning_space2 = TuningSpace(deepcopy(self.capability), deepcopy(conf)) - logger.debug(tuning_space2.root_item.get_details()) - found_quant_op_name4 = False - found_fp32_op_name4 = False - for quant_mode in ["static", "dynamic"]: - for item in tuning_space2.query_items_by_quant_mode(quant_mode): - if "op_name4" in item.name: - found_quant_op_name4 = True - break - - for item in tuning_space2.query_items_by_quant_mode("fp32"): - if "op_name4" in item.name: - found_fp32_op_name4 = True - break - self.assertFalse(found_quant_op_name4) - self.assertTrue(found_fp32_op_name4) - - def test_tuning_space_merge_model_wise_and_opty_wise(self): - # Test mode-wise + optype-wise - conf = { - "usr_cfg": { - "quantization": { - "model_wise": self.model_wise_user_config, - "optype_wise": self.optype_wise_user_config, - } - } - } - # the optype_wise config will overwrite the model-wise config - conf = DotDict(conf) - tuning_space2 = TuningSpace(deepcopy(self.capability), deepcopy(conf)) - logger.debug(tuning_space2.root_item.get_details()) - found_per_tensor = False - for quant_mode in ["static", "dynamic"]: - for op_item in tuning_space2.query_items_by_quant_mode(quant_mode): - for path in tuning_space2.ops_path_set[op_item.name]: - mode_item = tuning_space2.query_quant_mode_item_by_full_path(op_item.name, path) - act_algo_item = mode_item.get_option_by_name(("activation", "granularity")) - if act_algo_item and "per_tensor" in act_algo_item.options: - found_per_tensor = True - break - self.assertTrue(found_per_tensor) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/strategy/test_tuning_space_v2.py b/test/strategy/test_tuning_space_v2.py index c89e30d864f..9e7111d42fe 100644 --- a/test/strategy/test_tuning_space_v2.py +++ b/test/strategy/test_tuning_space_v2.py @@ -1,9 +1,9 @@ import unittest from copy import deepcopy -from neural_compressor.conf.dotdict import DotDict from neural_compressor.strategy.utils.tuning_space import TuningItem, TuningSpace from neural_compressor.utils import logger +from neural_compressor.utils.utility import DotDict op_cap = { # op1 have both weight and activation and support static/dynamic/fp32/b16 diff --git a/test/strategy/test_tuning_space_v2_1x.py b/test/strategy/test_tuning_space_v2_1x.py deleted file mode 100644 index feab5fed6c1..00000000000 --- a/test/strategy/test_tuning_space_v2_1x.py +++ /dev/null @@ -1,195 +0,0 @@ -import unittest -from copy import deepcopy - -from neural_compressor.conf.dotdict import DotDict -from neural_compressor.experimental.strategy.utils.tuning_space import TuningSpace -from neural_compressor.utils import logger - -op_cap = { - # op1 have both weight and activation and support static/dynamic/fp32/b16 - ("op_name1", "op_type1"): [ - { - "activation": { - "dtype": ["int8"], - "quant_mode": "static", - "scheme": ["sym"], - "granularity": ["per_channel", "per_tensor"], - "algorithm": ["minmax", "kl"], - }, - "weight": {"dtype": ["int8"], "scheme": ["sym"], "granularity": ["per_channel", "per_tensor"]}, - }, - { - "activation": { - "dtype": ["int4"], - "quant_mode": "static", - "scheme": ["sym"], - "granularity": ["per_channel", "per_tensor"], - "algorithm": ["minmax", "kl"], - }, - "weight": {"dtype": ["uint4"], "scheme": ["sym"], "granularity": ["per_channel", "per_tensor"]}, - }, - { - "activation": { - "dtype": ["int8"], - "quant_mode": "dynamic", - "scheme": ["sym"], - "granularity": ["per_channel", "per_tensor"], - "algorithm": ["minmax", "kl"], - }, - "weight": {"dtype": ["int8"], "scheme": ["sym"], "granularity": ["per_channel", "per_tensor"]}, - }, - {"activation": {"dtype": "bf16"}, "weight": {"dtype": "bf16"}}, - {"activation": {"dtype": "fp32"}, "weight": {"dtype": "fp32"}}, - ], - # op2 have both weight and activation and support static/dynamic/fp32 - ("op_name2", "op_type1"): [ - { - "activation": { - "dtype": ["int8"], - "quant_mode": "static", - "scheme": ["sym"], - "granularity": ["per_channel", "per_tensor"], - "algorithm": ["minmax", "kl"], - }, - "weight": {"dtype": ["int8"], "scheme": ["sym"], "granularity": ["per_channel", "per_tensor"]}, - }, - { - "activation": { - "dtype": ["int8"], - "quant_mode": "dynamic", - "scheme": ["sym"], - "granularity": ["per_channel", "per_tensor"], - "algorithm": ["minmax", "kl"], - }, - "weight": {"dtype": ["int8"], "scheme": ["sym"], "granularity": ["per_channel", "per_tensor"]}, - }, - {"activation": {"dtype": "fp32"}, "weight": {"dtype": "fp32"}}, - ], - # op3 have both weight and activation and support int4 - ("op_name3", "op_type3"): [ - { - "activation": { - "dtype": ["int4"], - "quant_mode": "static", - "scheme": ["sym"], - "granularity": ["per_channel", "per_tensor"], - "algorithm": ["minmax", "kl"], - }, - "weight": {"dtype": ["int4"], "scheme": ["sym"], "granularity": ["per_channel", "per_tensor"]}, - }, - { - "activation": { - "dtype": ["int8"], - "quant_mode": "static", - "scheme": ["sym"], - "granularity": ["per_channel", "per_tensor"], - "algorithm": ["minmax", "kl"], - }, - "weight": {"dtype": ["int8"], "scheme": ["sym"], "granularity": ["per_channel", "per_tensor"]}, - }, - {"activation": {"dtype": "fp32"}, "weight": {"dtype": "fp32"}}, - ], -} - - -class TestTuningSpaceV2(unittest.TestCase): - def setUp(self) -> None: - self.capability = {"calib": {"calib_sampling_size": [1, 10, 50]}, "op": deepcopy(op_cap)} - - self.op_wise_user_cfg_for_fallback = { - "op_name1": {"activation": {"dtype": ["fp32"]}, "weight": {"dtype": ["fp32"]}}, - } - - def test_tuning_sampler_int4(self): - # op-wise - conf = {"usr_cfg": {}} - conf = DotDict(conf) - # test space construction - tuning_space = TuningSpace(deepcopy(self.capability), deepcopy(conf)) - logger.debug(tuning_space.root_item.get_details()) - found_int4_activation = False - found_int4_weight = False - op3_act_item = tuning_space.query_quant_mode_item_by_full_path( - ("op_name3", "op_type3"), ("static", "activation") - ) - for dtype_item in op3_act_item.options: - if dtype_item.name == "int4": - found_int4_activation = True - self.assertTrue(found_int4_activation) - op3_weight_item = tuning_space.query_quant_mode_item_by_full_path( - ("op_name3", "op_type3"), ("static", "weight") - ) - for dtype_item in op3_weight_item.options: - if dtype_item.name == "int4": - found_int4_weight = True - self.assertTrue(found_int4_weight) - - def test_sampler_int4(self): - # test sampler - from collections import OrderedDict - - from neural_compressor.strategy.utils.tuning_sampler import OpWiseTuningSampler - from neural_compressor.strategy.utils.tuning_structs import OpTuningConfig - - # op-wise - conf = {"usr_cfg": {}} - conf = DotDict(conf) - # test space construction - tuning_space = TuningSpace(deepcopy(self.capability), deepcopy(conf)) - logger.debug(tuning_space.root_item.get_details()) - initial_op_tuning_cfg = {} - for item in tuning_space.root_item.options: - if item.item_type == "op": - op_name, op_type = item.name - initial_op_tuning_cfg[item.name] = OpTuningConfig(op_name, op_type, "fp32", tuning_space) - quant_mode_wise_items = OrderedDict() - from neural_compressor.strategy.utils.constant import auto_query_order as query_order - - pre_items = set() - for quant_mode in query_order: - items = tuning_space.query_items_by_quant_mode(quant_mode) - filtered_items = [item for item in items if item not in pre_items] - pre_items = pre_items.union(set(items)) - quant_mode_wise_items[quant_mode] = filtered_items - - def initial_op_quant_mode(items_lst, target_quant_mode, op_item_dtype_dict): - for item in items_lst: - op_item_dtype_dict[item.name] = target_quant_mode - - op_item_dtype_dict = OrderedDict() - for quant_mode, quant_mode_items in quant_mode_wise_items.items(): - initial_op_quant_mode(quant_mode_items, quant_mode, op_item_dtype_dict) - - op_wise_tuning_sampler = OpWiseTuningSampler( - deepcopy(tuning_space), [], [], op_item_dtype_dict, initial_op_tuning_cfg - ) - op3 = ("op_name3", "op_type3") - for tune_cfg in op_wise_tuning_sampler: - op_cfg = tune_cfg[op3].get_state() - act_dtype = op_cfg["activation"]["dtype"] - weight_dtype = op_cfg["weight"]["dtype"] - self.assertTrue(act_dtype == weight_dtype == "int4") - - def test_tuning_space_merge_op_wise(self): - # op-wise - conf = { - "usr_cfg": { - "quantization": { - "op_wise": self.op_wise_user_cfg_for_fallback, - } - } - } - conf = DotDict(conf) - # test fallback - tuning_space2 = TuningSpace(deepcopy(self.capability), deepcopy(conf)) - logger.debug(tuning_space2.root_item.get_details()) - op_name1_only_fp32 = True - for quant_mode in ["static", "dynamic"]: - for item in tuning_space2.query_items_by_quant_mode(quant_mode): - if item.name[0] == "op_name1": - op_name1_only_fp32 = False - self.assertTrue(op_name1_only_fp32) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/tfnewapi/test_tensorflow_bias_correction.py b/test/tfnewapi/test_tensorflow_bias_correction.py deleted file mode 100644 index 45e5523705c..00000000000 --- a/test/tfnewapi/test_tensorflow_bias_correction.py +++ /dev/null @@ -1,220 +0,0 @@ -import os -import unittest - -import tensorflow as tf -import yaml -from tensorflow.compat.v1 import graph_util - -from neural_compressor.adaptor.tensorflow import TensorflowQuery -from neural_compressor.adaptor.tf_utils.quantize_graph.quantize_graph_for_intel_cpu import QuantizeGraphForIntel -from neural_compressor.adaptor.tf_utils.quantize_graph_common import QuantizeGraphHelper -from neural_compressor.adaptor.tf_utils.transform_graph.bias_correction import BiasCorrection -from neural_compressor.adaptor.tf_utils.util import disable_random - - -def build_fake_yaml(): - fake_yaml = """ - model: - name: fake_yaml - framework: tensorflow - inputs: input - device: cpu - quantization: - model_wise: - weight: - granularity: per_tensor - scheme: sym - dtype: int8 - algorithm: minmax - evaluation: - accuracy: - metric: - topk: 1 - tuning: - strategy: - name: basic - accuracy_criterion: - relative: 0.1 - exit_policy: - performance_only: True - workspace: - path: saved - """ - y = yaml.load(fake_yaml, Loader=yaml.SafeLoader) - with open("fake_yaml.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - f.close() - - -class TestBiasCorrectionNewApi(unittest.TestCase): - @classmethod - def setUpClass(self): - build_fake_yaml() - - @classmethod - def tearDownClass(self): - os.remove("fake_yaml.yaml") - - @disable_random() - def test_bias_correction_new_api(self): - tf.compat.v1.disable_eager_execution() - x = tf.compat.v1.placeholder(tf.float32, [1, 224, 224, 3], name="input") - - if tf.version.VERSION <= "2.1.0": - x = tf.nn.relu(x) - conv1_weights = tf.compat.v1.get_variable( - "weights1", [3, 3, 3, 32], initializer=tf.compat.v1.random_normal_initializer() - ) - conv1 = tf.nn.conv2d(x, conv1_weights, strides=[1, 1, 1, 1], padding="SAME") - normed = tf.nn.bias_add( - conv1, - tf.constant( - [ - 3.0, - 1.2, - 1.0, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 0, - 1, - 4.0, - 5.2, - 8.1, - 2, - 4, - 5, - 8, - 9, - 10, - 12, - 11, - 2, - 5.0, - 7.2, - 3.2, - 3, - 4, - 5, - 7, - 8, - ] - ), - ) - relu1 = tf.nn.relu(normed, name="Relu_1") - op_wise_sequences = TensorflowQuery( - local_config_file=os.path.join(os.path.dirname(__file__), "../../neural_compressor/adaptor/tensorflow.yaml") - ).get_eightbit_patterns() - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[relu1.name.split(":")[0]] - ) - output_graph_def = QuantizeGraphHelper.remove_training_nodes( - output_graph_def, protected_nodes=[relu1.name.split(":")[0]] - ) - inputs = [x.name.split(":")[0]] - outputs = [relu1.name.split(":")[0]] - op_wise_config = { - "Conv2D": (False, "minmax", False, 7.0), - } - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 224, 224, 3), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - int8_output_graph = quantizer.fit() - - correct_graph_def = BiasCorrection( - int8_output_graph.graph_def, output_graph_def, "weight_empirical", True - ).do_transformation() - - self.assertEqual(len(correct_graph_def.node), len(int8_output_graph.graph_def.node)) - - -class TestBiasCorrectionOldApi(unittest.TestCase): - @disable_random() - def test_bias_correction_old_api(self): - tf.compat.v1.disable_eager_execution() - x = tf.compat.v1.placeholder(tf.float32, [1, 224, 224, 3], name="input") - - if tf.version.VERSION <= "2.1.0": - x = tf.nn.relu(x) - conv_weights = tf.compat.v1.get_variable( - "weights", [3, 3, 3, 32], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv2d(x, conv_weights, strides=[1, 1, 1, 1], padding="SAME") - normed = tf.nn.bias_add( - conv, - tf.constant( - [ - 3.0, - 1.2, - 1.0, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 0, - 1, - 4.0, - 5.2, - 8.1, - 2, - 4, - 5, - 8, - 9, - 10, - 12, - 11, - 2, - 5.0, - 7.2, - 3.2, - 3, - 4, - 5, - 7, - 8, - ] - ), - ) - relu = tf.nn.relu(normed, name="Relu_0") - op_wise_sequences = TensorflowQuery( - local_config_file=os.path.join(os.path.dirname(__file__), "../../neural_compressor/adaptor/tensorflow.yaml") - ).get_eightbit_patterns() - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[relu.name.split(":")[0]] - ) - output_graph_def = QuantizeGraphHelper.remove_training_nodes( - output_graph_def, protected_nodes=[relu.name.split(":")[0]] - ) - inputs = [x.name.split(":")[0]] - outputs = [relu.name.split(":")[0]] - op_wise_config = { - "Conv2D": (False, "minmax", False, 7.0), - } - - int8_graph_def, _, _ = QuantizeGraphForIntel( - output_graph_def, inputs, outputs, op_wise_config, op_wise_sequences, "cpu" - ).do_transform() - - correct_graph_def = BiasCorrection(int8_graph_def, output_graph_def).do_transformation() - self.assertEqual(len(correct_graph_def.node), len(int8_graph_def.node)) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/tfnewapi/test_tensorflow_fuse_reshape_transpose.py b/test/tfnewapi/test_tensorflow_fuse_reshape_transpose.py deleted file mode 100644 index b80dc785d97..00000000000 --- a/test/tfnewapi/test_tensorflow_fuse_reshape_transpose.py +++ /dev/null @@ -1,139 +0,0 @@ -import imp -import os -import unittest - -import numpy as np -import tensorflow.compat.v1 as tf -import yaml -from numpy.core.fromnumeric import squeeze -from tensorflow.compat.v1 import graph_util - -from neural_compressor.adaptor.tf_utils.util import disable_random - - -def build_fake_yaml(): - fake_yaml = """ - model: - name: fake_yaml - framework: tensorflow - inputs: input - outputs: op_to_store - device: cpu - quantization: - model_wise: - weight: - granularity: per_tensor - scheme: sym - dtype: int8 - algorithm: minmax - evaluation: - accuracy: - metric: - topk: 1 - tuning: - strategy: - name: mse - accuracy_criterion: - relative: 0.01 - exit_policy: - performance_only: True - workspace: - path: saved - """ - y = yaml.load(fake_yaml, Loader=yaml.SafeLoader) - with open("fake_yaml.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - f.close() - - -class TestFuseReshapeTransposeOptimizer(unittest.TestCase): - @classmethod - def setUpClass(self): - build_fake_yaml() - - @classmethod - def tearDownClass(self): - os.remove("fake_yaml.yaml") - - @disable_random() - def test_fuse_enter_reshape_transpose(self): - x_data = np.array([[0.1, 0.2], [0.2, 0.3]]) - y_data = np.array([[1, 2], [3, 4]], dtype=np.float32) - x = tf.placeholder(tf.float32, shape=[2, 2], name="x") - y = tf.constant(y_data, dtype=tf.float32, shape=[2, 2]) - enter = tf.raw_ops.Enter(data=y, frame_name="test") - enter_perm = tf.raw_ops.Enter(data=[1, 0], frame_name="test", is_constant=True) - transpose = tf.transpose(enter, perm=enter_perm) - enter_reshape = tf.raw_ops.Enter(data=[2, 2], frame_name="test", is_constant=True) - reshape = tf.reshape(transpose, enter_reshape) - x_enter = tf.raw_ops.Enter(data=x, frame_name="test") - z = tf.raw_ops.MatMul(a=x_enter, b=reshape, name="matmul_1") - z = tf.raw_ops.Exit(data=z) - found_quantized_matmul = True - found_transpose = False - found_reshape = False - - with tf.Session() as sess: - sess.run(z, feed_dict={x: x_data, y: y_data}) - float_graph_def = sess.graph.as_graph_def() - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(2, 2), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.eval_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.model = float_graph_def - output_graph = quantizer.fit() - for i in output_graph.graph_def.node: - if i.op == "MatMul": - found_quantized_matmul = False - if i.op == "Transpose": - found_transpose = True - if i.op == "Reshape": - found_reshape = True - self.assertEqual(found_quantized_matmul, True) - self.assertEqual(found_transpose, False) - self.assertEqual(found_reshape, False) - - @disable_random() - def test_fuse_reshape_transpose(self): - x_data = np.array([[0.1, 0.2], [0.2, 0.3]]) - y_data = np.array([[1, 2], [3, 4]], dtype=np.float32) - x = tf.placeholder(tf.float32, shape=[2, 2], name="x") - y = tf.constant(y_data, dtype=tf.float32, shape=[2, 2]) - transpose = tf.transpose(y, perm=[1, 0]) - reshape = tf.reshape(transpose, [2, 2]) - z = tf.raw_ops.MatMul(a=x, b=reshape, name="matmul_2") - z = tf.nn.bias_add(z, [1, 2], name="op_to_store") - found_quantized_matmul = True - found_transpose = False - found_reshape = False - - with tf.Session() as sess: - sess.run(z, feed_dict={x: x_data, y: y_data}) - float_graph_def = sess.graph.as_graph_def() - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(2, 2), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.eval_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.model = float_graph_def - output_graph = quantizer.fit() - - for i in output_graph.graph_def.node: - if i.op == "MatMul": - found_quantized_matmul = False - if i.op == "Transpose": - found_transpose = True - if i.op == "Reshape": - found_reshape = True - self.assertEqual(found_quantized_matmul, True) - self.assertEqual(found_transpose, False) - self.assertEqual(found_reshape, False) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/tfnewapi/test_tensorflow_graph_biasadd_add_fusion.py b/test/tfnewapi/test_tensorflow_graph_biasadd_add_fusion.py deleted file mode 100644 index ef6b7745271..00000000000 --- a/test/tfnewapi/test_tensorflow_graph_biasadd_add_fusion.py +++ /dev/null @@ -1,135 +0,0 @@ -# -# -*- coding: utf-8 -*- -# -import os -import unittest - -import numpy as np -import tensorflow as tf -import yaml -from tensorflow.compat.v1 import graph_util - -from neural_compressor.adaptor.tensorflow import TensorflowQuery -from neural_compressor.adaptor.tf_utils.util import disable_random - - -def build_fake_yaml(): - fake_yaml = """ - model: - name: fake_yaml - framework: tensorflow - inputs: input - device: cpu - quantization: - model_wise: - weight: - granularity: per_tensor - scheme: sym - dtype: int8 - algorithm: minmax - evaluation: - accuracy: - metric: - topk: 1 - tuning: - strategy: - name: basic - accuracy_criterion: - relative: 0.1 - exit_policy: - performance_only: True - workspace: - path: saved - """ - y = yaml.load(fake_yaml, Loader=yaml.SafeLoader) - with open("fake_yaml.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - f.close() - - -class TestConvBiasAddAddFusion(unittest.TestCase): - @classmethod - def setUpClass(self): - build_fake_yaml() - - @classmethod - def tearDownClass(self): - os.remove("fake_yaml.yaml") - - @disable_random() - def test_conv_biasadd_add_relu_fusion(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - top_relu = tf.nn.relu(x) - - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv2d(top_relu, conv_weights, strides=[1, 2, 2, 1], padding="SAME") - normed = tf.nn.bias_add(conv, tf.constant([3.0, 1.2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 12, 2, 3, 4])) - add = normed + tf.constant([3.0]) - relu = tf.nn.relu6(add) - mul1 = tf.math.multiply(relu, tf.constant([0.1])) - mul2 = tf.math.multiply(mul1, tf.constant([0.8]), name="op_to_store") - - out_name = mul2.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - - from neural_compressor.adaptor.tf_utils.graph_rewriter.generic.fuse_biasadd_add import ( - FuseBiasAddAndAddOptimizer, - ) - - output_graph_def = FuseBiasAddAndAddOptimizer(output_graph_def).do_transformation() - - found_addv2 = False - - for i in output_graph_def.node: - if i.op.find("AddV2") != -1: - found_addv2 = True - break - - self.assertEqual(found_addv2, False) - - def test_conv_biasadd_add_relu_no_fusion(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - top_relu = tf.nn.relu(x) - - conv_weights2 = tf.compat.v1.get_variable( - "weight2", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv2 = tf.nn.conv2d(top_relu, conv_weights2, strides=[1, 2, 2, 1], padding="SAME") - normed2 = tf.nn.bias_add(conv2, tf.constant([3.0, 1.2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 12, 2, 3, 4])) - add_y = tf.compat.v1.get_variable("add_y", [16], initializer=tf.compat.v1.random_normal_initializer()) - add = normed2 + add_y - relu = tf.nn.relu6(add) - mul1 = tf.math.multiply(relu, tf.constant([0.1])) - mul2 = tf.math.multiply(mul1, tf.constant([0.8]), name="op_to_store") - - out_name = mul2.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - - from neural_compressor.adaptor.tf_utils.graph_rewriter.generic.fuse_biasadd_add import ( - FuseBiasAddAndAddOptimizer, - ) - - output_graph_def = FuseBiasAddAndAddOptimizer(output_graph_def).do_transformation() - - found_addv2 = False - - for i in output_graph_def.node: - if i.op.find("AddV2") != -1: - found_addv2 = True - break - - self.assertEqual(found_addv2, True) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/tfnewapi/test_tensorflow_graph_conv_fusion.py b/test/tfnewapi/test_tensorflow_graph_conv_fusion.py deleted file mode 100644 index 19e38a7a8f8..00000000000 --- a/test/tfnewapi/test_tensorflow_graph_conv_fusion.py +++ /dev/null @@ -1,817 +0,0 @@ -# -# -*- coding: utf-8 -*- -# -import os -import unittest - -import numpy as np -import tensorflow as tf -import yaml -from pkg_resources import parse_version -from tensorflow.compat.v1 import graph_util -from tensorflow.python.framework import function - -from neural_compressor.adaptor.tensorflow import TensorflowQuery -from neural_compressor.adaptor.tf_utils.graph_rewriter.generic.fold_batch_norm import FoldBatchNormNodesOptimizer -from neural_compressor.adaptor.tf_utils.graph_rewriter.generic.strip_unused_nodes import StripUnusedNodesOptimizer -from neural_compressor.adaptor.tf_utils.quantize_graph.qdq.optimize_qdq import OptimizeQDQGraph -from neural_compressor.adaptor.tf_utils.util import disable_random - - -def build_fake_yaml(): - fake_yaml = """ - model: - name: fake_yaml - framework: tensorflow - inputs: input - device: cpu - quantization: - model_wise: - weight: - granularity: per_tensor - scheme: sym - dtype: int8 - algorithm: minmax - evaluation: - accuracy: - metric: - topk: 1 - tuning: - strategy: - name: basic - accuracy_criterion: - relative: 0.1 - exit_policy: - performance_only: True - workspace: - path: saved - """ - y = yaml.load(fake_yaml, Loader=yaml.SafeLoader) - with open("fake_yaml.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - f.close() - - -class TestConvBiasAddAddReluFusion(unittest.TestCase): - @classmethod - def setUpClass(self): - build_fake_yaml() - - @classmethod - def tearDownClass(self): - os.remove("fake_yaml.yaml") - - @disable_random() - def test_conv_single_fusion(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - top_relu = tf.nn.relu(x) - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(top_relu, paddings, "CONSTANT") - conv1_weights = tf.compat.v1.get_variable( - "weight_conv1", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv1 = tf.nn.conv2d(x_pad, conv1_weights, strides=[1, 2, 2, 1], padding="VALID") - matmul_weights = tf.compat.v1.get_variable( - "weight_matmul", [1, 28, 16, 32], initializer=tf.compat.v1.random_normal_initializer() - ) - matmul = tf.linalg.matmul(conv1, matmul_weights) - conv2_weights = tf.compat.v1.get_variable( - "weight_conv2", [7, 7, 32, 1], initializer=tf.compat.v1.random_normal_initializer() - ) - conv2 = tf.nn.conv2d(matmul, conv2_weights, strides=[1, 2, 2, 1], padding="VALID") - leaky_relu = tf.nn.leaky_relu(conv2, name="op_to_store") - - out_name = leaky_relu.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - - find_single_qconv = [] - for i in output_graph.graph_def.node: - if i.op == "_FusedQuantizedConv2D": - find_single_qconv.append(i.attr["fused_ops"].list.s == [b"Requantize"]) - - self.assertEqual(find_single_qconv, [False, False]) - - @disable_random() - def test_spacetobatchnd_conv2d_batchtospacend_fusion(self): - i = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - x = tf.space_to_batch_nd(i, block_shape=[2, 2], paddings=[[0, 0], [0, 0]]) - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv2d(x, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - y = tf.compat.v1.batch_to_space_nd(conv, block_shape=[2, 2], crops=[[0, 0], [0, 0]]) - out = tf.identity(y, name="op_to_store") - out_name = out.name.split(":")[0] - - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - found_op = False - - for i in output_graph.graph_def.node: - if i.op == "SpaceToBatchND" or i.op == "BatchToSpaceND": - found_op = True - break - - self.assertEqual(found_op, False) - - @disable_random() - def test_conv_relu_fusion(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - top_relu = tf.nn.relu(x) - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(top_relu, paddings, "CONSTANT") - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv2d(x_pad, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - relu = tf.nn.relu(conv) - - relu6 = tf.nn.relu6(relu, name="op_to_store") - - out_name = relu6.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - found_conv_fusion = True - - for i in output_graph.graph_def.node: - if i.op == "Relu": - found_conv_fusion = False - break - - self.assertEqual(found_conv_fusion, False) - - @disable_random() - def test_conv_biasadd_relu6_fusion(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(x, paddings, "CONSTANT") - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv2d(x_pad, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - normed = tf.compat.v1.layers.batch_normalization(conv) - - relu6 = tf.nn.relu6(normed, name="op_to_store") - - out_name = relu6.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - found_conv_fusion = True - - for i in output_graph.graph_def.node: - if i.op == "Relu6": - found_conv_fusion = False - break - self.assertEqual(found_conv_fusion, True) - - @disable_random() - def test_conv_biasadd_swishf32_fusion(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(x, paddings, "CONSTANT") - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv2d(x_pad, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - normed = tf.compat.v1.layers.batch_normalization(conv) - - @function.Defun(tf.float32, func_name="swish_f32") - def swish_f32(x): - return tf.nn.silu(x, beta=1.0) - - swish = swish_f32(normed, name="swish_f32_output_node") - - out_name = swish.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - found_conv_fusion = True - - for i in output_graph.graph_def.node: - if i.op == "swish_f32": - found_conv_fusion = False - break - self.assertEqual(found_conv_fusion, True) - - @disable_random() - def test_conv_addv2_fusion(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - conv1_weights = tf.compat.v1.get_variable( - "weight_conv1", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv1 = tf.nn.conv2d(x, conv1_weights, strides=[1, 2, 2, 1], padding="SAME") - conv2_weights = tf.compat.v1.get_variable( - "weight_conv2", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv2 = tf.nn.conv2d(x, conv2_weights, strides=[1, 2, 2, 1], padding="SAME") - sumadd = tf.raw_ops.AddV2(x=conv1, y=conv2, name="addv2") - - out_name = sumadd.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - - found_conv_fusion = False - for i in output_graph.graph_def.node: - if i.op.find("QuantizedConv2D") != -1: - found_conv_fusion = True - break - - self.assertEqual(found_conv_fusion, True) - - @disable_random() - def test_conv_biasadd_add_relu_fusion(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - top_relu = tf.nn.relu(x) - conv_weights2 = tf.compat.v1.get_variable( - "weight2", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv2 = tf.nn.conv2d(top_relu, conv_weights2, strides=[1, 2, 2, 1], padding="SAME") - normed2 = tf.nn.bias_add(conv2, tf.constant([3.0, 1.2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 12, 2, 3, 4])) - relu = tf.nn.relu(normed2 + tf.constant([3.0])) - relu6 = tf.nn.relu6(relu, name="op_to_store") - - out_name = relu6.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - - found_conv_fusion = False - - for i in output_graph.graph_def.node: - if i.op.find("QuantizedConv2D") != -1: - found_conv_fusion = True - break - - self.assertEqual(found_conv_fusion, True) - - @disable_random() - def test_conv_biasadd_addv2_relu_fallback_fusion_1(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - top_relu = tf.nn.leaky_relu(x) - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(x, paddings, "CONSTANT") - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv2d(x_pad, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - normed = tf.compat.v1.layers.batch_normalization(conv) - # relu = tf.nn.relu(normed) - - conv_weights2 = tf.compat.v1.get_variable( - "weight2", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv2 = tf.nn.conv2d(top_relu, conv_weights2, strides=[1, 2, 2, 1], padding="SAME") - normed2 = tf.compat.v1.layers.batch_normalization(conv2) - # relu2 = tf.nn.relu(normed2) - add = tf.raw_ops.AddV2(x=normed, y=normed2, name="addv2") - relu = tf.nn.relu(add) - relu6 = tf.nn.relu6(relu, name="op_to_store") - - out_name = relu6.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - - found_conv_fusion = False - - for i in output_graph.graph_def.node: - if i.op == "_FusedQuantizedConv2D" and i.attr["fused_ops"].list.s == [ - b"BiasAdd", - b"Sum", - b"Relu", - b"Requantize", - ]: - found_conv_fusion = True - break - self.assertEqual(found_conv_fusion, True) - - @disable_random() - def test_conv_biasadd_addv2_relu_fallback_fusion_2(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - top_relu = tf.nn.relu(x) - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(top_relu, paddings, "CONSTANT") - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv2d(x_pad, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - normed = tf.compat.v1.layers.batch_normalization(conv) - # relu = tf.nn.relu(normed) - - conv_weights2 = tf.compat.v1.get_variable( - "weight2", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv2 = tf.nn.conv2d(top_relu, conv_weights2, strides=[1, 2, 2, 1], padding="SAME") - normed2 = tf.compat.v1.layers.batch_normalization(conv2) - # relu2 = tf.nn.relu(normed2) - add = tf.raw_ops.AddV2(x=normed, y=normed2, name="addv2") - relu = tf.nn.relu(add) - relu6 = tf.nn.relu6(relu, name="op_to_store") - - out_name = relu6.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - found_conv_fusion = False - - for i in output_graph.graph_def.node: - if i.op == "_FusedQuantizedConv2D" and i.attr["fused_ops"].list.s == [b"BiasAdd", b"Requantize"]: - found_conv_fusion = True - break - - self.assertEqual(found_conv_fusion, True) - - @disable_random() - def test_conv_fusion_with_last_matmul(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - top_relu = tf.nn.relu(x) - # paddings = tf.constant([[0, 0], [1, 1], [1, 1], [0, 0]]) - # x_pad = tf.pad(top_relu, paddings, "CONSTANT") - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv2d(top_relu, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - normed = tf.compat.v1.layers.batch_normalization(conv) - - relu = tf.nn.relu(normed) - pooling = tf.nn.max_pool(relu, ksize=1, strides=[1, 2, 2, 1], padding="SAME") - reshape = tf.reshape(pooling, [-1, 3136]) - - y_data = np.random.random([3136, 1]) - - y = tf.constant(y_data, dtype=tf.float32, shape=[3136, 1]) - z = tf.matmul(reshape, y) - relu1 = tf.nn.relu(z) - y_data_1 = np.random.random([1, 1]) - y_1 = tf.constant(y_data_1, dtype=tf.float32, shape=[1, 1]) - - z_2nd_matmul = tf.matmul(relu1, y_1) - relu6 = tf.nn.relu6(z_2nd_matmul, name="op_to_store") - - out_name = relu6.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - - quantize_v2_count = 0 - for i in output_graph.graph_def.node: - if i.op == "QuantizeV2": - quantize_v2_count += 1 - break - - self.assertEqual(quantize_v2_count, 1) - - @disable_random() - def test_conv_fusion_with_last_conv(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - top_relu = tf.nn.relu(x) - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv2d(top_relu, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - normed = tf.compat.v1.layers.batch_normalization(conv) - - relu = tf.nn.relu(normed) - pooling = tf.nn.max_pool(relu, ksize=1, strides=[1, 2, 2, 1], padding="SAME") - conv_weights_2 = tf.compat.v1.get_variable( - "weight2", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv2 = tf.nn.conv2d(pooling, conv_weights_2, strides=[1, 2, 2, 1], padding="VALID") - conv_weights_3 = tf.compat.v1.get_variable( - "weight3", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - relu2 = tf.nn.relu(conv2) - conv3 = tf.nn.conv2d(relu2, conv_weights_3, strides=[1, 2, 2, 1], padding="VALID") - - relu3 = tf.nn.relu(conv3) - relu6 = tf.nn.relu6(relu3, name="op_to_store") - - out_name = relu6.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - - quantize_v2_count = 0 - for i in output_graph.graph_def.node: - if i.op == "QuantizeV2": - quantize_v2_count += 1 - break - - self.assertEqual(quantize_v2_count, 1) - - @disable_random() - def test_conv_fusion_with_max_pooling(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - - relu = tf.nn.relu(x) - pooling = tf.nn.max_pool(relu, ksize=1, strides=[1, 2, 2, 1], padding="SAME") - conv_weights = tf.compat.v1.get_variable( - "weight2", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv2d(pooling, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - biasadd = tf.compat.v1.layers.batch_normalization(conv, name="op_to_store") - out_name = biasadd.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - - quantized_pool_data_type = None - quantized_conv_data_type = None - for i in output_graph.graph_def.node: - if i.op.find("QuantizedMaxPool") != -1: - quantized_pool_data_type = i.attr["T"].type - if i.op.find("QuantizedConv2D") != -1: - quantized_conv_data_type = i.attr["Tinput"].type - - self.assertNotEqual(quantized_pool_data_type, None) - self.assertEqual(quantized_pool_data_type, quantized_conv_data_type) - - @disable_random() - def test_conv3d_addv2_relu_fusion(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 128, 64, 64, 16], name="input") - top_relu = tf.nn.relu(x) - conv3d_1_weights = tf.compat.v1.get_variable( - "weight_conv3d_1", [3, 3, 3, 16, 32], initializer=tf.compat.v1.random_normal_initializer() - ) - conv3d_1 = tf.nn.conv3d(top_relu, conv3d_1_weights, strides=[1, 2, 2, 2, 1], padding="SAME") - add = tf.raw_ops.AddV2(x=conv3d_1, y=tf.constant(np.random.randn(32), dtype=tf.float32), name="addv2") - relu = tf.nn.relu(add) - conv3d_2_weights = tf.compat.v1.get_variable( - "weight_conv3d_2", [3, 3, 3, 32, 1], initializer=tf.compat.v1.random_normal_initializer() - ) - conv3d_2 = tf.nn.conv3d(relu, conv3d_2_weights, strides=[1, 2, 2, 2, 1], padding="SAME") - - out_name = conv3d_2.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 128, 64, 64, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - - found_conv_sumadd_fusion = False - found_conv_biasadd_fusion = False - for i in output_graph.graph_def.node: - if i.op == "_FusedQuantizedConv3D": - if b"Sum" in i.attr["fused_ops"].list.s: - found_conv_sumadd_fusion = True - if i.attr["fused_ops"].list.s == [b"BiasAdd", b"Relu", b"Requantize"]: - found_conv_biasadd_fusion = True - self.assertEqual(found_conv_sumadd_fusion, False) - self.assertEqual(found_conv_biasadd_fusion, True) - - # conv2d + dummybiasadd + addv2 fusion - @disable_random() - def test_conv_add_addn_non_const_fusion(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(x, paddings, "CONSTANT") - top_relu = tf.nn.relu(x_pad) - conv2d_1_weights = tf.compat.v1.get_variable( - "weight1", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv2d_1 = tf.nn.conv2d(top_relu, conv2d_1_weights, strides=[1, 2, 2, 1], padding="SAME") - conv2d_2_weights = tf.compat.v1.get_variable( - "weight2", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv2d_2 = tf.nn.conv2d(top_relu, conv2d_2_weights, strides=[1, 2, 2, 1], padding="SAME") - add_1 = tf.raw_ops.AddV2(x=conv2d_1, y=conv2d_2, name="addv2_1") - conv2d_3_weights = tf.compat.v1.get_variable( - "weight3", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv2d_3 = tf.nn.conv2d(top_relu, conv2d_3_weights, strides=[1, 2, 2, 1], padding="SAME") - add = tf.raw_ops.AddV2(x=add_1, y=conv2d_3, name="addv2_2") - out_name = add.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - - found_conv_fusion = False - for i in output_graph.graph_def.node: - if i.op == "_FusedQuantizedConv2D" and i.attr["fused_ops"].list.s == [ - b"BiasAdd", - b"Sum", - b"Requantize", - ]: - found_conv_fusion = True - self.assertEqual(found_conv_fusion, True) - - @disable_random() - @unittest.skipIf( - tf.__version__ not in ["2.11.0202242", "2.11.0202250", "2.11.0202317", "2.11.0202323"], - "deconv2d quantization only support 2.11", - ) - def test_deconv2d_biasadd_fusion(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 2, 2, 1], name="input") - conv_weights2 = tf.compat.v1.get_variable( - "weight2", [3, 3, 1, 1], initializer=tf.compat.v1.random_normal_initializer() - ) - - conv2 = tf.nn.conv2d_transpose( - x, conv_weights2, output_shape=[1, 2, 2, 1], strides=[1, 1, 1, 1], padding="SAME" - ) - - normed2 = tf.nn.bias_add(conv2, tf.constant([3.0])) - out = tf.identity(normed2) - - out_name = out.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 2, 2, 1), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - - found_deconv2d_fusion = False - - for i in output_graph.graph_def.node: - if i.op.find("_FusedQuantizedDeconv2D") != -1: - found_deconv2d_fusion = True - break - - self.assertEqual(found_deconv2d_fusion, True) - - @disable_random() - @unittest.skipIf( - tf.__version__ not in ["2.11.0202242", "2.11.0202250", "2.11.0202317", "2.11.0202323"], - "deconv2d quantization only support 2.11", - ) - def test_single_deconv2d_fusion(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 2, 2, 1], name="input") - conv_weights2 = tf.compat.v1.get_variable( - "weight2", [3, 3, 1, 1], initializer=tf.compat.v1.random_normal_initializer() - ) - - conv2 = tf.nn.conv2d_transpose( - x, conv_weights2, output_shape=[1, 2, 2, 1], strides=[1, 1, 1, 1], padding="SAME" - ) - - out = tf.identity(conv2) - - out_name = out.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 2, 2, 1), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - - found_deconv2d_fusion = False - - for i in output_graph.graph_def.node: - if i.op.find("_FusedQuantizedDeconv2D") != -1: - found_deconv2d_fusion = True - break - - self.assertEqual(found_deconv2d_fusion, True) - - @disable_random() - @unittest.skipIf( - tf.__version__ not in ["2.11.0202242", "2.11.0202250", "2.11.0202317", "2.11.0202323"], - "deconv2d quantization only support 2.11", - ) - def test_deconv3d_biasadd_fusion(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 2, 2, 2, 1], name="input") - conv3d_weights = tf.compat.v1.get_variable( - "weight_conv3d_1", [3, 3, 3, 1, 1], initializer=tf.compat.v1.random_normal_initializer() - ) - conv3d = tf.nn.conv3d_transpose( - x, conv3d_weights, output_shape=[1, 2, 2, 2, 1], strides=[1, 1, 1, 1, 1], padding="SAME" - ) - - normed2 = tf.nn.bias_add(conv3d, tf.constant([3.0])) - out = tf.identity(normed2) - - out_name = out.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 2, 2, 2, 1), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - - found_deconv3d_fusion = False - - for i in output_graph.graph_def.node: - if i.op.find("_FusedQuantizedDeconv3D") != -1: - found_deconv3d_fusion = True - break - - self.assertEqual(found_deconv3d_fusion, True) - - @disable_random() - @unittest.skipIf( - tf.__version__ not in ["2.11.0202242", "2.11.0202250", "2.11.0202317", "2.11.0202323"], - "deconv2d quantization only support 2.11", - ) - def test_single_deconv3d_fusion(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 2, 2, 2, 1], name="input") - conv3d_weights = tf.compat.v1.get_variable( - "weight_conv3d_1", [3, 3, 3, 1, 1], initializer=tf.compat.v1.random_normal_initializer() - ) - conv3d = tf.nn.conv3d_transpose( - x, conv3d_weights, output_shape=[1, 2, 2, 2, 1], strides=[1, 1, 1, 1, 1], padding="SAME" - ) - - out = tf.identity(conv3d) - - out_name = out.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 2, 2, 2, 1), label=True) - # quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - - found_deconv3d_fusion = False - - for i in output_graph.graph_def.node: - if i.op.find("_FusedQuantizedDeconv3D") != -1: - found_deconv3d_fusion = True - break - - self.assertEqual(found_deconv3d_fusion, True) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/tfnewapi/test_tensorflow_graph_conv_requantize_fusion.py b/test/tfnewapi/test_tensorflow_graph_conv_requantize_fusion.py deleted file mode 100644 index 55021a94eaa..00000000000 --- a/test/tfnewapi/test_tensorflow_graph_conv_requantize_fusion.py +++ /dev/null @@ -1,954 +0,0 @@ -# -# -*- coding: utf-8 -*- -# -import logging -import os -import unittest - -import numpy as np -import tensorflow as tf -import yaml -from tensorflow.compat.v1 import graph_util - -from neural_compressor.adaptor.tensorflow import TensorflowQuery -from neural_compressor.adaptor.tf_utils.util import disable_random - - -def build_tensorflow_yaml(): - fake_yaml = """ - model: - name: tensorflow_yaml - framework: tensorflow - inputs: input - device: cpu - quantization: - model_wise: - weight: - granularity: per_tensor - scheme: sym - dtype: int8 - algorithm: minmax - evaluation: - accuracy: - metric: - topk: 1 - tuning: - strategy: - name: basic - accuracy_criterion: - relative: 0.1 - exit_policy: - performance_only: True - workspace: - path: saved - """ - y = yaml.load(fake_yaml, Loader=yaml.SafeLoader) - with open("tensorflow_yaml.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - f.close() - - -class TestConvRequantizedFusionNewAPI(unittest.TestCase): - @classmethod - def setUpClass(self): - build_tensorflow_yaml() - - @classmethod - def tearDownClass(self): - os.remove("tensorflow_yaml.yaml") - - @disable_random() - def test_conv_biasadd_relu6_fusion(self): - logging.getLogger().info("test_conv_biasadd_relu6_fusion") - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(x, paddings, "CONSTANT") - conv_weights = tf.compat.v1.get_variable( - "weight0", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv2d(x_pad, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - normed = tf.compat.v1.layers.batch_normalization(conv) - - relu6 = tf.nn.relu6(normed, name="op_to_store") - - out_name = relu6.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("tensorflow_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - found_conv_fusion = True - - for i in output_graph.graph_def.node: - if i.op == "Relu6": - found_conv_fusion = False - break - self.assertEqual(found_conv_fusion, True) - - @disable_random() - def test_single_conv3d_fusion(self): - logging.getLogger().info("test_single_conv3d_fusion") - x = tf.compat.v1.placeholder(tf.float32, [1, 64, 64, 64, 1], name="input") - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(x, paddings, "CONSTANT") - conv_weights = tf.compat.v1.get_variable( - "weight1", [4, 4, 4, 1, 64], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv3d(x_pad, conv_weights, strides=[1, 2, 2, 2, 1], padding="VALID", name="op_to_store") - - out_name = conv.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("tensorflow_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 64, 64, 64, 1), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - found_conv_fusion = False - - for i in output_graph.graph_def.node: - if i.op == "_FusedQuantizedConv3D": - found_conv_fusion = True - break - self.assertEqual(found_conv_fusion, True) - - @disable_random() - def test_conv3d_biasadd_fusion(self): - logging.getLogger().info("test_conv3d_biasadd_fusion") - x = tf.compat.v1.placeholder(tf.float32, [1, 64, 64, 64, 1], name="input") - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(x, paddings, "CONSTANT") - conv_weights = tf.compat.v1.get_variable( - "weight2", [4, 4, 4, 1, 64], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv3d(x_pad, conv_weights, strides=[1, 2, 2, 2, 1], padding="VALID") - relu6 = tf.nn.relu6(conv, name="op_to_store") - out_name = relu6.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("tensorflow_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 64, 64, 64, 1), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - found_conv_fusion = False - - for i in output_graph.graph_def.node: - if i.op == "_FusedQuantizedConv3D": - found_conv_fusion = True - break - self.assertEqual(found_conv_fusion, True) - - @disable_random() - def test_conv3d_add_relu_fusion(self): - logging.getLogger().info("test_conv3d_add_relu_fusion") - x = tf.compat.v1.placeholder(tf.float32, [1, 64, 64, 64, 1], name="input") - conv_weights = tf.compat.v1.get_variable( - "weight6", [4, 4, 4, 1, 64], initializer=tf.compat.v1.random_normal_initializer() - ) - conv1_weights = tf.compat.v1.get_variable( - "weight7", [4, 4, 4, 1, 64], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv3d(x, conv_weights, strides=[1, 2, 2, 2, 1], padding="VALID") - conv1 = tf.nn.conv3d(x, conv1_weights, strides=[1, 2, 2, 2, 1], padding="VALID") - add = conv + conv1 - relu = tf.nn.relu(add) - - out_name = relu.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("tensorflow_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 64, 64, 64, 1), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - found_conv_fusion = False - - for i in output_graph.graph_def.node: - if i.op == "_FusedQuantizedConv3D" and i.attr["fused_ops"].list.s == [b"BiasAdd", b"Dequantize"]: - found_conv_fusion = True - break - self.assertEqual(found_conv_fusion, True) - - @disable_random() - def test_conv2d_biasadd_elu_fusion(self): - input = tf.compat.v1.placeholder(tf.float32, shape=(1, 3, 3, 1), name="input") - weight = tf.compat.v1.constant(np.random.random((2, 2, 1, 1)).astype(np.float32), name="weight") - bias = tf.constant(np.random.random((1)), name="bias", dtype=tf.float32) - conv = tf.nn.conv2d(input=input, filters=weight, strides=[1, 1, 1, 1], padding="VALID", name="conv") - bias_add = tf.nn.bias_add(conv, bias, name="bias_add") - res = tf.nn.elu(bias_add, name="res") - output = tf.nn.softmax(res, name="op_to_store") - - out_name = output.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("tensorflow_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 3, 3, 1), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - self.assertNotEqual(output_graph, None) - elu_fused = False - for node in output_graph.graph_def.node: - if node.name == "conv_eightbit_requantize_dequantize": - if b"Elu" in node.attr["fused_ops"].list.s: - elu_fused = True - self.assertEqual(elu_fused, True) - - @disable_random() - def test_conv3d_add_const_fusion(self): - logging.getLogger().info("test_conv3d_add_const_fusion") - x = tf.compat.v1.placeholder(tf.float32, [1, 64, 64, 64, 1], name="input") - conv_weights = tf.compat.v1.get_variable( - "weight11", [4, 4, 4, 1, 64], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv3d(x, conv_weights, strides=[1, 2, 2, 2, 1], padding="VALID") - add = conv + tf.constant( - [ - [ - [ - [ - [ - 0.000015179151887423359, - 0.000022200847524800338, - -0.000009995766049541999, - -0.0000022956028260523453, - 0.000008830029400996864, - 0.0000017190360495078494, - 0.000019561824956326745, - 0.00014721050683874637, - -0.000005871841494808905, - 0.000004377178811409976, - -0.000006191140982991783, - 0.000009258330464945175, - -0.000009839599442784674, - 0.000008547322067897767, - 0.000004629391241905978, - 2.345327061448188e-7, - 0.000015179151887423359, - 0.000022200847524800338, - -0.000009995766049541999, - -0.0000022956028260523453, - 0.000008830029400996864, - 0.0000017190360495078494, - 0.000019561824956326745, - 0.00014721050683874637, - -0.000005871841494808905, - 0.000004377178811409976, - -0.000006191140982991783, - 0.000009258330464945175, - -0.000009839599442784674, - 0.000008547322067897767, - 0.000004629391241905978, - 2.345327061448188e-7, - 0.000015179151887423359, - 0.000022200847524800338, - -0.000009995766049541999, - -0.0000022956028260523453, - 0.000008830029400996864, - 0.0000017190360495078494, - 0.000019561824956326745, - 0.00014721050683874637, - -0.000005871841494808905, - 0.000004377178811409976, - -0.000006191140982991783, - 0.000009258330464945175, - -0.000009839599442784674, - 0.000008547322067897767, - 0.000004629391241905978, - 2.345327061448188e-7, - 0.000015179151887423359, - 0.000022200847524800338, - -0.000009995766049541999, - -0.0000022956028260523453, - 0.000008830029400996864, - 0.0000017190360495078494, - 0.000019561824956326745, - 0.00014721050683874637, - -0.000005871841494808905, - 0.000004377178811409976, - -0.000006191140982991783, - 0.000009258330464945175, - -0.000009839599442784674, - 0.000008547322067897767, - 0.000004629391241905978, - 2.345327061448188e-7, - ] - ] - ] - ] - ] - ) - - out_name = add.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("tensorflow_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 64, 64, 64, 1), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - found_conv_fusion = True - - for i in output_graph.graph_def.node: - if i.op == "AddV2": - found_conv_fusion = False - break - self.assertEqual(found_conv_fusion, True) - - @disable_random() - def test_conv_add_add_fusion(self): - logging.getLogger().info("test_conv_add_add_fusion") - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(x, paddings, "CONSTANT") - conv_weights = tf.compat.v1.get_variable( - "weight12", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv2d(x_pad, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - normed = tf.compat.v1.layers.batch_normalization(conv) - add = normed + tf.constant(np.random.randn(16), dtype=tf.float32) - relu6 = tf.nn.relu6(add, name="op_to_store") - - out_name = relu6.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("tensorflow_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - found_conv_fusion = True - - for i in output_graph.graph_def.node: - if i.op == "Add": - found_conv_fusion = False - break - self.assertEqual(found_conv_fusion, True) - - @disable_random() - def test_single_conv2d_fusion(self): - logging.getLogger().info("test_single_conv2d_fusion") - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(x, paddings, "CONSTANT") - conv_weights = tf.compat.v1.get_variable( - "weight13", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv2d(x_pad, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - out_name = conv.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("tensorflow_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - found_conv_fusion = False - - for i in output_graph.graph_def.node: - if i.op == "_FusedQuantizedConv2D": - found_conv_fusion = True - break - self.assertEqual(found_conv_fusion, True) - - @disable_random() - def test_conv3d_add_addn_const_relu_fusion(self): - logging.getLogger().info("test_conv3d_add_addn_const_relu_fusion") - x = tf.compat.v1.placeholder(tf.float32, [1, 128, 64, 64, 16], name="input") - top_relu = tf.nn.relu(x) - conv3d_1_weights = tf.compat.v1.get_variable( - "weight14", [3, 3, 3, 16, 32], initializer=tf.compat.v1.random_normal_initializer() - ) - conv3d_1 = tf.nn.conv3d(top_relu, conv3d_1_weights, strides=[1, 2, 2, 2, 1], padding="SAME") - add_1 = tf.raw_ops.AddV2(x=conv3d_1, y=tf.constant(np.random.randn(32), dtype=tf.float32), name="addv2") - var = tf.compat.v1.get_variable( - "add_y", [1, 64, 32, 32, 32], initializer=tf.compat.v1.random_normal_initializer() - ) - add = tf.raw_ops.AddV2(x=add_1, y=var, name="addv2_1") - relu = tf.nn.relu(add) - out_name = relu.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("tensorflow_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 128, 64, 64, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - - found_conv_sumadd_fusion = False - found_conv_biasadd_fusion = False - for i in output_graph.graph_def.node: - if i.op == "_FusedQuantizedConv3D": - if str(b"Sum") in str(i.attr["fused_ops"].list.s): - found_conv_sumadd_fusion = True - if str(i.attr["fused_ops"].list.s) == str([b"BiasAdd", b"Sum", b"Relu"]): - found_conv_biasadd_fusion = True - self.assertEqual(found_conv_sumadd_fusion, False) - self.assertEqual(found_conv_biasadd_fusion, False) - - @disable_random() - def test_conv3d_add_const_addn_relu_fusion(self): - logging.getLogger().info("test_conv3d_add_const_addn_relu_fusion") - x = tf.compat.v1.placeholder(tf.float32, [1, 128, 64, 64, 16], name="input") - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(x, paddings, "CONSTANT") - top_relu = tf.nn.relu(x_pad) - conv3d_1_weights = tf.compat.v1.get_variable( - "weight15", [3, 3, 3, 16, 32], initializer=tf.compat.v1.random_normal_initializer() - ) - conv3d_1 = tf.nn.conv3d(top_relu, conv3d_1_weights, strides=[1, 2, 2, 2, 1], padding="SAME") - add_1 = tf.raw_ops.AddV2(x=conv3d_1, y=tf.constant(np.random.randn(32), dtype=tf.float32), name="addv2_2") - conv3d_2_weights = tf.compat.v1.get_variable( - "weight16", [3, 3, 3, 16, 32], initializer=tf.compat.v1.random_normal_initializer() - ) - conv3d_2 = tf.nn.conv3d(top_relu, conv3d_2_weights, strides=[1, 2, 2, 2, 1], padding="SAME") - add = tf.raw_ops.AddV2(x=add_1, y=conv3d_2, name="addv2_3") - relu = tf.nn.relu(add) - out_name = relu.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("tensorflow_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 128, 64, 64, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - - found_conv_sumadd_fusion = False - found_conv_biasadd_fusion = False - for i in output_graph.graph_def.node: - if i.op == "_FusedQuantizedConv3D": - if str(b"Sum") in str(i.attr["fused_ops"].list.s): - found_conv_sumadd_fusion = True - if str(i.attr["fused_ops"].list.s) == str([b"BiasAdd", b"Sum", b"Relu"]): - found_conv_biasadd_fusion = True - self.assertEqual(found_conv_sumadd_fusion, True) - self.assertEqual(found_conv_biasadd_fusion, False) - - @disable_random() - def test_conv3d_add_addn_fusion(self): - logging.getLogger().info("test_conv3d_add_addn_fusion") - x = tf.compat.v1.placeholder(tf.float32, [1, 128, 64, 64, 16], name="input") - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(x, paddings, "CONSTANT") - top_relu = tf.nn.relu(x_pad) - conv3d_1_weights = tf.compat.v1.get_variable( - "weight15", [3, 3, 3, 16, 32], initializer=tf.compat.v1.random_normal_initializer() - ) - conv3d_1 = tf.nn.conv3d(top_relu, conv3d_1_weights, strides=[1, 2, 2, 2, 1], padding="SAME") - add_1 = tf.raw_ops.AddV2(x=conv3d_1, y=tf.constant(np.random.randn(32), dtype=tf.float32), name="addv2_4") - conv3d_2_weights = tf.compat.v1.get_variable( - "weight16", [3, 3, 3, 16, 32], initializer=tf.compat.v1.random_normal_initializer() - ) - conv3d_2 = tf.nn.conv3d(top_relu, conv3d_2_weights, strides=[1, 2, 2, 2, 1], padding="SAME") - add = tf.raw_ops.AddV2(x=add_1, y=conv3d_2, name="addv2_5") - out_name = add.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("tensorflow_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 128, 64, 64, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - - found_conv_fusion = False - for i in output_graph.graph_def.node: - if i.op == "_FusedQuantizedConv3D": - found_conv_fusion = True - self.assertEqual(found_conv_fusion, True) - - @disable_random() - def test_conv3d_add_addn_relu_fusion(self): - logging.getLogger().info("test_conv3d_add_addn_relu_fusion") - x = tf.compat.v1.placeholder(tf.float32, [1, 128, 64, 64, 16], name="input") - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(x, paddings, "CONSTANT") - top_relu = tf.nn.relu(x_pad) - conv3d_1_weights = tf.compat.v1.get_variable( - "weight17", [3, 3, 3, 16, 32], initializer=tf.compat.v1.random_normal_initializer() - ) - conv3d_2_weights = tf.compat.v1.get_variable( - "weight18", [3, 3, 3, 16, 32], initializer=tf.compat.v1.random_normal_initializer() - ) - conv3d_1 = tf.nn.conv3d(top_relu, conv3d_1_weights, strides=[1, 2, 2, 2, 1], padding="SAME") - conv3d_2 = tf.nn.conv3d(top_relu, conv3d_2_weights, strides=[1, 2, 2, 2, 1], padding="SAME") - add_1 = tf.raw_ops.AddV2(x=conv3d_1, y=conv3d_2, name="addv2_6") - conv3d_3_weights = tf.compat.v1.get_variable( - "weight19", [3, 3, 3, 16, 32], initializer=tf.compat.v1.random_normal_initializer() - ) - conv3d_2 = tf.nn.conv3d(top_relu, conv3d_3_weights, strides=[1, 2, 2, 2, 1], padding="SAME") - add = tf.raw_ops.AddV2(x=add_1, y=conv3d_2, name="addv2_7") - relu = tf.nn.relu(add) - out_name = relu.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("tensorflow_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 128, 64, 64, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - - found_relu_fusion = False - for i in output_graph.graph_def.node: - if i.op == "Relu": - found_relu_fusion = True - self.assertEqual(found_relu_fusion, True) - - @disable_random() - def test_conv3d_relu_fusion(self): - logging.getLogger().info("test_conv3d_relu_fusion") - x = tf.compat.v1.placeholder(tf.float32, [1, 64, 64, 64, 1], name="input") - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(x, paddings, "CONSTANT") - conv_weights = tf.compat.v1.get_variable( - "weight20", [4, 4, 4, 1, 64], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv3d(x_pad, conv_weights, strides=[1, 2, 2, 2, 1], padding="VALID") - relu = tf.nn.leaky_relu(conv) - - out_name = relu.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("tensorflow_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 64, 64, 64, 1), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - found_conv_fusion = False - - for i in output_graph.graph_def.node: - if i.op == "_FusedQuantizedConv3D": - found_conv_fusion = True - break - self.assertEqual(found_conv_fusion, True) - - @disable_random() - def test_conv3d_add_fusion(self): - logging.getLogger().info("test_conv3d_add_fusion") - x = tf.compat.v1.placeholder(tf.float32, [1, 128, 64, 64, 16], name="input") - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(x, paddings, "CONSTANT") - top_relu = tf.nn.relu(x_pad) - conv3d_1_weights = tf.compat.v1.get_variable( - "weight21", [3, 3, 3, 16, 32], initializer=tf.compat.v1.random_normal_initializer() - ) - conv3d_1 = tf.nn.conv3d(top_relu, conv3d_1_weights, strides=[1, 2, 2, 2, 1], padding="SAME") - conv3d_2_weights = tf.compat.v1.get_variable( - "weight22", [3, 3, 3, 16, 32], initializer=tf.compat.v1.random_normal_initializer() - ) - conv3d_2 = tf.nn.conv3d(top_relu, conv3d_2_weights, strides=[1, 2, 2, 2, 1], padding="SAME") - add = tf.raw_ops.AddV2(x=conv3d_1, y=conv3d_2, name="addv2_8") - out_name = add.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("tensorflow_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 128, 64, 64, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - - found_conv_fusion = False - for i in output_graph.graph_def.node: - if i.op == "_FusedQuantizedConv3D": - found_conv_fusion = True - self.assertEqual(found_conv_fusion, True) - - @disable_random() - def test_conv3d_add_const_addn_relu_requantize_fusion(self): - logging.getLogger().info("test_conv3d_add_const_addn_relu_requantize_fusion") - x = tf.compat.v1.placeholder(tf.float32, [1, 128, 64, 64, 16], name="input") - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(x, paddings, "CONSTANT") - top_relu = tf.nn.relu(x_pad) - conv3d_1_weights = tf.compat.v1.get_variable( - "weight23", [3, 3, 3, 16, 32], initializer=tf.compat.v1.random_normal_initializer() - ) - conv3d_1 = tf.nn.conv3d(top_relu, conv3d_1_weights, strides=[1, 2, 2, 2, 1], padding="SAME") - y_const = tf.constant(np.random.randn(1, 1, 1, 1, 32), dtype=tf.float32) - add_1 = tf.raw_ops.AddV2(x=conv3d_1, y=y_const, name="addv2_9") - conv3d_2_weights = tf.compat.v1.get_variable( - "weight24", [3, 3, 3, 16, 32], initializer=tf.compat.v1.random_normal_initializer() - ) - conv3d_2 = tf.nn.conv3d(top_relu, conv3d_2_weights, strides=[1, 2, 2, 2, 1], padding="SAME") - add_2 = tf.raw_ops.AddV2(x=add_1, y=conv3d_2, name="addv2_10") - relu = tf.nn.relu(add_2) - out_name = relu.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("tensorflow_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 128, 64, 64, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - - found_conv_sumadd_fusion = False - found_conv_biasadd_fusion = False - for i in output_graph.graph_def.node: - if i.op == "_FusedQuantizedConv3D": - if str(b"Sum") in str(i.attr["fused_ops"].list.s): - found_conv_sumadd_fusion = True - if str(i.attr["fused_ops"].list.s) == str([b"BiasAdd", b"Sum", b"Relu", b"Requantize"]): - found_conv_biasadd_fusion = True - self.assertEqual(found_conv_sumadd_fusion, True) - self.assertEqual(found_conv_biasadd_fusion, True) - - @disable_random() - def test_conv3d_add_const_addn_fusion(self): - logging.getLogger().info("test_conv3d_add_const_addn_fusion") - x = tf.compat.v1.placeholder(tf.float32, [1, 128, 64, 64, 16], name="input") - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(x, paddings, "CONSTANT") - top_relu = tf.nn.relu(x_pad) - conv3d_1_weights = tf.compat.v1.get_variable( - "weight25", [3, 3, 3, 16, 32], initializer=tf.compat.v1.random_normal_initializer() - ) - conv3d_1 = tf.nn.conv3d(top_relu, conv3d_1_weights, strides=[1, 2, 2, 2, 1], padding="SAME") - y_const = tf.constant(np.random.randn(1, 1, 1, 1, 32), dtype=tf.float32) - add_1 = tf.raw_ops.AddV2(x=conv3d_1, y=y_const, name="addv2_11") - conv3d_2_weights = tf.compat.v1.get_variable( - "weight26", [3, 3, 3, 16, 32], initializer=tf.compat.v1.random_normal_initializer() - ) - conv3d_2 = tf.nn.conv3d(top_relu, conv3d_2_weights, strides=[1, 2, 2, 2, 1], padding="SAME") - add_2 = tf.raw_ops.AddV2(x=add_1, y=conv3d_2, name="addv2_12") - out_name = add_2.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("tensorflow_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 128, 64, 64, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - - found_conv_fusion = False - for i in output_graph.graph_def.node: - if i.op == "_FusedQuantizedConv3D": - found_conv_fusion = True - self.assertEqual(found_conv_fusion, True) - - @disable_random() - def test_conv3d_add_no_relu_fusion(self): - logging.getLogger().info("test_conv3d_add_no_relu_fusion") - x = tf.compat.v1.placeholder(tf.float32, [1, 128, 64, 64, 16], name="input") - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(x, paddings, "CONSTANT") - top_relu = tf.nn.relu(x_pad) - conv3d_1_weights = tf.compat.v1.get_variable( - "weight27", [3, 3, 3, 16, 32], initializer=tf.compat.v1.random_normal_initializer() - ) - conv3d_1 = tf.nn.conv3d(top_relu, conv3d_1_weights, strides=[1, 2, 2, 2, 1], padding="SAME") - y_const = tf.constant(np.random.randn(1, 1, 1, 1, 32), dtype=tf.float32) - add = tf.raw_ops.AddV2(x=conv3d_1, y=y_const, name="addv2_13") - pooling = tf.nn.max_pool(add, ksize=1, strides=[1, 2, 2, 2, 1], padding="SAME") - out_name = pooling.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("tensorflow_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 128, 64, 64, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - - for i in output_graph.graph_def.node: - if i.op == "_FusedQuantizedConv3D": - found_conv_fusion = True - break - self.assertEqual(found_conv_fusion, True) - - @disable_random() - def test_conv3d_add_const_relu_fusion(self): - logging.getLogger().info("test_conv3d_add_const_relu_fusion") - x = tf.compat.v1.placeholder(tf.float32, [1, 128, 64, 64, 16], name="input") - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(x, paddings, "CONSTANT") - top_relu = tf.nn.relu(x_pad) - conv3d_1_weights = tf.compat.v1.get_variable( - "weight28", [3, 3, 3, 16, 32], initializer=tf.compat.v1.random_normal_initializer() - ) - conv3d_1 = tf.nn.conv3d(top_relu, conv3d_1_weights, strides=[1, 2, 2, 2, 1], padding="SAME") - y_const = tf.constant(np.random.randn(1, 1, 1, 1, 32), dtype=tf.float32) - add = tf.raw_ops.AddV2(x=conv3d_1, y=y_const, name="addv2_10") - relu = tf.nn.relu(add) - out_name = relu.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("tensorflow_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 128, 64, 64, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - - for i in output_graph.graph_def.node: - if i.op == "_FusedQuantizedConv3D": - found_conv_fusion = True - break - self.assertEqual(found_conv_fusion, True) - - @disable_random() - def test_conv2d_add_const_leakyrelu_add_fusion(self): - logging.getLogger().info("test_conv2d_add_const_leakyrelu_add_fusion") - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(x, paddings, "CONSTANT") - top_relu = tf.nn.relu(x_pad) - conv2d_1_weights = tf.compat.v1.get_variable( - "weight29", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv2d_1 = tf.nn.conv2d(top_relu, conv2d_1_weights, strides=[1, 2, 2, 1], padding="SAME") - y_const = tf.constant(np.random.randn(16), dtype=tf.float32) - add_1 = tf.raw_ops.AddV2(x=conv2d_1, y=y_const, name="addv2_11") - relu = tf.nn.leaky_relu(add_1) - conv2d_2_weights = tf.compat.v1.get_variable( - "weight30", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv2d_2 = tf.nn.conv2d(top_relu, conv2d_2_weights, strides=[1, 2, 2, 1], padding="SAME") - add_2 = tf.raw_ops.AddV2(x=relu, y=conv2d_2, name="addv2_12") - out_name = add_2.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("tensorflow_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - found_conv_fusion = False - for i in output_graph.graph_def.node: - if i.op == "_FusedQuantizedConv2D": - found_conv_fusion = True - self.assertEqual(found_conv_fusion, True) - - @disable_random() - def test_conv3d_add_const_leakyrelu_add_fusion(self): - logging.getLogger().info("test_conv3d_add_const_leakyrelu_add_fusion") - x = tf.compat.v1.placeholder(tf.float32, [1, 128, 64, 64, 16], name="input") - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(x, paddings, "CONSTANT") - top_relu = tf.nn.relu(x_pad) - conv3d_1_weights = tf.compat.v1.get_variable( - "weight31", [3, 3, 3, 16, 32], initializer=tf.compat.v1.random_normal_initializer() - ) - conv3d_1 = tf.nn.conv3d(top_relu, conv3d_1_weights, strides=[1, 2, 2, 2, 1], padding="SAME") - y_const = tf.constant(np.random.randn(1, 1, 1, 1, 32), dtype=tf.float32) - add_1 = tf.raw_ops.AddV2(x=conv3d_1, y=y_const, name="addv2_13") - relu = tf.nn.leaky_relu(add_1) - conv3d_2_weights = tf.compat.v1.get_variable( - "weight32", [3, 3, 3, 16, 32], initializer=tf.compat.v1.random_normal_initializer() - ) - conv3d_2 = tf.nn.conv3d(top_relu, conv3d_2_weights, strides=[1, 2, 2, 2, 1], padding="SAME") - add_2 = tf.raw_ops.AddV2(x=relu, y=conv3d_2, name="addv2_14") - out_name = add_2.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("tensorflow_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 128, 64, 64, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - found_conv_fusion = False - for i in output_graph.graph_def.node: - if i.op == "_FusedQuantizedConv3D": - found_conv_fusion = True - self.assertEqual(found_conv_fusion, True) - - @disable_random() - def test_conv3d_add_addn_non_const_fusion(self): - logging.getLogger().info("test_conv3d_add_addn_non_const_fusion") - x = tf.compat.v1.placeholder(tf.float32, [1, 128, 64, 64, 16], name="input") - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(x, paddings, "CONSTANT") - top_relu = tf.nn.relu(x_pad) - conv3d_1_weights = tf.compat.v1.get_variable( - "weight33", [3, 3, 3, 16, 32], initializer=tf.compat.v1.random_normal_initializer() - ) - conv3d_1 = tf.nn.conv3d(top_relu, conv3d_1_weights, strides=[1, 2, 2, 2, 1], padding="SAME") - conv3d_2_weights = tf.compat.v1.get_variable( - "weight34", [3, 3, 3, 16, 32], initializer=tf.compat.v1.random_normal_initializer() - ) - conv3d_2 = tf.nn.conv3d(top_relu, conv3d_2_weights, strides=[1, 2, 2, 2, 1], padding="SAME") - add_1 = tf.raw_ops.AddV2(x=conv3d_1, y=conv3d_2, name="addv2_15") - conv3d_3_weights = tf.compat.v1.get_variable( - "weight35", [3, 3, 3, 16, 32], initializer=tf.compat.v1.random_normal_initializer() - ) - conv3d_3 = tf.nn.conv3d(top_relu, conv3d_3_weights, strides=[1, 2, 2, 2, 1], padding="SAME") - add = tf.raw_ops.AddV2(x=add_1, y=conv3d_3, name="addv2_16") - out_name = add.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("tensorflow_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 128, 64, 64, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - - found_conv_fusion = False - for i in output_graph.graph_def.node: - if i.op == "_FusedQuantizedConv3D": - found_conv_fusion = True - self.assertEqual(found_conv_fusion, True) - - @disable_random() - def test_conv3d_add_const_elu_add_fusion(self): - logging.getLogger().info("test_conv3d_add_const_elufusion") - x = tf.compat.v1.placeholder(tf.float32, [1, 128, 64, 64, 16], name="input") - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(x, paddings, "CONSTANT") - top_relu = tf.nn.relu(x_pad) - conv3d_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 3, 16, 32], initializer=tf.compat.v1.random_normal_initializer() - ) - conv3d = tf.nn.conv3d(top_relu, conv3d_weights, strides=[1, 2, 2, 2, 1], padding="SAME") - y_const = tf.constant(np.random.randn(1, 1, 1, 1, 32), dtype=tf.float32) - add = tf.raw_ops.AddV2(x=conv3d, y=y_const, name="addv2") - elu = tf.nn.elu(add) - output = tf.nn.softmax(elu, name="op_to_store") - out_name = output.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("tensorflow_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 128, 64, 64, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - found_conv_fusion = False - for i in output_graph.graph_def.node: - if i.op == "_FusedQuantizedConv3D": - found_conv_fusion = True - self.assertEqual(found_conv_fusion, True) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/tfnewapi/test_tensorflow_graph_depthwiseconv_fusion.py b/test/tfnewapi/test_tensorflow_graph_depthwiseconv_fusion.py deleted file mode 100644 index 2eaf6efe3b1..00000000000 --- a/test/tfnewapi/test_tensorflow_graph_depthwiseconv_fusion.py +++ /dev/null @@ -1,271 +0,0 @@ -# -# -*- coding: utf-8 -*- -# -import os -import unittest - -import numpy as np -import tensorflow as tf -import yaml -from tensorflow.compat.v1 import graph_util -from tensorflow.core.framework import attr_value_pb2, graph_pb2, node_def_pb2 -from tensorflow.python.framework import dtypes, tensor_util - -from neural_compressor.adaptor.tf_utils.util import disable_random - - -def build_fake_yaml(): - fake_yaml = """ - model: - name: fake_yaml - framework: tensorflow - inputs: input - device: cpu - quantization: - model_wise: - weight: - granularity: per_tensor - scheme: sym - dtype: int8 - algorithm: minmax - evaluation: - accuracy: - metric: - topk: 1 - tuning: - strategy: - name: basic - accuracy_criterion: - relative: 0.1 - exit_policy: - performance_only: True - workspace: - path: saved - """ - y = yaml.load(fake_yaml, Loader=yaml.SafeLoader) - with open("fake_yaml.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - f.close() - - -def build_Conv2dBiasAddAddRelu6MulMul(): - input_node = node_def_pb2.NodeDef() - input_node.name = "input" - input_node.op = "Placeholder" - input_node.attr["dtype"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - - conv1_weight_node = node_def_pb2.NodeDef() - conv1_weight_node.name = "conv1_weights" - conv1_weight_node.op = "Const" - conv1_weight_value = np.float32(np.abs(np.random.randn(3, 3, 3, 32))) - conv1_weight_node.attr["dtype"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - conv1_weight_node.attr["value"].CopyFrom( - attr_value_pb2.AttrValue( - tensor=tensor_util.make_tensor_proto( - conv1_weight_value, conv1_weight_value.dtype.type, conv1_weight_value.shape - ) - ) - ) - - conv1_node = node_def_pb2.NodeDef() - conv1_node.name = "conv1" - conv1_node.op = "Conv2D" - conv1_node.attr["T"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - conv1_node.input.extend([input_node.name, conv1_weight_node.name]) - conv1_node.attr["strides"].CopyFrom( - attr_value_pb2.AttrValue(list=attr_value_pb2.AttrValue.ListValue(i=[1, 1, 1, 1])) - ) - conv1_node.attr["dilations"].CopyFrom( - attr_value_pb2.AttrValue(list=attr_value_pb2.AttrValue.ListValue(i=[1, 1, 1, 1])) - ) - conv1_node.attr["padding"].CopyFrom(attr_value_pb2.AttrValue(s=b"SAME")) - conv1_node.attr["data_format"].CopyFrom(attr_value_pb2.AttrValue(s=b"NHWC")) - - bias_node = node_def_pb2.NodeDef() - bias_node.name = "conv1_bias" - bias_node.op = "Const" - bias_value = np.float32(np.abs(np.random.randn(32))) - bias_node.attr["dtype"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - bias_node.attr["value"].CopyFrom( - attr_value_pb2.AttrValue( - tensor=tensor_util.make_tensor_proto(bias_value, bias_value.dtype.type, bias_value.shape) - ) - ) - - bias_add_node = node_def_pb2.NodeDef() - bias_add_node.name = "conv1_bias_add" - bias_add_node.op = "BiasAdd" - bias_add_node.attr["T"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - bias_add_node.attr["data_format"].CopyFrom(attr_value_pb2.AttrValue(s=b"NHWC")) - bias_add_node.input.extend([conv1_node.name, bias_node.name]) - - offset_node = node_def_pb2.NodeDef() - offset_node.name = "offset" - offset_node.op = "Const" - offset_value = np.float32(np.abs(np.random.randn(1))) - offset_node.attr["dtype"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - offset_node.attr["value"].CopyFrom( - attr_value_pb2.AttrValue( - tensor=tensor_util.make_tensor_proto(offset_value, offset_value.dtype.type, offset_value.shape) - ) - ) - - add_node = node_def_pb2.NodeDef() - add_node.op = "Add" - add_node.name = "add/hard_swish" - add_node.attr["T"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - add_node.input.extend([bias_add_node.name, offset_node.name]) - - relu_node = node_def_pb2.NodeDef() - relu_node.op = "Relu6" - relu_node.name = "relu6/hard_swish" - relu_node.attr["T"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - relu_node.input.extend([add_node.name]) - - mul_node = node_def_pb2.NodeDef() - mul_node.op = "Mul" - mul_node.name = "mul/hard_swish" - mul_node.attr["T"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - mul_node.input.extend([bias_add_node.name, relu_node.name]) - - offset1_node = node_def_pb2.NodeDef() - offset1_node.name = "mul1_offset" - offset1_node.op = "Const" - offset1_value = np.float32(np.abs(np.random.randn(1))) - offset1_node.attr["dtype"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - offset1_node.attr["value"].CopyFrom( - attr_value_pb2.AttrValue( - tensor=tensor_util.make_tensor_proto(offset1_value, offset1_value.dtype.type, offset1_value.shape) - ) - ) - - mul1_node = node_def_pb2.NodeDef() - mul1_node.op = "Mul" - mul1_node.name = "mul1/hard_swish" - mul1_node.attr["T"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - mul1_node.input.extend([mul_node.name, offset1_node.name]) - - test_graph = graph_pb2.GraphDef() - - test_graph.node.extend( - [ - input_node, - conv1_weight_node, - conv1_node, - bias_node, - bias_add_node, - add_node, - relu_node, - offset_node, - offset1_node, - mul_node, - mul1_node, - ] - ) - return test_graph - - -class TestConvBiasAddAddReluFusion(unittest.TestCase): - @classmethod - def setUpClass(self): - build_fake_yaml() - - @classmethod - def tearDownClass(self): - os.remove("fake_yaml.yaml") - - @disable_random() - def test_depthwiseconv_biasadd_fusion(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.depthwise_conv2d(x, conv_weights, strides=[1, 1, 1, 1], padding="VALID") - - normed = tf.compat.v1.layers.batch_normalization(conv, name="op_to_store") - out_name = normed.name.split(":")[0] - - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - found_conv_fusion = False - - for i in output_graph.graph_def.node: - if i.op == "_FusedQuantizedDepthwiseConv2D": - found_conv_fusion = True - break - - self.assertEqual(found_conv_fusion, True) - - @disable_random() - def test_depthwiseConv2dNative_BiasAddAddRelu6MulMul_fusion(self): - output_graph_def = build_Conv2dBiasAddAddRelu6MulMul() - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 224, 224, 3), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - - found_conv_fusion = False - - for i in output_graph.graph_def.node: - if i.op == "_FusedQuantizedConv2D": - found_conv_fusion = True - break - - self.assertEqual(found_conv_fusion, True) - - @disable_random() - def test_depthwiseconv_biasadd_leakyrelu_fusion(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.depthwise_conv2d(x, conv_weights, strides=[1, 1, 1, 1], padding="VALID") - - normed = tf.compat.v1.layers.batch_normalization(conv, name="op_to_store") - - leakyrelu = tf.nn.leaky_relu(normed) - out_name = leakyrelu.name.split(":")[0] - - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - found_conv_fusion = False - - for i in output_graph.graph_def.node: - if i.op == "_FusedQuantizedDepthwiseConv2D": - found_conv_fusion = True - break - - self.assertEqual(found_conv_fusion, True) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/tfnewapi/test_tensorflow_graph_dequantize_cast_optimizer_newapi.py b/test/tfnewapi/test_tensorflow_graph_dequantize_cast_optimizer_newapi.py deleted file mode 100644 index 376e0e6dc0b..00000000000 --- a/test/tfnewapi/test_tensorflow_graph_dequantize_cast_optimizer_newapi.py +++ /dev/null @@ -1,92 +0,0 @@ -import os -import unittest - -import numpy as np -import tensorflow as tf -import yaml -from tensorflow.python.framework import dtypes - -from neural_compressor.adaptor.tf_utils.graph_rewriter.bf16.dequantize_cast_optimizer import DequantizeCastOptimizer -from neural_compressor.adaptor.tf_utils.graph_util import GraphRewriterHelper as Helper -from neural_compressor.adaptor.tf_utils.util import disable_random - - -def build_fake_graphdef(set_min_first=False, dq_multi_outputs=False): - tf.compat.v1.disable_eager_execution() - - input = tf.compat.v1.placeholder(tf.float32, shape=(32, 224, 224, 3), name="input") - graph_def = tf.compat.v1.get_default_graph().as_graph_def(add_shapes=True) - - min_input = Helper.create_constant_node("test_min", value=0.0, dtype=dtypes.float32) - - max_input = Helper.create_constant_node("test_max", value=[1], dtype=dtypes.float32) - - quant_v2_node = Helper.create_node("QuantizeV2", "test_quantize", [input.name, min_input.name, max_input.name]) - - dequantize_node = Helper.create_node( - "Dequantize", "test_dequantize", [quant_v2_node.name, quant_v2_node.name + ":1", quant_v2_node.name + ":2"] - ) - if set_min_first: - Helper.set_attr_string(dequantize_node, "mode", b"MIN_FIRST") - - cast_node = Helper.create_node("Cast", "test_cast", [dequantize_node.name]) - Helper.set_attr_dtype(cast_node, "DstT", dtypes.bfloat16) - Helper.set_attr_dtype(cast_node, "SrcT", dtypes.float32) - Helper.set_attr_bool(cast_node, "Truncate", False) - - dentity_node = Helper.create_node("Identity", "output", [cast_node.name]) - Helper.set_attr_dtype(dentity_node, "T", dtypes.bfloat16) - - graph_def.node.extend( - [ - min_input, - max_input, - quant_v2_node, - dequantize_node, - cast_node, - dentity_node, - ] - ) - - if dq_multi_outputs: - dentity_node_2 = Helper.create_node("Identity", "id_1", [dequantize_node.name]) - Helper.set_attr_dtype(dentity_node_2, "T", dtypes.float32) - graph_def.node.extend([dentity_node_2]) - - return graph_def - - -class TestDequantizeCastOptimizer(unittest.TestCase): - @disable_random() - def test_dequantize_cast_normal(self): - graph_def = build_fake_graphdef() - converted_graph_def = DequantizeCastOptimizer(graph_def).do_transformation() - for i in converted_graph_def.node: - self.assertNotEqual(i.op, "Cast") - - @disable_random() - def test_dequantize_cast_min_first(self): - graph_def = build_fake_graphdef(set_min_first=True) - converted_graph_def = DequantizeCastOptimizer(graph_def).do_transformation() - hasCast = False - # Remove MIN_FIRST limitation for spr-base, so the "Cast" will be removed now - for i in converted_graph_def.node: - if i.op == "Cast": - hasCast = True - break - self.assertEqual(hasCast, False) - - @disable_random() - def test_dequantize_cast_multiple_outputs(self): - graph_def = build_fake_graphdef(dq_multi_outputs=True) - converted_graph_def = DequantizeCastOptimizer(graph_def).do_transformation() - hasCast = False - for i in converted_graph_def.node: - if i.op == "Cast": - hasCast = True - break - self.assertEqual(hasCast, True) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/tfnewapi/test_tensorflow_graph_dq_cast_fusion.py b/test/tfnewapi/test_tensorflow_graph_dq_cast_fusion.py deleted file mode 100644 index 85e9fb9aed7..00000000000 --- a/test/tfnewapi/test_tensorflow_graph_dq_cast_fusion.py +++ /dev/null @@ -1,89 +0,0 @@ -import os -import unittest - -import numpy as np -import tensorflow as tf -import yaml -from tensorflow.compat.v1 import graph_util - -from neural_compressor.adaptor.tf_utils.util import disable_random - - -def build_fake_yaml(): - fake_yaml = """ - model: - name: fake_yaml - framework: tensorflow - inputs: input - outputs: op_to_store - device: cpu - quantization: - model_wise: - weight: - granularity: per_tensor - scheme: sym - dtype: int8 - algorithm: minmax - evaluation: - accuracy: - metric: - topk: 1 - tuning: - strategy: - name: mse - accuracy_criterion: - relative: 0.01 - exit_policy: - performance_only: True - workspace: - path: saved - """ - y = yaml.load(fake_yaml, Loader=yaml.SafeLoader) - with open("fake_yaml.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - f.close() - - -class TestDqCastFusion(unittest.TestCase): - @classmethod - def setUpClass(self): - build_fake_yaml() - os.environ["FORCE_BF16"] = "1" - - @classmethod - def tearDownClass(self): - os.remove("fake_yaml.yaml") - - @disable_random() - def test_dq_all_outputs_bf16(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - conv_weights = tf.constant(np.random.random((1, 3, 16, 16)).astype(np.float32), name="y") - conv = tf.nn.conv2d(x, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - conv_reshape1 = tf.reshape(conv, [1, 28, 27, 16]) - conv_reshape2 = tf.reshape(conv, [1, 28, 27, 16]) - out = tf.math.add(conv_reshape1, conv_reshape2, name="op_to_store") - out_name = out.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16)) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - found_cast = False - for node in output_graph.graph_def.node: - if node.op == "Cast": - found_cast = True - break - self.assertEqual(found_cast, False) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/tfnewapi/test_tensorflow_graph_fuse_gelu_newapi.py b/test/tfnewapi/test_tensorflow_graph_fuse_gelu_newapi.py deleted file mode 100644 index f14f7d70fbe..00000000000 --- a/test/tfnewapi/test_tensorflow_graph_fuse_gelu_newapi.py +++ /dev/null @@ -1,413 +0,0 @@ -# -# -*- coding: utf-8 -*- -# -import unittest - -import tensorflow as tf -from tensorflow.compat.v1 import graph_util - -from neural_compressor.adaptor.tf_utils.graph_rewriter.generic.fuse_gelu import FuseGeluOptimizer -from neural_compressor.adaptor.tf_utils.util import disable_random - - -class TestGeluFusion(unittest.TestCase): - def gelu(self, input_tensor, mul_value=0.5, addv2_value=1.0, sqrt_value=2.0): - cdf = mul_value * (addv2_value + tf.math.erf(input_tensor / tf.sqrt(sqrt_value))) - return input_tensor * cdf - - def gelu_enable_approximation( - self, - input_tensor, - another_mul_value=0.5, - mul1_value=0.044715, - addv2_value=1.0, - mul2_value=0.7978845608028654, - pow_value=3, - ): - coeff = tf.cast(mul1_value, input_tensor.dtype) - return ( - another_mul_value - * input_tensor - * (addv2_value + tf.tanh(mul2_value * (input_tensor + coeff * tf.pow(input_tensor, pow_value)))) - ) - - def gelu_enable_approximation_varaint( - self, - input_tensor, - another_mul_value=0.5, - mul1_value=0.044715, - addv2_value=1.0, - mul2_value=0.7978845608028654, - pow_value=3, - ): - coeff = tf.cast(mul1_value, input_tensor.dtype) - cdf = another_mul_value * ( - addv2_value + tf.tanh(mul2_value * (input_tensor + coeff * tf.pow(input_tensor, pow_value))) - ) - - return input_tensor * cdf - - def gelu_disable_approximation( - self, - input_tensor, - another_add_value=0.5, - mul1_value=0.044715, - addv2_value=1.0, - mul2_value=0.7978845608028654, - pow_value=3, - ): - coeff = tf.cast(mul1_value, input_tensor.dtype) - return (another_add_value + input_tensor) * ( - addv2_value + tf.tanh(mul2_value * (input_tensor + coeff * tf.pow(input_tensor, pow_value))) - ) - - @disable_random() - def test_gelu_disable_approximation_fusion(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 224, 224, 3], name="input") - - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 3, 32], initializer=tf.compat.v1.random_normal_initializer() - ) - conv_bias = tf.compat.v1.get_variable("bias", [32], initializer=tf.compat.v1.random_normal_initializer()) - conv1 = tf.nn.conv2d(x, conv_weights, strides=[1, 1, 1, 1], padding="SAME") - conv_bias = tf.math.add(conv1, conv_bias) - - gelu = self.gelu_disable_approximation(conv_bias) - relu = tf.nn.relu(gelu) - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[relu.name.split(":")[0]] - ) - - output_graph_def = FuseGeluOptimizer(output_graph_def).do_transformation() - - found_gelu = False - for i in output_graph_def.node: - if i.op == "Gelu": - found_gelu = True - break - - self.assertEqual(found_gelu, False) - - @disable_random() - def test_gelu_approximation_fusion(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 224, 224, 3], name="input") - - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 3, 32], initializer=tf.compat.v1.random_normal_initializer() - ) - conv_bias = tf.compat.v1.get_variable("bias", [32], initializer=tf.compat.v1.random_normal_initializer()) - conv1 = tf.nn.conv2d(x, conv_weights, strides=[1, 1, 1, 1], padding="SAME") - conv_bias = tf.math.add(conv1, conv_bias) - - gelu = self.gelu_enable_approximation(conv_bias) - relu = tf.nn.relu(gelu) - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[relu.name.split(":")[0]] - ) - - output_graph_def = FuseGeluOptimizer(output_graph_def).do_transformation() - - found_gelu = False - for i in output_graph_def.node: - if i.op == "Gelu": - found_gelu = True - break - - self.assertEqual(found_gelu, True) - - @disable_random() - def test_gelu_approximation_fusion_varaint(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 224, 224, 3], name="input") - - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 3, 32], initializer=tf.compat.v1.random_normal_initializer() - ) - conv_bias = tf.compat.v1.get_variable("bias", [32], initializer=tf.compat.v1.random_normal_initializer()) - conv1 = tf.nn.conv2d(x, conv_weights, strides=[1, 1, 1, 1], padding="SAME") - conv_bias = tf.math.add(conv1, conv_bias) - - gelu = self.gelu_enable_approximation_varaint(conv_bias) - relu = tf.nn.relu(gelu) - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[relu.name.split(":")[0]] - ) - - output_graph_def = FuseGeluOptimizer(output_graph_def).do_transformation() - - found_gelu = False - for i in output_graph_def.node: - if i.op == "Gelu": - found_gelu = True - break - - self.assertEqual(found_gelu, True) - - @disable_random() - def test_gelu_approximation_fusion_with_invalid_pow_value(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 224, 224, 3], name="input") - - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 3, 32], initializer=tf.compat.v1.random_normal_initializer() - ) - conv_bias = tf.compat.v1.get_variable("bias", [32], initializer=tf.compat.v1.random_normal_initializer()) - conv1 = tf.nn.conv2d(x, conv_weights, strides=[1, 1, 1, 1], padding="SAME") - conv_bias = tf.math.add(conv1, conv_bias) - - gelu = self.gelu_enable_approximation(conv_bias, pow_value=1.0) - relu = tf.nn.relu(gelu) - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[relu.name.split(":")[0]] - ) - - output_graph_def = FuseGeluOptimizer(output_graph_def).do_transformation() - - found_gelu = False - for i in output_graph_def.node: - if i.op == "Gelu": - found_gelu = True - break - - self.assertEqual(found_gelu, False) - - @disable_random() - def test_gelu_approximation_fusion_with_invalid_mul2_value(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 224, 224, 3], name="input") - - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 3, 32], initializer=tf.compat.v1.random_normal_initializer() - ) - conv_bias = tf.compat.v1.get_variable("bias", [32], initializer=tf.compat.v1.random_normal_initializer()) - conv1 = tf.nn.conv2d(x, conv_weights, strides=[1, 1, 1, 1], padding="SAME") - conv_bias = tf.math.add(conv1, conv_bias) - - gelu = self.gelu_enable_approximation(conv_bias, mul2_value=1.0) - relu = tf.nn.relu(gelu) - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[relu.name.split(":")[0]] - ) - - output_graph_def = FuseGeluOptimizer(output_graph_def).do_transformation() - - found_gelu = False - for i in output_graph_def.node: - if i.op == "Gelu": - found_gelu = True - break - - self.assertEqual(found_gelu, False) - - @disable_random() - def test_gelu_approximation_fusion_with_invalid_addv2_value(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 224, 224, 3], name="input") - - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 3, 32], initializer=tf.compat.v1.random_normal_initializer() - ) - conv_bias = tf.compat.v1.get_variable("bias", [32], initializer=tf.compat.v1.random_normal_initializer()) - conv1 = tf.nn.conv2d(x, conv_weights, strides=[1, 1, 1, 1], padding="SAME") - conv_bias = tf.math.add(conv1, conv_bias) - - gelu = self.gelu_enable_approximation(conv_bias, addv2_value=12.0) - relu = tf.nn.relu(gelu) - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[relu.name.split(":")[0]] - ) - - output_graph_def = FuseGeluOptimizer(output_graph_def).do_transformation() - - found_gelu = False - for i in output_graph_def.node: - if i.op == "Gelu": - found_gelu = True - break - - self.assertEqual(found_gelu, False) - - @disable_random() - def test_gelu_approximation_fusion_with_invalid_mul1_value(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 224, 224, 3], name="input") - - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 3, 32], initializer=tf.compat.v1.random_normal_initializer() - ) - conv_bias = tf.compat.v1.get_variable("bias", [32], initializer=tf.compat.v1.random_normal_initializer()) - conv1 = tf.nn.conv2d(x, conv_weights, strides=[1, 1, 1, 1], padding="SAME") - conv_bias = tf.math.add(conv1, conv_bias) - - gelu = self.gelu_enable_approximation(conv_bias, mul1_value=1.0) - relu = tf.nn.relu(gelu) - - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[relu.name.split(":")[0]] - ) - - output_graph_def = FuseGeluOptimizer(output_graph_def).do_transformation() - - found_gelu = False - for i in output_graph_def.node: - if i.op == "Gelu": - found_gelu = True - break - - self.assertEqual(found_gelu, False) - - @disable_random() - def test_gelu_approximation_fusion_with_invalid_another_mul(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 224, 224, 3], name="input") - - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 3, 32], initializer=tf.compat.v1.random_normal_initializer() - ) - conv_bias = tf.compat.v1.get_variable("bias", [32], initializer=tf.compat.v1.random_normal_initializer()) - conv1 = tf.nn.conv2d(x, conv_weights, strides=[1, 1, 1, 1], padding="SAME") - conv_bias = tf.math.add(conv1, conv_bias) - - gelu = self.gelu_enable_approximation(conv_bias, another_mul_value=1.0) - relu = tf.nn.relu(gelu) - - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[relu.name.split(":")[0]] - ) - - output_graph_def = FuseGeluOptimizer(output_graph_def).do_transformation() - - found_gelu = False - for i in output_graph_def.node: - if i.op == "Gelu": - found_gelu = True - break - - self.assertEqual(found_gelu, False) - - @disable_random() - def test_gelu_fusion_with_invalid_sqrt(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 224, 224, 3], name="input") - - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 3, 32], initializer=tf.compat.v1.random_normal_initializer() - ) - conv_bias = tf.compat.v1.get_variable("bias", [32], initializer=tf.compat.v1.random_normal_initializer()) - conv1 = tf.nn.conv2d(x, conv_weights, strides=[1, 1, 1, 1], padding="SAME") - conv_bias = tf.math.add(conv1, conv_bias) - - gelu = self.gelu(conv_bias, sqrt_value=1.0) - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[gelu.name.split(":")[0]] - ) - - output_graph_def = FuseGeluOptimizer(output_graph_def).do_transformation() - - found_gelu = False - for i in output_graph_def.node: - if i.op == "Gelu": - found_gelu = True - break - - self.assertEqual(found_gelu, False) - - @disable_random() - def test_gelu_fusion_with_invalid_addv2(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 224, 224, 3], name="input") - - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 3, 32], initializer=tf.compat.v1.random_normal_initializer() - ) - conv_bias = tf.compat.v1.get_variable("bias", [32], initializer=tf.compat.v1.random_normal_initializer()) - conv1 = tf.nn.conv2d(x, conv_weights, strides=[1, 1, 1, 1], padding="SAME") - conv_bias = tf.math.add(conv1, conv_bias) - - gelu = self.gelu(conv_bias, addv2_value=10.0) - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[gelu.name.split(":")[0]] - ) - - output_graph_def = FuseGeluOptimizer(output_graph_def).do_transformation() - - found_gelu = False - for i in output_graph_def.node: - if i.op == "Gelu": - found_gelu = True - break - - self.assertEqual(found_gelu, False) - - @disable_random() - def test_gelu_fusion_with_invalid_mul(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 224, 224, 3], name="input") - - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 3, 32], initializer=tf.compat.v1.random_normal_initializer() - ) - conv_bias = tf.compat.v1.get_variable("bias", [32], initializer=tf.compat.v1.random_normal_initializer()) - conv1 = tf.nn.conv2d(x, conv_weights, strides=[1, 1, 1, 1], padding="SAME") - conv_bias = tf.math.add(conv1, conv_bias) - - gelu = self.gelu(conv_bias, mul_value=1.0) - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[gelu.name.split(":")[0]] - ) - - output_graph_def = FuseGeluOptimizer(output_graph_def).do_transformation() - - found_gelu = False - for i in output_graph_def.node: - if i.op == "Gelu": - found_gelu = True - break - - self.assertEqual(found_gelu, False) - - @disable_random() - def test_gelu_fusion(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 224, 224, 3], name="input") - - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 3, 32], initializer=tf.compat.v1.random_normal_initializer() - ) - conv_bias = tf.compat.v1.get_variable("bias", [32], initializer=tf.compat.v1.random_normal_initializer()) - conv1 = tf.nn.conv2d(x, conv_weights, strides=[1, 1, 1, 1], padding="SAME") - conv_bias = tf.math.add(conv1, conv_bias) - - gelu = self.gelu(conv_bias) - relu = tf.nn.relu(gelu) - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[relu.name.split(":")[0]] - ) - - output_graph_def = FuseGeluOptimizer(output_graph_def).do_transformation() - - found_gelu = False - for i in output_graph_def.node: - if i.op == "Gelu": - found_gelu = True - break - - self.assertEqual(found_gelu, True) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/tfnewapi/test_tensorflow_graph_fuse_pad_conv_fp32.py b/test/tfnewapi/test_tensorflow_graph_fuse_pad_conv_fp32.py deleted file mode 100644 index 6b1ac19ba02..00000000000 --- a/test/tfnewapi/test_tensorflow_graph_fuse_pad_conv_fp32.py +++ /dev/null @@ -1,126 +0,0 @@ -import os -import unittest - -import tensorflow as tf -import yaml -from tensorflow.compat.v1 import graph_util - -from neural_compressor.adaptor.tf_utils.util import disable_random - - -def build_fake_yaml(): - fake_yaml = """ - model: - name: fake_yaml - framework: tensorflow - inputs: input - outputs: op_to_store - device: cpu - quantization: - model_wise: - weight: - granularity: per_tensor - scheme: sym - dtype: int8 - algorithm: minmax - evaluation: - accuracy: - metric: - topk: 1 - tuning: - strategy: - name: mse - accuracy_criterion: - relative: 0.01 - exit_policy: - performance_only: True - workspace: - path: saved - """ - y = yaml.load(fake_yaml, Loader=yaml.SafeLoader) - with open("fake_yaml.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - f.close() - - -class TestFoldPadConv(unittest.TestCase): - @classmethod - def setUpClass(self): - build_fake_yaml() - - @classmethod - def tearDownClass(self): - os.remove("fake_yaml.yaml") - - @disable_random() - def test_fold_pad_conv(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(x, paddings, "CONSTANT") - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv2d(x_pad, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - normed = tf.compat.v1.layers.batch_normalization(conv) - relu = tf.nn.relu(normed, name="op_to_store") - out_name = relu.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - found_pad = False - - for i in output_graph.graph_def.node: - if i.op == "Pad": - found_pad = True - break - self.assertEqual(found_pad, False) - - @disable_random() - def test_fold_non_const_pad_conv(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [0, 0]]) - vec = tf.raw_ops.DataFormatVecPermute(x=paddings, src_format="NHWC", dst_format="NHWC") - x_pad = tf.pad(x, vec, "CONSTANT") - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv2d(x_pad, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - normed = tf.compat.v1.layers.batch_normalization(conv) - relu = tf.nn.relu(normed, name="op_to_store") - out_name = relu.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - found_pad = False - - for i in output_graph.graph_def.node: - if i.op == "Pad": - found_pad = True - break - self.assertEqual(found_pad, False) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/tfnewapi/test_tensorflow_graph_qdq_bn_fusion.py b/test/tfnewapi/test_tensorflow_graph_qdq_bn_fusion.py deleted file mode 100644 index 02ee5d61bf8..00000000000 --- a/test/tfnewapi/test_tensorflow_graph_qdq_bn_fusion.py +++ /dev/null @@ -1,417 +0,0 @@ -# -# -*- coding: utf-8 -*- -# -import logging -import os -import unittest - -import numpy as np -import tensorflow as tf -import yaml -from tensorflow.compat.v1 import graph_util -from tensorflow.core.framework import attr_value_pb2 -from tensorflow.python.framework import dtypes - -from neural_compressor.adaptor.tf_utils.util import disable_random -from neural_compressor.experimental import Quantization, common -from neural_compressor.utils import logger -from neural_compressor.utils.utility import CpuInfo - - -def build_fake_yaml_1(): - fake_yaml_1 = """ - model: - name: fake_yaml_1 - framework: tensorflow - inputs: input - device: cpu - quantization: - model_wise: - weight: - granularity: per_tensor - scheme: sym - dtype: int8 - algorithm: minmax - evaluation: - accuracy: - metric: - topk: 1 - tuning: - strategy: - name: basic - accuracy_criterion: - relative: 0.1 - exit_policy: - performance_only: True - workspace: - path: saved - """ - y = yaml.load(fake_yaml_1, Loader=yaml.SafeLoader) - with open("fake_yaml_1.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - f.close() - - -def build_fake_yaml_2(): - fake_yaml_2 = """ - model: - name: fake_yaml_2 - framework: tensorflow - inputs: input - device: cpu - quantization: - model_wise: - weight: - granularity: per_tensor - scheme: sym - dtype: int8 - algorithm: minmax - evaluation: - accuracy: - metric: - topk: 1 - tuning: - strategy: - name: basic - accuracy_criterion: - relative: 0.1 - workspace: - path: saved - """ - y = yaml.load(fake_yaml_2, Loader=yaml.SafeLoader) - with open("fake_yaml_2.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - f.close() - - -class TestTensorflowQdqConvFusion(unittest.TestCase): - @classmethod - def setUpClass(self): - build_fake_yaml_1() - build_fake_yaml_2() - - @classmethod - def tearDownClass(self): - os.remove("fake_yaml_1.yaml") - os.remove("fake_yaml_2.yaml") - - @disable_random() - def test_bn_relu_depthwiseconv_biasadd_relu6_fusion(self): - logger.info("test_bn_relu_depthwiseconv_biasadd_relu6_fusion") - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - normed_0 = tf.compat.v1.layers.batch_normalization(x) - relu = tf.nn.relu(normed_0, name="op_to_store_0") - conv = tf.compat.v1.nn.depthwise_conv2d_native(relu, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - normed_1 = tf.compat.v1.layers.batch_normalization(conv) - relu6 = tf.nn.relu6(normed_1, name="op_to_store_1") - out_name = relu6.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - - quantizer = Quantization("fake_yaml_1.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - conv_input_type = True - found_fusion = True - qbn_num = 0 - dq_num = 0 - for i in output_graph.graph_def.node: - if i.op == "_FusedQuantizedDepthwiseConv2D" and i.attr["Thost_inputs"].list.type != [ - 11, - 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - ]: - conv_input_type = False - break - if i.op in ["Relu", "Relu6", "FusedBatchNormV3"]: - found_fusion = False - break - if i.op == "_QuantizedFusedBatchNorm": - qbn_num += 1 - if i.op == "Dequantize": - dq_num += 1 - self.assertEqual(conv_input_type, True) - self.assertEqual(found_fusion, True) - self.assertEqual(qbn_num, 1) - self.assertEqual(dq_num, 0) - - @disable_random() - def test_training_bn_relu_depthwiseconv_biasadd_relu6_fusion(self): - logger.info("test_training_bn_relu_depthwiseconv_biasadd_relu6_fusion") - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - normed_0 = tf.compat.v1.layers.batch_normalization(x, training=True) - relu = tf.nn.relu(normed_0, name="op_to_store_0") - conv = tf.compat.v1.nn.depthwise_conv2d_native(relu, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - normed_1 = tf.compat.v1.layers.batch_normalization(conv) - relu6 = tf.nn.relu6(normed_1, name="op_to_store_1") - out_name = relu6.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - - quantizer = Quantization("fake_yaml_1.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - bn_num, bf16_bn_num, qbn_num, dq_num = 0, 0, 0, 0 - for i in output_graph.graph_def.node: - if i.op == "FusedBatchNormV3": - bn_num += 1 - if i.attr["T"].type == dtypes.bfloat16.as_datatype_enum: - bf16_bn_num += 1 - if i.op == "_QuantizedFusedBatchNorm": - qbn_num += 1 - if i.op == "Dequantize": - dq_num += 1 - self.assertEqual(bn_num, 1) - self.assertEqual(qbn_num, 0) - self.assertEqual(dq_num, 0) - bf16_enabled = bool(CpuInfo().bf16 or os.getenv("FORCE_BF16") == "1") - if bf16_enabled: - self.assertEqual(bf16_bn_num, 1) - - @disable_random() - def test_bn_leakyrelu_conv_biasadd_relu(self): - logger.info("test_bn_leakyrelu_conv_biasadd_relu") - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - normed_0 = tf.compat.v1.layers.batch_normalization(x) - leaky_relu = tf.nn.leaky_relu(normed_0, alpha=0.3, name="op_to_store_0") - conv = tf.nn.conv2d(leaky_relu, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - normed_1 = tf.compat.v1.layers.batch_normalization(conv) - relu = tf.nn.relu(normed_1, name="op_to_store_1") - out_name = relu.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - - quantizer = Quantization("fake_yaml_1.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - - quantizer.model = output_graph_def - output_graph = quantizer.fit() - conv_input_type = True - found_fusion = True - qbn_num = 0 - dq_num = 0 - qbn_output_max_name = "batch_normalization/FusedBatchNormV3_eightbit_quantized_bn/frozen_bn_output_max" - for i in output_graph.graph_def.node: - if i.op == "_FusedQuantizedConv2D" and i.attr["Thost_inputs"].list.type != [11, 11, 1, 1, 1, 1, 1, 1, 1]: - conv_input_type = False - break - if i.op in ["Relu", "LeakyRelu", "FusedBatchNormV3"]: - found_fusion = False - break - if i.op == "_QuantizedFusedBatchNorm": - is_offset_const = i.attr["is_offset_const"].b - is_mean_const = i.attr["is_mean_const"].b - qbn_alpha = i.attr["alpha"].f - frozen_qbn_output_max = i.input[8] - qbn_num += 1 - if i.name == qbn_output_max_name: - frozen_qbn_output_max_value = i.attr["value"].tensor.float_val[0] - if i.op == "Dequantize": - dq_num += 1 - self.assertEqual(conv_input_type, True) - self.assertEqual(found_fusion, True) - self.assertEqual(qbn_num, 1) - self.assertEqual(dq_num, 0) - self.assertEqual(is_offset_const, True) - self.assertEqual(is_mean_const, True) - self.assertEqual(round(qbn_alpha, 7), 0.3) - self.assertEqual(frozen_qbn_output_max, qbn_output_max_name) - self.assertGreater(frozen_qbn_output_max_value, 126) - - @disable_random() - def test_bn_relu_conv_biasadd_relu(self): - logger.info("test_bn_relu_conv_biasadd_relu") - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - normed_0 = tf.compat.v1.layers.batch_normalization(x) - relu_0 = tf.nn.relu(normed_0, name="op_to_store_0") - conv = tf.nn.conv2d(relu_0, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - normed_1 = tf.compat.v1.layers.batch_normalization(conv) - relu_1 = tf.nn.relu(normed_1, name="op_to_store_1") - out_name = relu_1.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - - quantizer = Quantization("fake_yaml_1.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - - quantizer.model = output_graph_def - output_graph = quantizer.fit() - conv_input_type = True - found_fusion = True - qbn_num = 0 - dq_num = 0 - qbn_output_max_name = "batch_normalization/FusedBatchNormV3_eightbit_quantized_bn/frozen_bn_output_max" - for i in output_graph.graph_def.node: - if i.op == "_FusedQuantizedConv2D" and i.attr["Thost_inputs"].list.type != [11, 11, 1, 1, 1, 1, 1, 1, 1]: - conv_input_type = False - break - if i.op in ["Relu", "FusedBatchNormV3"]: - found_fusion = False - break - if i.op == "_QuantizedFusedBatchNorm": - is_offset_const = i.attr["is_offset_const"].b - is_mean_const = i.attr["is_mean_const"].b - frozen_qbn_output_max = i.input[8] - qbn_num += 1 - if i.name == qbn_output_max_name: - frozen_qbn_output_max_value = i.attr["value"].tensor.float_val[0] - if i.op == "Dequantize": - dq_num += 1 - self.assertEqual(conv_input_type, True) - self.assertEqual(found_fusion, True) - self.assertEqual(qbn_num, 1) - self.assertEqual(dq_num, 0) - self.assertEqual(is_offset_const, True) - self.assertEqual(is_mean_const, True) - self.assertEqual(frozen_qbn_output_max, qbn_output_max_name) - self.assertGreater(frozen_qbn_output_max_value, 126) - - @disable_random() - def test_bn_performance_only_false(self): - logger.info("test_bn_performance_only_false") - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - normed_0 = tf.compat.v1.layers.batch_normalization(x) - relu_0 = tf.nn.relu(normed_0, name="op_to_store_0") - conv = tf.nn.conv2d(relu_0, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - normed_1 = tf.compat.v1.layers.batch_normalization(conv) - relu_1 = tf.nn.relu6(normed_1, name="op_to_store_1") - out_name = relu_1.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - - quantizer = Quantization("fake_yaml_2.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - - quantizer.model = output_graph_def - output_graph = quantizer.fit() - found_fusion = True - qconv_num = 0 - qbn_num = 0 - dq_num = 0 - for i in output_graph.graph_def.node: - if i.op in ["Relu6"]: - found_fusion = False - break - if i.op == "_FusedQuantizedConv2D": - qconv_num += 1 - if i.op == "_QuantizedFusedBatchNorm": - qbn_num += 1 - if i.op == "Dequantize": - dq_num += 1 - self.assertEqual(found_fusion, True) - self.assertEqual(qconv_num, 1) - self.assertEqual(qbn_num, 0) - self.assertEqual(dq_num, 1) - - @disable_random() - def test_bnex_performance_only_false(self): - logger.info("test_bnex_performance_only_false") - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - conv_weights_0 = tf.compat.v1.get_variable( - "weight_0", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - normed_0 = tf.compat.v1.layers.batch_normalization(x) - relu_0 = tf.nn.relu(normed_0, name="op_to_store_0") - conv_0 = tf.nn.conv2d(relu_0, conv_weights_0, strides=[1, 2, 2, 1], padding="VALID") - normed_1 = tf.compat.v1.layers.batch_normalization(conv_0) - conv_weights_1 = tf.compat.v1.get_variable( - "weight_1", [5, 5, 16, 2], initializer=tf.compat.v1.random_normal_initializer() - ) - conv_1 = tf.nn.conv2d(normed_1, conv_weights_1, strides=[1, 3, 3, 1], padding="VALID") - relu_1 = tf.nn.relu6(conv_1, name="op_to_store_1") - out_name = relu_1.name.split(":")[0] - """graph_def = tf.compat.v1.get_default_graph().as_graph_def() - for node in graph_def.node: - - if node.name == "batch_normalization_1/FusedBatchNormV3": - node.op = "_FusedBatchNormEx" - with tf.Graph().as_default() as graph: - tf.import_graph_def(graph_def, name='') - """ - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - for node in output_graph_def.node: - if node.name == "batch_normalization_1/FusedBatchNormV3": - node.op = "_FusedBatchNormEx" - node.attr["activation_mode"].CopyFrom(attr_value_pb2.AttrValue(s=b"Relu")) - - quantizer = Quantization("fake_yaml_2.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - - quantizer.model = output_graph_def - output_graph = quantizer.fit() - found_fusion = True - qconv_num = 0 - qbn_num = 0 - dq_num = 0 - for i in output_graph.graph_def.node: - if i.op in ["Relu6", "_FusedBatchNormEx"]: - found_fusion = False - break - if i.op == "_FusedQuantizedConv2D": - qconv_num += 1 - if i.op == "_QuantizedFusedBatchNorm": - qbn_num += 1 - if i.op == "Dequantize": - dq_num += 1 - self.assertEqual(found_fusion, True) - self.assertEqual(qconv_num, 2) - self.assertEqual(qbn_num, 0) - self.assertEqual(dq_num, 1) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/tfnewapi/test_tensorflow_graph_qdq_concat_fusion.py b/test/tfnewapi/test_tensorflow_graph_qdq_concat_fusion.py deleted file mode 100644 index 93d252e793a..00000000000 --- a/test/tfnewapi/test_tensorflow_graph_qdq_concat_fusion.py +++ /dev/null @@ -1,224 +0,0 @@ -# -# -# -*- coding: utf-8 -*- -import os -import unittest - -import tensorflow as tf -import yaml -from tensorflow.compat.v1 import graph_util - -from neural_compressor.adaptor.tensorflow import TensorflowQuery -from neural_compressor.adaptor.tf_utils.quantize_graph.quantize_graph_for_intel_cpu import QuantizeGraphForIntel -from neural_compressor.adaptor.tf_utils.util import disable_random, read_graph - - -def build_fake_yaml(): - fake_yaml = """ - model: - name: fake_yaml - framework: tensorflow - inputs: input - outputs: predict - device: cpu - quantization: - model_wise: - weight: - granularity: per_tensor - scheme: sym - dtype: int8 - algorithm: minmax - evaluation: - accuracy: - metric: - topk: 1 - tuning: - strategy: - name: mse - accuracy_criterion: - relative: 0.01 - exit_policy: - performance_only: True - workspace: - path: saved - """ - y = yaml.load(fake_yaml, Loader=yaml.SafeLoader) - with open("fake_yaml.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - f.close() - - -class TestTensorflowQdqConcatFusion(unittest.TestCase): - mb_model_url = ( - "https://storage.googleapis.com/intel-optimized-tensorflow/models/v1_8/inceptionv3_fp32_pretrained_model.pb" - ) - pb_path = "/tmp/.neural_compressor/inceptionv3_fp32.pb" - - @classmethod - def setUpClass(self): - if not os.path.exists(self.pb_path): - os.system("mkdir -p /tmp/.neural_compressor && wget {} -O {} ".format(self.mb_model_url, self.pb_path)) - self.op_wise_sequences = TensorflowQuery( - local_config_file=os.path.join(os.path.dirname(__file__), "../../neural_compressor/adaptor/tensorflow.yaml") - ).get_eightbit_patterns() - build_fake_yaml() - - @classmethod - def tearDownClass(self): - os.remove("fake_yaml.yaml") - - def test_tensorflow_concat_quantization(self): - output_graph_def = read_graph(self.pb_path) - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 299, 299, 3), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - found_quantized_concat_node = False - - target_concat_node_name = "v0/cg/incept_v3_a0/concat_eightbit_quantized_concatv2" - from neural_compressor.adaptor.tf_utils.graph_util import GraphAnalyzer - - cur_graph = GraphAnalyzer() - cur_graph.graph = output_graph.graph_def - graph_info = cur_graph.parse_graph() - found_quantized_concat_node = target_concat_node_name in graph_info - - self.assertEqual(found_quantized_concat_node, True) - min_out, max_out = [], [] - for input_conv_name in graph_info[target_concat_node_name].node.input[:4]: - # print (input_conv_name, graph_info[input_conv_name].node.input) - min_freezed_out_name = graph_info[input_conv_name].node.input[-2] - max_freezed_out_name = graph_info[input_conv_name].node.input[-1] - min_freezed_out_value = (graph_info[min_freezed_out_name].node.attr["value"].tensor.float_val)[0] - max_freezed_out_value = (graph_info[max_freezed_out_name].node.attr["value"].tensor.float_val)[0] - min_out.append(min_freezed_out_value) - max_out.append(max_freezed_out_value) - - self.assertEqual(len(set(min_out)), 1) - self.assertEqual(len(set(max_out)), 1) - - @disable_random() - def test_concat_with_different_input_type(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 128, 128, 16], name="input") - conv_weights = tf.compat.v1.get_variable( - "weight", [2, 2, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv_bias = tf.compat.v1.get_variable("bias", [16], initializer=tf.compat.v1.random_normal_initializer()) - x = tf.nn.relu(x) - sqrt = tf.math.sqrt(x) - relu_sqrt = tf.nn.relu(sqrt) - conv = tf.nn.conv2d(relu_sqrt, conv_weights, strides=[1, 2, 2, 1], padding="SAME", name="last") - normed = tf.compat.v1.layers.batch_normalization(conv) - - relu = tf.nn.relu(normed) - conv1 = tf.nn.conv2d(x, conv_weights, strides=[1, 2, 2, 1], padding="SAME", name="last") - conv_bias = tf.nn.bias_add(conv1, conv_bias) - concat = tf.concat([relu, conv_bias], 1) - pool = tf.nn.avg_pool(concat, ksize=1, strides=[1, 2, 2, 1], name="avgpool", padding="SAME") - final_node = tf.nn.relu(pool, name="op_to_store") - out_name = final_node.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 128, 128, 16), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - - quantized_concat = False - for i in output_graph.graph_def.node: - if i.op == "QuantizedConcatV2": - quantized_concat = True - self.assertEqual(quantized_concat, False) - - @disable_random() - def test_concat_with_same_input_type(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 128, 128, 16], name="input") - conv_weights = tf.compat.v1.get_variable( - "weight", [2, 2, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv_bias = tf.compat.v1.get_variable("bias", [16], initializer=tf.compat.v1.random_normal_initializer()) - conv1_bias = tf.compat.v1.get_variable("bias1", [16], initializer=tf.compat.v1.random_normal_initializer()) - x = tf.nn.relu(x) - sqrt = tf.math.sqrt(x) - relu_sqrt = tf.nn.relu(sqrt) - conv = tf.nn.conv2d(relu_sqrt, conv_weights, strides=[1, 2, 2, 1], padding="SAME", name="last") - conv_bias = tf.nn.bias_add(conv, conv_bias) - relu1 = tf.nn.relu(conv_bias) - - conv1 = tf.nn.conv2d(x, conv_weights, strides=[1, 2, 2, 1], padding="SAME", name="last") - conv1_bias = tf.nn.bias_add(conv1, conv1_bias) - relu2 = tf.nn.relu(conv1_bias) - concat = tf.concat([relu1, relu2], 1) - pool = tf.nn.avg_pool(concat, ksize=1, strides=[1, 2, 2, 1], name="avgpool", padding="SAME") - final_node = tf.nn.relu(pool, name="op_to_store") - out_name = final_node.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 128, 128, 16), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - - quantized_concat = False - for i in output_graph.graph_def.node: - if i.op == "QuantizedConcatV2": - quantized_concat = True - self.assertEqual(quantized_concat, True) - - @disable_random() - def test_concat_with_qint8_and_fp32_input_type(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 128, 128, 16], name="input") - bias = tf.compat.v1.get_variable("bias", [16], initializer=tf.compat.v1.random_normal_initializer()) - - bias_add = tf.nn.bias_add(x, bias) - - pool = tf.nn.avg_pool(x, ksize=1, strides=[1, 1, 1, 1], name="avgpool", padding="SAME") - concat = tf.concat([bias_add, pool], 1) - final_node = tf.nn.relu(concat, name="op_to_store") - out_name = final_node.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 128, 128, 16), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - dtype = None - quantized_concat = False - from tensorflow.python.framework import dtypes - - for i in output_graph.graph_def.node: - if i.op == "QuantizedConcatV2": - dtype = dtypes.DType(i.attr["T"].type) - quantized_concat = True - self.assertEqual(quantized_concat, True) - self.assertEqual(dtype, dtypes.qint8) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/tfnewapi/test_tensorflow_graph_qdq_conv3d_fusion.py b/test/tfnewapi/test_tensorflow_graph_qdq_conv3d_fusion.py deleted file mode 100644 index cbcb63e95e2..00000000000 --- a/test/tfnewapi/test_tensorflow_graph_qdq_conv3d_fusion.py +++ /dev/null @@ -1,854 +0,0 @@ -# -# -*- coding: utf-8 -*- -# -import logging -import os -import unittest - -import numpy as np -import tensorflow as tf -import yaml -from pkg_resources import parse_version -from tensorflow.compat.v1 import graph_util -from tensorflow.python.framework import function - -from neural_compressor.adaptor.tensorflow import TensorflowQuery -from neural_compressor.adaptor.tf_utils.graph_rewriter.generic.fold_batch_norm import FoldBatchNormNodesOptimizer -from neural_compressor.adaptor.tf_utils.graph_rewriter.generic.strip_unused_nodes import StripUnusedNodesOptimizer -from neural_compressor.adaptor.tf_utils.quantize_graph.quantize_graph_for_intel_cpu import QuantizeGraphForIntel -from neural_compressor.adaptor.tf_utils.util import disable_random - - -def build_fake_yaml(): - fake_yaml = """ - model: - name: fake_yaml - framework: tensorflow - inputs: input - device: cpu - quantization: - model_wise: - weight: - granularity: per_tensor - scheme: sym - dtype: int8 - algorithm: minmax - evaluation: - accuracy: - metric: - topk: 1 - tuning: - strategy: - name: basic - accuracy_criterion: - relative: 0.1 - exit_policy: - performance_only: True - workspace: - path: saved - """ - - y = yaml.load(fake_yaml, Loader=yaml.SafeLoader) - - with open("fake_yaml.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - - f.close() - - -class TestTensorflowQdqConvFusion(unittest.TestCase): - @classmethod - def setUpClass(self): - build_fake_yaml() - - @classmethod - def tearDownClass(self): - os.remove("fake_yaml.yaml") - - @disable_random() - def test_conv3d_addv2_relu_fusion(self): - logging.getLogger().info("test_conv3d_addv2_relu_fusion") - x = tf.compat.v1.placeholder(tf.float32, [1, 128, 64, 64, 16], name="input") - top_relu = tf.nn.relu(x) - conv3d_1_weights = tf.compat.v1.get_variable( - "weight_conv3d_1", [3, 3, 3, 16, 32], initializer=tf.compat.v1.random_normal_initializer() - ) - conv3d_1 = tf.nn.conv3d(top_relu, conv3d_1_weights, strides=[1, 2, 2, 2, 1], padding="SAME") - add = tf.raw_ops.AddV2(x=conv3d_1, y=tf.constant(np.random.randn(32), dtype=tf.float32), name="addv2") - relu = tf.nn.relu(add) - conv3d_2_weights = tf.compat.v1.get_variable( - "weight_conv3d_2", [3, 3, 3, 32, 1], initializer=tf.compat.v1.random_normal_initializer() - ) - conv3d_2 = tf.nn.conv3d(relu, conv3d_2_weights, strides=[1, 2, 2, 2, 1], padding="SAME") - - out_name = conv3d_2.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 128, 64, 64, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - - found_conv_sumadd_fusion = False - found_conv_biasadd_fusion = False - for i in output_graph.graph_def.node: - if i.op == "_FusedQuantizedConv3D": - if b"Sum" in i.attr["fused_ops"].list.s: - found_conv_sumadd_fusion = True - if i.attr["fused_ops"].list.s == [b"BiasAdd", b"Relu", b"Requantize"]: - found_conv_biasadd_fusion = True - self.assertEqual(found_conv_sumadd_fusion, False) - self.assertEqual(found_conv_biasadd_fusion, True) - - @disable_random() - def test_single_conv3d_fusion(self): - logging.getLogger().info("test_single_conv3d_fusion") - x = tf.compat.v1.placeholder(tf.float32, [1, 64, 64, 64, 1], name="input") - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(x, paddings, "CONSTANT") - conv_weights = tf.compat.v1.get_variable( - "weight", [4, 4, 4, 1, 64], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv3d(x_pad, conv_weights, strides=[1, 2, 2, 2, 1], padding="VALID") - mul = tf.multiply(conv, 2.0, name="op_to_store") - out_name = mul.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 64, 64, 64, 1), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - found_conv_fusion = False - found_dequantize_fusion = False - - for i in output_graph.graph_def.node: - if i.op == "_FusedQuantizedConv3D": - found_conv_fusion = True - if str(i.attr["fused_ops"].list.s) == str([b"Dequantize"]): - found_dequantize_fusion = True - self.assertEqual(found_conv_fusion, True) - self.assertEqual(found_dequantize_fusion, True) - - @disable_random() - def test_conv3d_biasadd_fusion(self): - logging.getLogger().info("test_conv3d_biasadd_fusion") - x = tf.compat.v1.placeholder(tf.float32, [1, 64, 64, 64, 1], name="input") - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(x, paddings, "CONSTANT") - conv_weights = tf.compat.v1.get_variable( - "weight", [4, 4, 4, 1, 64], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv3d(x_pad, conv_weights, strides=[1, 2, 2, 2, 1], padding="VALID") - y_const = tf.constant(np.random.randn(1, 1, 1, 1, 64), dtype=tf.float32) - add = tf.raw_ops.AddV2(x=conv, y=y_const, name="addv2") - out_name = add.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 64, 64, 64, 1), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - found_conv_fusion = False - found_dequantize_fusion = False - - for i in output_graph.graph_def.node: - if i.op == "_FusedQuantizedConv3D": - found_conv_fusion = True - if str(i.attr["fused_ops"].list.s) == str([b"BiasAdd", b"Dequantize"]): - found_dequantize_fusion = True - self.assertEqual(found_conv_fusion, True) - self.assertEqual(found_dequantize_fusion, True) - - def test_conv3d_relu6_fusion(self): - logging.getLogger().info("test_conv3d_biasadd_fusion") - x = tf.compat.v1.placeholder(tf.float32, [1, 64, 64, 64, 1], name="input") - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(x, paddings, "CONSTANT") - conv_weights = tf.compat.v1.get_variable( - "weight", [4, 4, 4, 1, 64], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv3d(x_pad, conv_weights, strides=[1, 2, 2, 2, 1], padding="VALID") - relu6 = tf.nn.relu6(conv, name="op_to_store") - out_name = relu6.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 64, 64, 64, 1), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - found_conv_fusion = False - found_requantize_fusion = False - - for i in output_graph.graph_def.node: - if i.op == "_FusedQuantizedConv3D": - found_conv_fusion = True - if str(i.attr["fused_ops"].list.s) == str([b"BiasAdd", b"Relu", b"Dequantize"]): - found_requantize_fusion = True - self.assertEqual(found_conv_fusion, True) - self.assertEqual(found_requantize_fusion, True) - - @disable_random() - def test_conv3d_add_relu_fusion(self): - logging.getLogger().info("test_conv3d_add_relu_fusion") - x = tf.compat.v1.placeholder(tf.float32, [1, 64, 64, 64, 1], name="input") - conv_weights = tf.compat.v1.get_variable( - "weight1", [4, 4, 4, 1, 64], initializer=tf.compat.v1.random_normal_initializer() - ) - conv1_weights = tf.compat.v1.get_variable( - "weight2", [4, 4, 4, 1, 64], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv3d(x, conv_weights, strides=[1, 2, 2, 2, 1], padding="VALID") - conv1 = tf.nn.conv3d(x, conv1_weights, strides=[1, 2, 2, 2, 1], padding="VALID") - add = conv + conv1 - relu = tf.nn.relu(add) - - out_name = relu.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 64, 64, 64, 1), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - found_conv_fusion = False - - for i in output_graph.graph_def.node: - if i.op == "_FusedQuantizedConv3D": - found_conv_fusion = True - break - self.assertEqual(found_conv_fusion, True) - - @disable_random() - def test_conv3d_add_const_fusion(self): - logging.getLogger().info("test_conv3d_add_const_fusion") - x = tf.compat.v1.placeholder(tf.float32, [1, 64, 64, 64, 1], name="input") - conv_weights = tf.compat.v1.get_variable( - "weight", [4, 4, 4, 1, 64], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv3d(x, conv_weights, strides=[1, 2, 2, 2, 1], padding="VALID") - add = conv + tf.constant( - [ - [ - [ - [ - [ - 0.000015179151887423359, - 0.000022200847524800338, - -0.000009995766049541999, - -0.0000022956028260523453, - 0.000008830029400996864, - 0.0000017190360495078494, - 0.000019561824956326745, - 0.00014721050683874637, - -0.000005871841494808905, - 0.000004377178811409976, - -0.000006191140982991783, - 0.000009258330464945175, - -0.000009839599442784674, - 0.000008547322067897767, - 0.000004629391241905978, - 2.345327061448188e-7, - 0.000015179151887423359, - 0.000022200847524800338, - -0.000009995766049541999, - -0.0000022956028260523453, - 0.000008830029400996864, - 0.0000017190360495078494, - 0.000019561824956326745, - 0.00014721050683874637, - -0.000005871841494808905, - 0.000004377178811409976, - -0.000006191140982991783, - 0.000009258330464945175, - -0.000009839599442784674, - 0.000008547322067897767, - 0.000004629391241905978, - 2.345327061448188e-7, - 0.000015179151887423359, - 0.000022200847524800338, - -0.000009995766049541999, - -0.0000022956028260523453, - 0.000008830029400996864, - 0.0000017190360495078494, - 0.000019561824956326745, - 0.00014721050683874637, - -0.000005871841494808905, - 0.000004377178811409976, - -0.000006191140982991783, - 0.000009258330464945175, - -0.000009839599442784674, - 0.000008547322067897767, - 0.000004629391241905978, - 2.345327061448188e-7, - 0.000015179151887423359, - 0.000022200847524800338, - -0.000009995766049541999, - -0.0000022956028260523453, - 0.000008830029400996864, - 0.0000017190360495078494, - 0.000019561824956326745, - 0.00014721050683874637, - -0.000005871841494808905, - 0.000004377178811409976, - -0.000006191140982991783, - 0.000009258330464945175, - -0.000009839599442784674, - 0.000008547322067897767, - 0.000004629391241905978, - 2.345327061448188e-7, - ] - ] - ] - ] - ] - ) - - out_name = add.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 64, 64, 64, 1), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - found_conv_fusion = True - - for i in output_graph.graph_def.node: - if i.op == "AddV2": - found_conv_fusion = False - break - self.assertEqual(found_conv_fusion, True) - - @disable_random() - def test_conv3d_add_addn_const_relu_fusion(self): - logging.getLogger().info("test_conv3d_add_addn_const_relu_fusion") - x = tf.compat.v1.placeholder(tf.float32, [1, 128, 64, 64, 16], name="input") - top_relu = tf.nn.relu(x) - conv3d_1_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 3, 16, 32], initializer=tf.compat.v1.random_normal_initializer() - ) - conv3d_1 = tf.nn.conv3d(top_relu, conv3d_1_weights, strides=[1, 2, 2, 2, 1], padding="SAME") - add_1 = tf.raw_ops.AddV2(x=conv3d_1, y=tf.constant(np.random.randn(32), dtype=tf.float32), name="addv2_1") - var = tf.compat.v1.get_variable( - "add_y", [1, 64, 32, 32, 32], initializer=tf.compat.v1.random_normal_initializer() - ) - add_2 = tf.raw_ops.AddV2(x=add_1, y=var, name="addv2_2") - relu = tf.nn.relu(add_2) - out_name = relu.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 128, 64, 64, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - - found_conv_sumadd_fusion = False - found_conv_biasadd_fusion = False - for i in output_graph.graph_def.node: - if i.op == "_FusedQuantizedConv3D": - if str(b"Sum") in str(i.attr["fused_ops"].list.s): - found_conv_sumadd_fusion = True - if str(i.attr["fused_ops"].list.s) == str([b"BiasAdd", b"Sum", b"Relu"]): - found_conv_biasadd_fusion = True - self.assertEqual(found_conv_sumadd_fusion, False) - self.assertEqual(found_conv_biasadd_fusion, False) - - @disable_random() - def test_conv3d_add_const_addn_relu_fusion(self): - logging.getLogger().info("test_conv3d_add_const_addn_relu_fusion") - x = tf.compat.v1.placeholder(tf.float32, [1, 128, 64, 64, 16], name="input") - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(x, paddings, "CONSTANT") - top_relu = tf.nn.relu(x_pad) - conv3d_1_weights = tf.compat.v1.get_variable( - "weight1", [3, 3, 3, 16, 32], initializer=tf.compat.v1.random_normal_initializer() - ) - conv3d_1 = tf.nn.conv3d(top_relu, conv3d_1_weights, strides=[1, 2, 2, 2, 1], padding="SAME") - add_1 = tf.raw_ops.AddV2(x=conv3d_1, y=tf.constant(np.random.randn(32), dtype=tf.float32), name="addv2_1") - conv3d_2_weights = tf.compat.v1.get_variable( - "weight2", [3, 3, 3, 16, 32], initializer=tf.compat.v1.random_normal_initializer() - ) - conv3d_2 = tf.nn.conv3d(top_relu, conv3d_2_weights, strides=[1, 2, 2, 2, 1], padding="SAME") - add = tf.raw_ops.AddV2(x=add_1, y=conv3d_2, name="addv2_2") - relu = tf.nn.relu(add) - out_name = relu.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 128, 64, 64, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - - found_conv_sumadd_fusion = False - found_conv_biasadd_fusion = False - for i in output_graph.graph_def.node: - if i.op == "_FusedQuantizedConv3D": - if str(b"Sum") in str(i.attr["fused_ops"].list.s): - found_conv_sumadd_fusion = True - if str(i.attr["fused_ops"].list.s) == str([b"BiasAdd", b"Sum", b"Relu"]): - found_conv_biasadd_fusion = True - self.assertEqual(found_conv_sumadd_fusion, True) - - @disable_random() - def test_conv3d_add_addn_fusion(self): - logging.getLogger().info("test_conv3d_add_addn_fusion") - x = tf.compat.v1.placeholder(tf.float32, [1, 128, 64, 64, 16], name="input") - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(x, paddings, "CONSTANT") - top_relu = tf.nn.relu(x_pad) - conv3d_1_weights = tf.compat.v1.get_variable( - "weight1", [3, 3, 3, 16, 32], initializer=tf.compat.v1.random_normal_initializer() - ) - conv3d_1 = tf.nn.conv3d(top_relu, conv3d_1_weights, strides=[1, 2, 2, 2, 1], padding="SAME") - add_1 = tf.raw_ops.AddV2(x=conv3d_1, y=tf.constant(np.random.randn(32), dtype=tf.float32), name="addv2_4") - conv3d_2_weights = tf.compat.v1.get_variable( - "weight2", [3, 3, 3, 16, 32], initializer=tf.compat.v1.random_normal_initializer() - ) - conv3d_2 = tf.nn.conv3d(top_relu, conv3d_2_weights, strides=[1, 2, 2, 2, 1], padding="SAME") - add = tf.raw_ops.AddV2(x=add_1, y=conv3d_2, name="addv2") - out_name = add.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 128, 64, 64, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - - found_conv_fusion = False - for i in output_graph.graph_def.node: - if i.op == "_FusedQuantizedConv3D" and i.attr["fused_ops"].list.s == [ - b"BiasAdd", - b"Sum", - b"Requantize", - ]: - found_conv_fusion = True - self.assertEqual(found_conv_fusion, True) - - @disable_random() - def test_conv3d_add_addn_relu_fusion(self): - logging.getLogger().info("test_conv3d_add_addn_relu_fusion") - x = tf.compat.v1.placeholder(tf.float32, [1, 128, 64, 64, 16], name="input") - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(x, paddings, "CONSTANT") - top_relu = tf.nn.relu(x_pad) - conv3d_1_weights = tf.compat.v1.get_variable( - "weight1", [3, 3, 3, 16, 32], initializer=tf.compat.v1.random_normal_initializer() - ) - conv3d_2_weights = tf.compat.v1.get_variable( - "weight2", [3, 3, 3, 16, 32], initializer=tf.compat.v1.random_normal_initializer() - ) - conv3d_1 = tf.nn.conv3d(top_relu, conv3d_1_weights, strides=[1, 2, 2, 2, 1], padding="SAME") - conv3d_2 = tf.nn.conv3d(top_relu, conv3d_2_weights, strides=[1, 2, 2, 2, 1], padding="SAME") - add_1 = tf.raw_ops.AddV2(x=conv3d_1, y=conv3d_2, name="addv2_1") - conv3d_3_weights = tf.compat.v1.get_variable( - "weight3", [3, 3, 3, 16, 32], initializer=tf.compat.v1.random_normal_initializer() - ) - conv3d_2 = tf.nn.conv3d(top_relu, conv3d_3_weights, strides=[1, 2, 2, 2, 1], padding="SAME") - add = tf.raw_ops.AddV2(x=add_1, y=conv3d_2, name="addv2_2") - relu = tf.nn.relu(add) - out_name = relu.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 128, 64, 64, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - - found_relu_fusion = False - for i in output_graph.graph_def.node: - if i.op == "_FusedQuantizedConv3D" and i.attr["fused_ops"].list.s == [ - b"BiasAdd", - b"Sum", - b"Relu", - b"Requantize", - ]: - found_relu_fusion = True - self.assertEqual(found_relu_fusion, True) - - @disable_random() - def test_conv3d_leakyrelu_fusion(self): - logging.getLogger().info("test_conv3d_relu_fusion") - x = tf.compat.v1.placeholder(tf.float32, [1, 64, 64, 64, 1], name="input") - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(x, paddings, "CONSTANT") - conv_weights = tf.compat.v1.get_variable( - "weight", [4, 4, 4, 1, 64], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv3d(x_pad, conv_weights, strides=[1, 2, 2, 2, 1], padding="VALID") - relu = tf.nn.leaky_relu(conv) - - out_name = relu.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 64, 64, 64, 1), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - found_conv_fusion = False - - for i in output_graph.graph_def.node: - if i.op == "_FusedQuantizedConv3D" and i.attr["fused_ops"].list.s == [ - b"BiasAdd", - b"LeakyRelu", - b"Dequantize", - ]: - found_conv_fusion = True - break - self.assertEqual(found_conv_fusion, True) - - @disable_random() - def test_conv3d_add_fusion(self): - logging.getLogger().info("test_conv3d_add_fusion") - x = tf.compat.v1.placeholder(tf.float32, [1, 128, 64, 64, 16], name="input") - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(x, paddings, "CONSTANT") - top_relu = tf.nn.relu(x_pad) - conv3d_1_weights = tf.compat.v1.get_variable( - "weight1", [3, 3, 3, 16, 32], initializer=tf.compat.v1.random_normal_initializer() - ) - conv3d_1 = tf.nn.conv3d(top_relu, conv3d_1_weights, strides=[1, 2, 2, 2, 1], padding="SAME") - conv3d_2_weights = tf.compat.v1.get_variable( - "weight2", [3, 3, 3, 16, 32], initializer=tf.compat.v1.random_normal_initializer() - ) - conv3d_2 = tf.nn.conv3d(top_relu, conv3d_2_weights, strides=[1, 2, 2, 2, 1], padding="SAME") - add = tf.raw_ops.AddV2(x=conv3d_1, y=conv3d_2, name="addv2") - out_name = add.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 128, 64, 64, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - - found_conv_fusion = False - for i in output_graph.graph_def.node: - if i.op == "_FusedQuantizedConv3D" and i.attr["fused_ops"].list.s == [ - b"BiasAdd", - b"Sum", - b"Requantize", - ]: - found_conv_fusion = True - self.assertEqual(found_conv_fusion, True) - - @disable_random() - def test_conv3d_add_const_addn_relu_requantize_fusion(self): - logging.getLogger().info("test_conv3d_add_const_addn_relu_requantize_fusion") - x = tf.compat.v1.placeholder(tf.float32, [1, 128, 64, 64, 16], name="input") - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(x, paddings, "CONSTANT") - top_relu = tf.nn.relu(x_pad) - conv3d_1_weights = tf.compat.v1.get_variable( - "weight1", [3, 3, 3, 16, 32], initializer=tf.compat.v1.random_normal_initializer() - ) - conv3d_1 = tf.nn.conv3d(top_relu, conv3d_1_weights, strides=[1, 2, 2, 2, 1], padding="SAME") - y_const = tf.constant(np.random.randn(1, 1, 1, 1, 32), dtype=tf.float32) - add_1 = tf.raw_ops.AddV2(x=conv3d_1, y=y_const, name="addv2_1") - conv3d_2_weights = tf.compat.v1.get_variable( - "weight2", [3, 3, 3, 16, 32], initializer=tf.compat.v1.random_normal_initializer() - ) - conv3d_2 = tf.nn.conv3d(top_relu, conv3d_2_weights, strides=[1, 2, 2, 2, 1], padding="SAME") - add_2 = tf.raw_ops.AddV2(x=add_1, y=conv3d_2, name="addv2_2") - relu = tf.nn.relu(add_2) - out_name = relu.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 128, 64, 64, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - - found_conv_sumadd_fusion = False - found_conv_biasadd_fusion = False - for i in output_graph.graph_def.node: - if i.op == "_FusedQuantizedConv3D": - if str(b"Sum") in str(i.attr["fused_ops"].list.s): - found_conv_sumadd_fusion = True - if str(i.attr["fused_ops"].list.s) == str([b"BiasAdd", b"Sum", b"Relu", b"Requantize"]): - found_conv_biasadd_fusion = True - self.assertEqual(found_conv_sumadd_fusion, True) - self.assertEqual(found_conv_biasadd_fusion, True) - - @disable_random() - def test_conv3d_add_const_addn_fusion(self): - logging.getLogger().info("test_conv3d_add_const_addn_fusion") - x = tf.compat.v1.placeholder(tf.float32, [1, 128, 64, 64, 16], name="input") - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(x, paddings, "CONSTANT") - top_relu = tf.nn.relu(x_pad) - conv3d_1_weights = tf.compat.v1.get_variable( - "weight1", [3, 3, 3, 16, 32], initializer=tf.compat.v1.random_normal_initializer() - ) - conv3d_1 = tf.nn.conv3d(top_relu, conv3d_1_weights, strides=[1, 2, 2, 2, 1], padding="SAME") - y_const = tf.constant(np.random.randn(1, 1, 1, 1, 32), dtype=tf.float32) - add_1 = tf.raw_ops.AddV2(x=conv3d_1, y=y_const, name="addv2_1") - conv3d_2_weights = tf.compat.v1.get_variable( - "weight2", [3, 3, 3, 16, 32], initializer=tf.compat.v1.random_normal_initializer() - ) - conv3d_2 = tf.nn.conv3d(top_relu, conv3d_2_weights, strides=[1, 2, 2, 2, 1], padding="SAME") - add_2 = tf.raw_ops.AddV2(x=add_1, y=conv3d_2, name="addv2_2") - out_name = add_2.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 128, 64, 64, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - - found_conv_fusion = False - for i in output_graph.graph_def.node: - if i.op == "_FusedQuantizedConv3D": - found_conv_fusion = True - self.assertEqual(found_conv_fusion, True) - - @disable_random() - def test_conv3d_add_no_relu_fusion(self): - logging.getLogger().info("test_conv3d_add_no_relu_fusion") - x = tf.compat.v1.placeholder(tf.float32, [1, 128, 64, 64, 16], name="input") - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(x, paddings, "CONSTANT") - top_relu = tf.nn.relu(x_pad) - conv3d_1_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 3, 16, 32], initializer=tf.compat.v1.random_normal_initializer() - ) - conv3d_1 = tf.nn.conv3d(top_relu, conv3d_1_weights, strides=[1, 2, 2, 2, 1], padding="SAME") - y_const = tf.constant(np.random.randn(1, 1, 1, 1, 32), dtype=tf.float32) - add = tf.raw_ops.AddV2(x=conv3d_1, y=y_const, name="addv2") - pooling = tf.nn.max_pool(add, ksize=1, strides=[1, 2, 2, 2, 1], padding="SAME") - out_name = pooling.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 128, 64, 64, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - - for i in output_graph.graph_def.node: - if i.op == "_FusedQuantizedConv3D": - found_conv_fusion = True - break - self.assertEqual(found_conv_fusion, True) - - @disable_random() - def test_conv3d_add_const_relu_fusion(self): - logging.getLogger().info("test_conv3d_add_const_relu_fusion") - x = tf.compat.v1.placeholder(tf.float32, [1, 128, 64, 64, 16], name="input") - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(x, paddings, "CONSTANT") - top_relu = tf.nn.relu(x_pad) - conv3d_1_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 3, 16, 32], initializer=tf.compat.v1.random_normal_initializer() - ) - conv3d_1 = tf.nn.conv3d(top_relu, conv3d_1_weights, strides=[1, 2, 2, 2, 1], padding="SAME") - y_const = tf.constant(np.random.randn(1, 1, 1, 1, 32), dtype=tf.float32) - add = tf.raw_ops.AddV2(x=conv3d_1, y=y_const, name="addv2") - relu = tf.nn.relu(add) - out_name = relu.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 128, 64, 64, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - - for i in output_graph.graph_def.node: - if i.op == "_FusedQuantizedConv3D": - found_conv_fusion = True - break - self.assertEqual(found_conv_fusion, True) - - @disable_random() - def test_conv3d_add_const_leakyrelu_add_fusion(self): - logging.getLogger().info("test_conv3d_add_const_leakyrelu_add_fusion") - x = tf.compat.v1.placeholder(tf.float32, [1, 128, 64, 64, 16], name="input") - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(x, paddings, "CONSTANT") - top_relu = tf.nn.relu(x_pad) - conv3d_1_weights = tf.compat.v1.get_variable( - "weight1", [3, 3, 3, 16, 32], initializer=tf.compat.v1.random_normal_initializer() - ) - conv3d_1 = tf.nn.conv3d(top_relu, conv3d_1_weights, strides=[1, 2, 2, 2, 1], padding="SAME") - y_const = tf.constant(np.random.randn(1, 1, 1, 1, 32), dtype=tf.float32) - add_1 = tf.raw_ops.AddV2(x=conv3d_1, y=y_const, name="addv2_1") - relu = tf.nn.leaky_relu(add_1) - conv3d_2_weights = tf.compat.v1.get_variable( - "weight2", [3, 3, 3, 16, 32], initializer=tf.compat.v1.random_normal_initializer() - ) - conv3d_2 = tf.nn.conv3d(top_relu, conv3d_2_weights, strides=[1, 2, 2, 2, 1], padding="SAME") - add_2 = tf.raw_ops.AddV2(x=relu, y=conv3d_2, name="addv2_2") - out_name = add_2.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 128, 64, 64, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - found_conv_fusion = False - for i in output_graph.graph_def.node: - if i.op == "_FusedQuantizedConv3D": - found_conv_fusion = True - self.assertEqual(found_conv_fusion, True) - - @disable_random() - def test_conv3d_add_addn_non_const_fusion(self): - logging.getLogger().info("test_conv3d_add_addn_non_const_fusion") - x = tf.compat.v1.placeholder(tf.float32, [1, 128, 64, 64, 16], name="input") - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(x, paddings, "CONSTANT") - top_relu = tf.nn.relu(x_pad) - conv3d_1_weights = tf.compat.v1.get_variable( - "weight1", [3, 3, 3, 16, 32], initializer=tf.compat.v1.random_normal_initializer() - ) - conv3d_1 = tf.nn.conv3d(top_relu, conv3d_1_weights, strides=[1, 2, 2, 2, 1], padding="SAME") - conv3d_2_weights = tf.compat.v1.get_variable( - "weight2", [3, 3, 3, 16, 32], initializer=tf.compat.v1.random_normal_initializer() - ) - conv3d_2 = tf.nn.conv3d(top_relu, conv3d_2_weights, strides=[1, 2, 2, 2, 1], padding="SAME") - add_1 = tf.raw_ops.AddV2(x=conv3d_1, y=conv3d_2, name="addv2_1") - conv3d_3_weights = tf.compat.v1.get_variable( - "weight3", [3, 3, 3, 16, 32], initializer=tf.compat.v1.random_normal_initializer() - ) - conv3d_3 = tf.nn.conv3d(top_relu, conv3d_3_weights, strides=[1, 2, 2, 2, 1], padding="SAME") - add = tf.raw_ops.AddV2(x=add_1, y=conv3d_3, name="addv2_2") - out_name = add.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 128, 64, 64, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - - found_conv_fusion = False - for i in output_graph.graph_def.node: - if i.op == "_FusedQuantizedConv3D" and str(i.attr["fused_ops"].list.s) == str( - [b"BiasAdd", b"Sum", b"Requantize"] - ): - found_conv_fusion = True - self.assertEqual(found_conv_fusion, True) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/tfnewapi/test_tensorflow_graph_qdq_conv_fusion.py b/test/tfnewapi/test_tensorflow_graph_qdq_conv_fusion.py deleted file mode 100644 index f10209114c6..00000000000 --- a/test/tfnewapi/test_tensorflow_graph_qdq_conv_fusion.py +++ /dev/null @@ -1,922 +0,0 @@ -# -# -*- coding: utf-8 -*- -# -import logging -import os -import unittest - -import numpy as np -import tensorflow as tf -import yaml -from tensorflow.compat.v1 import graph_util -from tensorflow.python.framework import function - -from neural_compressor.adaptor.tf_utils.util import disable_random - - -def build_fake_yaml(): - fake_yaml = """ - model: - name: fake_yaml - framework: tensorflow - inputs: input - device: cpu - quantization: - model_wise: - weight: - granularity: per_tensor - scheme: sym - dtype: int8 - algorithm: minmax - evaluation: - accuracy: - metric: - topk: 1 - tuning: - strategy: - name: basic - accuracy_criterion: - relative: 0.1 - exit_policy: - performance_only: True - workspace: - path: saved - """ - - y = yaml.load(fake_yaml, Loader=yaml.SafeLoader) - - with open("fake_yaml.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - - f.close() - - -class TestTensorflowQdqConvFusion(unittest.TestCase): - @classmethod - def setUpClass(self): - build_fake_yaml() - - @classmethod - def tearDownClass(self): - os.remove("fake_yaml.yaml") - - @disable_random() - def test_fold_pad_conv(self): - logging.getLogger().info("test_fold_pad_conv") - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(x, paddings, "CONSTANT") - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv2d(x_pad, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - normed = tf.compat.v1.layers.batch_normalization(conv) - relu = tf.nn.relu(normed, name="op_to_store") - out_name = relu.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - found_pad = False - - for i in output_graph.graph_def.node: - if i.op == "Pad": - found_pad = True - break - self.assertEqual(found_pad, False) - - @disable_random() - def test_conv_relu_fusion(self): - logging.getLogger().info("test_conv_relu_fusion") - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - top_relu = tf.nn.relu(x) - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(top_relu, paddings, "CONSTANT") - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv2d(x_pad, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - relu = tf.nn.relu(conv) - - relu6 = tf.nn.relu6(relu, name="op_to_store") - - out_name = relu6.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - found_conv_fusion = True - - for i in output_graph.graph_def.node: - if i.op == "Relu": - found_conv_fusion = False - break - - self.assertEqual(found_conv_fusion, False) - - @disable_random() - def test_conv_biasadd_relu6_fusion(self): - logging.getLogger().info("test_conv_biasadd_relu6_fusion") - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(x, paddings, "CONSTANT") - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv2d(x_pad, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - normed = tf.compat.v1.layers.batch_normalization(conv) - - relu6 = tf.nn.relu6(normed, name="op_to_store") - - out_name = relu6.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - found_conv_fusion = True - - for i in output_graph.graph_def.node: - if i.op == "Relu6": - found_conv_fusion = False - break - self.assertEqual(found_conv_fusion, True) - - @disable_random() - def test_conv_biasadd_swishf32_fusion(self): - logging.getLogger().info("test_conv_biasadd_swishf32_fusion") - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(x, paddings, "CONSTANT") - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv2d(x_pad, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - normed = tf.compat.v1.layers.batch_normalization(conv) - - @function.Defun(tf.float32, func_name="swish_f32") - def swish_f32(x): - return tf.nn.silu(x, beta=1.0) - - swish = swish_f32(normed, name="swish_f32_output_node") - - out_name = swish.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - found_conv_fusion = True - - for i in output_graph.graph_def.node: - if i.op == "swish_f32": - found_conv_fusion = False - break - self.assertEqual(found_conv_fusion, True) - - @disable_random() - def test_conv_addv2_fusion(self): - logging.getLogger().info("test_conv_addv2_fusion") - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - conv1_weights = tf.compat.v1.get_variable( - "weight_conv1", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv1 = tf.nn.conv2d(x, conv1_weights, strides=[1, 2, 2, 1], padding="SAME") - conv2_weights = tf.compat.v1.get_variable( - "weight_conv2", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv2 = tf.nn.conv2d(x, conv2_weights, strides=[1, 2, 2, 1], padding="SAME") - sumadd = tf.raw_ops.AddV2(x=conv1, y=conv2, name="addv2") - - out_name = sumadd.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - - found_conv_fusion = False - for i in output_graph.graph_def.node: - if i.op.find("QuantizedConv2D") != -1: - found_conv_fusion = True - break - - self.assertEqual(found_conv_fusion, True) - - @disable_random() - def test_conv_biasadd_add_relu_fusion(self): - logging.getLogger().info("test_conv_biasadd_add_relu_fusion") - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - top_relu = tf.nn.relu(x) - conv_weights2 = tf.compat.v1.get_variable( - "weight2", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv2 = tf.nn.conv2d(top_relu, conv_weights2, strides=[1, 2, 2, 1], padding="SAME") - normed2 = tf.nn.bias_add(conv2, tf.constant([3.0, 1.2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 12, 2, 3, 4])) - relu = tf.nn.relu(normed2 + tf.constant([3.0])) - relu6 = tf.nn.relu6(relu, name="op_to_store") - - out_name = relu6.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - - found_conv_fusion = False - - for i in output_graph.graph_def.node: - if i.op.find("QuantizedConv2D") != -1: - found_conv_fusion = True - break - - self.assertEqual(found_conv_fusion, True) - - @disable_random() - def test_conv_biasadd_addv2_relu_fallback_fusion_1(self): - logging.getLogger().info("test_conv_biasadd_addv2_relu_fallback_fusion_1") - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - top_relu = tf.nn.leaky_relu(x) - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(x, paddings, "CONSTANT") - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv2d(x_pad, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - normed = tf.compat.v1.layers.batch_normalization(conv) - # relu = tf.nn.relu(normed) - - conv_weights2 = tf.compat.v1.get_variable( - "weight2", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv2 = tf.nn.conv2d(top_relu, conv_weights2, strides=[1, 2, 2, 1], padding="SAME") - normed2 = tf.compat.v1.layers.batch_normalization(conv2) - # relu2 = tf.nn.relu(normed2) - add = tf.raw_ops.AddV2(x=normed, y=normed2, name="addv2") - relu = tf.nn.relu(add) - relu6 = tf.nn.relu6(relu, name="op_to_store") - - out_name = relu6.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - - found_conv_fusion = False - - for i in output_graph.graph_def.node: - if i.op == "_FusedQuantizedConv2D" and i.attr["fused_ops"].list.s == [ - b"BiasAdd", - b"Sum", - b"Relu", - b"Requantize", - ]: - found_conv_fusion = True - break - self.assertEqual(found_conv_fusion, True) - - @disable_random() - def test_conv_fusion_with_last_conv(self): - logging.getLogger().info("test_conv_fusion_with_last_conv") - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - top_relu = tf.nn.relu(x) - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv2d(top_relu, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - normed = tf.compat.v1.layers.batch_normalization(conv) - - relu = tf.nn.relu(normed) - pooling = tf.nn.max_pool(relu, ksize=1, strides=[1, 2, 2, 1], padding="SAME") - conv_weights_2 = tf.compat.v1.get_variable( - "weight2", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv2 = tf.nn.conv2d(pooling, conv_weights_2, strides=[1, 2, 2, 1], padding="VALID") - conv_weights_3 = tf.compat.v1.get_variable( - "weight3", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - relu2 = tf.nn.relu(conv2) - conv3 = tf.nn.conv2d(relu2, conv_weights_3, strides=[1, 2, 2, 1], padding="VALID") - - relu3 = tf.nn.relu(conv3) - relu6 = tf.nn.relu6(relu3, name="op_to_store") - - out_name = relu6.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - - quantize_v2_count = 0 - for i in output_graph.graph_def.node: - if i.op == "QuantizeV2": - quantize_v2_count += 1 - break - - self.assertEqual(quantize_v2_count, 1) - - @disable_random() - def test_conv_fusion_with_max_pooling(self): - logging.getLogger().info("test_conv_fusion_with_max_pooling") - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - - relu = tf.nn.relu(x) - pooling = tf.nn.max_pool(relu, ksize=1, strides=[1, 2, 2, 1], padding="SAME") - conv_weights = tf.compat.v1.get_variable( - "weight2", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv2d(pooling, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - biasadd = tf.compat.v1.layers.batch_normalization(conv, name="op_to_store") - out_name = biasadd.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - - quantized_pool_data_type = None - quantized_conv_data_type = None - for i in output_graph.graph_def.node: - if i.op.find("QuantizedMaxPool") != -1: - quantized_pool_data_type = i.attr["T"].type - if i.op.find("QuantizedConv2D") != -1: - quantized_conv_data_type = i.attr["Tinput"].type - - self.assertNotEqual(quantized_pool_data_type, None) - self.assertEqual(quantized_pool_data_type, quantized_conv_data_type) - - @disable_random() - def test_conv_biasadd_fusion(self): - logging.getLogger().info("test_conv_biasadd_fusion") - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - top_relu = tf.nn.relu(x) - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(top_relu, paddings, "CONSTANT") - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv2d(x_pad, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - normed = tf.compat.v1.layers.batch_normalization(conv, name="op_to_store") - - out_name = normed.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - found_conv_fusion = True - - for i in output_graph.graph_def.node: - if i.op == "batch_normalization/FusedBatchNormV3": - found_conv_fusion = False - break - self.assertEqual(found_conv_fusion, True) - - @disable_random() - def test_depthwiseconv_biasadd_fusion(self): - logging.getLogger().info("test_depthwiseconv_biasadd_fusion") - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - top_relu = tf.nn.relu(x) - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.depthwise_conv2d(top_relu, conv_weights, strides=[1, 1, 1, 1], padding="VALID") - - normed = tf.compat.v1.layers.batch_normalization(conv, name="op_to_store") - - out_name = normed.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - found_conv_fusion = True - - for i in output_graph.graph_def.node: - if i.op == "batch_normalization/FusedBatchNormV3": - found_conv_fusion = False - break - self.assertEqual(found_conv_fusion, True) - - @disable_random() - def test_conv_biasadd_relu_fusion(self): - logging.getLogger().info("test_conv_biasadd_relu_fusion") - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv2d(x, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - normed = tf.compat.v1.layers.batch_normalization(conv) - - relu = tf.nn.relu(normed, name="op_to_store") - - out_name = relu.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - found_conv_fusion = True - - for i in output_graph.graph_def.node: - if i.op == "Relu": - found_conv_fusion = False - break - self.assertEqual(found_conv_fusion, True) - - @disable_random() - def test_conv_biasadd_leakyrelu_fusion(self): - logging.getLogger().info("test_conv_biasadd_leakyrelu_fusion") - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv2d(x, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - normed = tf.compat.v1.layers.batch_normalization(conv) - - leaky_relu = tf.nn.leaky_relu(normed, name="op_to_store") - - out_name = leaky_relu.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - found_conv_fusion = True - - for i in output_graph.graph_def.node: - if i.op == "Leaky_Relu": - found_conv_fusion = False - break - self.assertEqual(found_conv_fusion, True) - - @disable_random() - def test_depthwiseconv_biasadd_relu6_fusion(self): - logging.getLogger().info("test_depthwiseconv_biasadd_relu6_fusion") - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.compat.v1.nn.depthwise_conv2d_native(x, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - normed = tf.compat.v1.layers.batch_normalization(conv) - - relu6 = tf.nn.relu6(normed, name="op_to_store") - - out_name = relu6.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - found_conv_fusion = True - - for i in output_graph.graph_def.node: - if i.op == "Relu6": - found_conv_fusion = False - break - self.assertEqual(found_conv_fusion, True) - - @disable_random() - def test_depthwiseconv_biasadd_relu_fusion(self): - logging.getLogger().info("test_depthwiseconv_biasadd_relu_fusion") - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.compat.v1.nn.depthwise_conv2d_native(x, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - normed = tf.compat.v1.layers.batch_normalization(conv) - - relu6 = tf.nn.relu6(normed, name="op_to_store") - - out_name = relu6.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - found_conv_fusion = True - - for i in output_graph.graph_def.node: - if i.op == "Relu": - found_conv_fusion = False - break - self.assertEqual(found_conv_fusion, True) - - @disable_random() - def test_conv_single_fusion(self): - logging.getLogger().info("test_conv_single_fusion") - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - top_relu = tf.nn.relu(x) - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(top_relu, paddings, "CONSTANT") - conv1_weights = tf.compat.v1.get_variable( - "weight_conv1", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv1 = tf.nn.conv2d(x_pad, conv1_weights, strides=[1, 2, 2, 1], padding="VALID") - matmul_weights = tf.compat.v1.get_variable( - "weight_matmul", [1, 28, 16, 32], initializer=tf.compat.v1.random_normal_initializer() - ) - matmul = tf.matmul(conv1, matmul_weights) - conv2_weights = tf.compat.v1.get_variable( - "weight_conv2", [7, 7, 32, 1], initializer=tf.compat.v1.random_normal_initializer() - ) - conv2 = tf.nn.conv2d(matmul, conv2_weights, strides=[1, 2, 2, 1], padding="VALID") - leaky_relu = tf.nn.leaky_relu(conv2, name="op_to_store") - - out_name = leaky_relu.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - - find_single_qconv = [] - for i in output_graph.graph_def.node: - # BatchMatMul Quantization disabled - if i.op == "_FusedQuantizedConv2D": - find_single_qconv.append(i.attr["fused_ops"].list.s == [b"Requantize"]) - - self.assertEqual(find_single_qconv, [False, False]) - - @disable_random() - def test_conv_fusion_with_last_matmul(self): - logging.getLogger().info("test_conv_fusion_with_last_matmul") - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - top_relu = tf.nn.relu(x) - # paddings = tf.constant([[0, 0], [1, 1], [1, 1], [0, 0]]) - # x_pad = tf.pad(top_relu, paddings, "CONSTANT") - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv2d(top_relu, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - normed = tf.compat.v1.layers.batch_normalization(conv) - - relu = tf.nn.relu(normed) - pooling = tf.nn.max_pool(relu, ksize=1, strides=[1, 2, 2, 1], padding="SAME") - reshape = tf.reshape(pooling, [-1, 3136]) - - y_data = np.random.random([3136, 1]) - - y = tf.constant(y_data, dtype=tf.float32, shape=[3136, 1]) - z = tf.raw_ops.MatMul(a=reshape, b=y, name="matmul_1") - relu1 = tf.nn.relu(z) - y_data_1 = np.random.random([1, 1]) - y_1 = tf.constant(y_data_1, dtype=tf.float32, shape=[1, 1]) - - z_2nd_matmul = tf.raw_ops.MatMul(a=relu1, b=y_1, name="matmul_2") - relu6 = tf.nn.relu6(z_2nd_matmul, name="op_to_store") - - out_name = relu6.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - - quantize_v2_count = 0 - for i in output_graph.graph_def.node: - if i.op == "QuantizeV2": - quantize_v2_count += 1 - break - - self.assertEqual(quantize_v2_count, 1) - - @disable_random() - def test_conv2d_add_const_leakyrelu_add_fusion(self): - logging.getLogger().info("test_conv2d_add_const_leakyrelu_add_fusion") - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(x, paddings, "CONSTANT") - top_relu = tf.nn.relu(x_pad) - conv2d_1_weights = tf.compat.v1.get_variable( - "weight1", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv2d_1 = tf.nn.conv2d(top_relu, conv2d_1_weights, strides=[1, 2, 2, 1], padding="SAME") - y_const = tf.constant(np.random.randn(16), dtype=tf.float32) - add_1 = tf.raw_ops.AddV2(x=conv2d_1, y=y_const, name="addv2_1") - relu = tf.nn.leaky_relu(add_1) - conv2d_2_weights = tf.compat.v1.get_variable( - "weight2", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv2d_2 = tf.nn.conv2d(top_relu, conv2d_2_weights, strides=[1, 2, 2, 1], padding="SAME") - add_2 = tf.raw_ops.AddV2(x=relu, y=conv2d_2, name="addv2_2") - out_name = add_2.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - found_conv_fusion = False - for i in output_graph.graph_def.node: - if i.op == "_FusedQuantizedConv2D": - found_conv_fusion = True - self.assertEqual(found_conv_fusion, True) - - @disable_random() - def test_depthwiseconv_biasadd_leakyrelu_fusion(self): - logging.getLogger().info("test_depthwiseconv_biasadd_leakyrelu_fusion") - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.compat.v1.nn.depthwise_conv2d_native(x, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - normed = tf.compat.v1.layers.batch_normalization(conv) - - leaky_relu = tf.nn.leaky_relu(normed, name="op_to_store") - - out_name = leaky_relu.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - found_conv_fusion = True - - for i in output_graph.graph_def.node: - if i.op == "Relu": - found_conv_fusion = False - break - self.assertEqual(found_conv_fusion, True) - - @disable_random() - def test_conv_biasadd_addv2_relu_fallback_fusion_2(self): - logging.getLogger().info("test_conv_biasadd_addv2_relu_fallback_fusion_2") - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - top_relu = tf.nn.relu(x) - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(top_relu, paddings, "CONSTANT") - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv2d(x_pad, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - normed = tf.compat.v1.layers.batch_normalization(conv) - # relu = tf.nn.relu(normed) - - conv_weights2 = tf.compat.v1.get_variable( - "weight2", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv2 = tf.nn.conv2d(top_relu, conv_weights2, strides=[1, 2, 2, 1], padding="SAME") - normed2 = tf.compat.v1.layers.batch_normalization(conv2) - # relu2 = tf.nn.relu(normed2) - add = tf.raw_ops.AddV2(x=normed, y=normed2, name="addv2") - relu = tf.nn.relu(add) - relu6 = tf.nn.relu6(relu, name="op_to_store") - - out_name = relu6.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - - found_conv_fusion = False - - for i in output_graph.graph_def.node: - if i.op == "_FusedQuantizedConv2D" and i.attr["fused_ops"].list.s == [b"BiasAdd", b"Requantize"]: - found_conv_fusion = True - break - - self.assertEqual(found_conv_fusion, True) - - # fuse conv + biasadd + elu - @disable_random() - def test_conv_biasadd_elu_fusion(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv2d(x, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - normed = tf.compat.v1.layers.batch_normalization(conv) - - elu = tf.nn.elu(normed, name="op_to_store") - - out_name = elu.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - found_conv_fusion = True - - for i in output_graph.graph_def.node: - if i.op == "Elu": - found_conv_fusion = False - break - self.assertEqual(found_conv_fusion, True) - - # fuse conv + biasadd + sigmoid - @disable_random() - def test_conv_biasadd_sigmoid_fusion(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv2d(x, conv_weights, strides=[1, 2, 2, 1], padding="VALID") - normed = tf.compat.v1.layers.batch_normalization(conv) - - sigmoid = tf.math.sigmoid(normed, name="op_to_store") - - out_name = sigmoid.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - found_conv_fusion = True - - for i in output_graph.graph_def.node: - if i.op == "Sigmoid": - found_conv_fusion = False - break - self.assertEqual(found_conv_fusion, True) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/tfnewapi/test_tensorflow_graph_qdq_depthwiseconv_fusion.py b/test/tfnewapi/test_tensorflow_graph_qdq_depthwiseconv_fusion.py deleted file mode 100644 index 32f1650068e..00000000000 --- a/test/tfnewapi/test_tensorflow_graph_qdq_depthwiseconv_fusion.py +++ /dev/null @@ -1,306 +0,0 @@ -# -# -*- coding: utf-8 -*- -# -import os -import unittest - -import numpy as np -import tensorflow as tf -import yaml -from tensorflow.compat.v1 import graph_util -from tensorflow.core.framework import attr_value_pb2, graph_pb2, node_def_pb2 -from tensorflow.python.framework import dtypes, tensor_util - -from neural_compressor.adaptor.tf_utils.util import disable_random - - -def build_fake_yaml(): - fake_yaml = """ - model: - name: fake_yaml - framework: tensorflow - inputs: input - device: cpu - quantization: - model_wise: - weight: - granularity: per_tensor - scheme: sym - dtype: int8 - algorithm: minmax - evaluation: - accuracy: - metric: - topk: 1 - tuning: - strategy: - name: basic - accuracy_criterion: - relative: 0.1 - exit_policy: - performance_only: True - workspace: - path: saved - """ - y = yaml.load(fake_yaml, Loader=yaml.SafeLoader) - with open("fake_yaml.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - f.close() - - -def build_conv2d_biasadd_add_relu6_mul_mul(): - input_node = node_def_pb2.NodeDef() - input_node.name = "input" - input_node.op = "Placeholder" - input_node.attr["dtype"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - - conv1_weight_node = node_def_pb2.NodeDef() - conv1_weight_node.name = "conv1_weights" - conv1_weight_node.op = "Const" - conv1_weight_value = np.float32(np.abs(np.random.randn(3, 3, 3, 32))) - conv1_weight_node.attr["dtype"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - conv1_weight_node.attr["value"].CopyFrom( - attr_value_pb2.AttrValue( - tensor=tensor_util.make_tensor_proto( - conv1_weight_value, conv1_weight_value.dtype.type, conv1_weight_value.shape - ) - ) - ) - - conv1_node = node_def_pb2.NodeDef() - conv1_node.name = "conv1" - conv1_node.op = "Conv2D" - conv1_node.attr["T"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - conv1_node.input.extend([input_node.name, conv1_weight_node.name]) - conv1_node.attr["strides"].CopyFrom( - attr_value_pb2.AttrValue(list=attr_value_pb2.AttrValue.ListValue(i=[1, 1, 1, 1])) - ) - conv1_node.attr["dilations"].CopyFrom( - attr_value_pb2.AttrValue(list=attr_value_pb2.AttrValue.ListValue(i=[1, 1, 1, 1])) - ) - conv1_node.attr["padding"].CopyFrom(attr_value_pb2.AttrValue(s=b"SAME")) - conv1_node.attr["data_format"].CopyFrom(attr_value_pb2.AttrValue(s=b"NHWC")) - - bias_node = node_def_pb2.NodeDef() - bias_node.name = "conv1_bias" - bias_node.op = "Const" - bias_value = np.float32(np.abs(np.random.randn(32))) - bias_node.attr["dtype"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - bias_node.attr["value"].CopyFrom( - attr_value_pb2.AttrValue( - tensor=tensor_util.make_tensor_proto(bias_value, bias_value.dtype.type, bias_value.shape) - ) - ) - - bias_add_node = node_def_pb2.NodeDef() - bias_add_node.name = "conv1_bias_add" - bias_add_node.op = "BiasAdd" - bias_add_node.attr["T"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - bias_add_node.attr["data_format"].CopyFrom(attr_value_pb2.AttrValue(s=b"NHWC")) - bias_add_node.input.extend([conv1_node.name, bias_node.name]) - - offset_node = node_def_pb2.NodeDef() - offset_node.name = "offset" - offset_node.op = "Const" - offset_value = np.float32(np.abs(np.random.randn(1))) - offset_node.attr["dtype"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - offset_node.attr["value"].CopyFrom( - attr_value_pb2.AttrValue( - tensor=tensor_util.make_tensor_proto(offset_value, offset_value.dtype.type, offset_value.shape) - ) - ) - - add_node = node_def_pb2.NodeDef() - add_node.op = "Add" - add_node.name = "add/hard_swish" - add_node.attr["T"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - add_node.input.extend([bias_add_node.name, offset_node.name]) - - relu_node = node_def_pb2.NodeDef() - relu_node.op = "Relu6" - relu_node.name = "relu6/hard_swish" - relu_node.attr["T"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - relu_node.input.extend([add_node.name]) - - mul_node = node_def_pb2.NodeDef() - mul_node.op = "Mul" - mul_node.name = "mul/hard_swish" - mul_node.attr["T"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - mul_node.input.extend([bias_add_node.name, relu_node.name]) - - offset1_node = node_def_pb2.NodeDef() - offset1_node.name = "mul1_offset" - offset1_node.op = "Const" - offset1_value = np.float32(np.abs(np.random.randn(1))) - offset1_node.attr["dtype"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - offset1_node.attr["value"].CopyFrom( - attr_value_pb2.AttrValue( - tensor=tensor_util.make_tensor_proto(offset1_value, offset1_value.dtype.type, offset1_value.shape) - ) - ) - - mul1_node = node_def_pb2.NodeDef() - mul1_node.op = "Mul" - mul1_node.name = "mul1/hard_swish" - mul1_node.attr["T"].CopyFrom(attr_value_pb2.AttrValue(type=dtypes.float32.as_datatype_enum)) - mul1_node.input.extend([mul_node.name, offset1_node.name]) - - test_graph = graph_pb2.GraphDef() - - test_graph.node.extend( - [ - input_node, - conv1_weight_node, - conv1_node, - bias_node, - bias_add_node, - add_node, - relu_node, - offset_node, - offset1_node, - mul_node, - mul1_node, - ] - ) - return test_graph - - -class TestConv2DBiasAddAddReluFusion(unittest.TestCase): - @classmethod - def setUpClass(self): - build_fake_yaml() - - @classmethod - def tearDownClass(self): - os.remove("fake_yaml.yaml") - - @disable_random() - def test_single_depthwiseconv2d_fusion(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.depthwise_conv2d(x, conv_weights, strides=[1, 1, 1, 1], padding="VALID") - out_name = conv.name.split(":")[0] - - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - found_conv_fusion = False - found_dequantize_fusion = False - - for i in output_graph.graph_def.node: - if i.op == "_FusedQuantizedDepthwiseConv2D": - found_conv_fusion = True - if str(i.attr["fused_ops"].list.s) == str([b"Dequantize"]): - found_dequantize_fusion = True - self.assertEqual(found_conv_fusion, True) - self.assertEqual(found_dequantize_fusion, True) - - @disable_random() - def test_depthwiseconv2d_biasadd_fusion(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.depthwise_conv2d(x, conv_weights, strides=[1, 1, 1, 1], padding="VALID") - - normed = tf.compat.v1.layers.batch_normalization(conv, name="op_to_store") - out_name = normed.name.split(":")[0] - - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - found_conv_fusion = False - found_dequantize_fusion = False - - for i in output_graph.graph_def.node: - if i.op == "_FusedQuantizedDepthwiseConv2D": - found_conv_fusion = True - if str(i.attr["fused_ops"].list.s) == str([b"BiasAdd", b"Dequantize"]): - found_dequantize_fusion = True - self.assertEqual(found_conv_fusion, True) - self.assertEqual(found_dequantize_fusion, True) - - @disable_random() - def test_depthwiseconv2dnative_biasadd_add_relu6_mul_mul_fusion(self): - output_graph_def = build_conv2d_biasadd_add_relu6_mul_mul() - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 224, 224, 3), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - - found_conv_fusion = False - - for i in output_graph.graph_def.node: - if i.op == "_FusedQuantizedConv2D": - found_conv_fusion = True - break - self.assertEqual(found_conv_fusion, True) - - @disable_random() - def test_depthwiseconv2d_biasadd_leakyrelu_fusion(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.depthwise_conv2d(x, conv_weights, strides=[1, 1, 1, 1], padding="VALID") - - normed = tf.compat.v1.layers.batch_normalization(conv, name="op_to_store") - - leakyrelu = tf.nn.leaky_relu(normed) - out_name = leakyrelu.name.split(":")[0] - - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - found_conv_fusion = False - - for i in output_graph.graph_def.node: - if i.op == "_FusedQuantizedDepthwiseConv2D": - found_conv_fusion = True - break - - self.assertEqual(found_conv_fusion, True) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/tfnewapi/test_tensorflow_graph_qdq_matmul_fusion.py b/test/tfnewapi/test_tensorflow_graph_qdq_matmul_fusion.py deleted file mode 100644 index 0040c4d53a5..00000000000 --- a/test/tfnewapi/test_tensorflow_graph_qdq_matmul_fusion.py +++ /dev/null @@ -1,1258 +0,0 @@ -# -# -*- coding: utf-8 -*- -# -import os -import unittest - -import numpy as np -import tensorflow.compat.v1 as tf -import yaml -from tensorflow.python.framework import dtypes - -from neural_compressor.adaptor.tensorflow import TensorflowQuery -from neural_compressor.adaptor.tf_utils.util import disable_random - - -def build_fake_yaml(): - fake_yaml = """ - model: - name: fake_yaml - framework: tensorflow - inputs: x - outputs: op_to_store - device: cpu - evaluation: - accuracy: - metric: - topk: 1 - quantization: - model_wise: - weight: - granularity: per_tensor - scheme: sym - dtype: int8 - algorithm: minmax - tuning: - strategy: - name: basic - accuracy_criterion: - relative: 0.01 - exit_policy: - performance_only: True - workspace: - path: saved - """ - y = yaml.load(fake_yaml, Loader=yaml.SafeLoader) - with open("fake_yaml.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - f.close() - - -class TestGraphMatMulFusion(unittest.TestCase): - @classmethod - def setUpClass(self): - build_fake_yaml() - self.op_wise_sequences = TensorflowQuery( - local_config_file=os.path.join(os.path.dirname(__file__), "../../neural_compressor/adaptor/tensorflow.yaml") - ).get_eightbit_patterns(True) - - @classmethod - def tearDownClass(self): - os.remove("fake_yaml.yaml") - - @disable_random() - def test_matmul_biasadd_relu_requantize_fusion(self): - g = tf.Graph() - with g.as_default(): - x_data = np.array([[0.1, 0.2], [0.2, 0.3]]) - y_data = np.array([[1, 2], [3, 4]], dtype=np.float32) - x = tf.placeholder(tf.float32, shape=[2, 2], name="x") - y = tf.constant(y_data, dtype=tf.float32, shape=[2, 2]) - z = tf.matmul(x, y) - z = tf.nn.bias_add(z, [1, 2]) - z = tf.nn.relu(z, name="op_to_store") - found_quantized_matmul = False - with tf.Session() as sess: - sess.run(z, feed_dict={x: x_data, y: y_data}) - float_graph_def = sess.graph.as_graph_def() - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(2, 2), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.eval_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.model = float_graph_def - output_graph = quantizer.fit() - - for i in output_graph.graph_def.node: - if i.op == "_QuantizedMatMul" and i.attr["fused_ops"].list.s == [ - b"BiasAdd", - b"Relu", - b"Dequantize", - ]: - found_quantized_matmul = True - break - self.assertEqual(found_quantized_matmul, True) - - @disable_random() - def test_first_matmul_biasadd_relu_fusion(self): - x_data = np.array([[0.1, 0.2], [0.2, 0.3]]) - y_data = np.array([[1, 2], [3, 4]], dtype=np.float32) - x = tf.placeholder(tf.float32, shape=[2, 2], name="x") - y = tf.constant(y_data, dtype=tf.float32, shape=[2, 2]) - z = tf.matmul(x, y) - z = tf.nn.bias_add(z, [1, 2]) - z = tf.nn.relu(z, name="op_to_store") - - with tf.Session() as sess: - sess.run(z, feed_dict={x: x_data, y: y_data}) - float_graph_def = sess.graph.as_graph_def() - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(2, 2), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.eval_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.model = float_graph_def - output_graph = quantizer.fit() - - found_quantized_matmul = False - for i in output_graph.graph_def.node: - if ( - i.op == "QuantizeV2" - and i.name == "MatMul_eightbit_quantize_x" - and i.attr["T"].type == dtypes.quint8 - ): - found_quantized_matmul = True - break - - self.assertEqual(found_quantized_matmul, True) - - @disable_random() - def test_matmul_biasadd_requantize_dequantize_fusion(self): - g = tf.Graph() - with g.as_default(): - x_data = np.array([[0.1, 0.2], [0.2, 0.3]]) - y_data = np.array([[1, 2], [3, 4]], dtype=np.float32) - x = tf.placeholder(tf.float32, shape=[2, 2], name="x") - y = tf.constant(y_data, dtype=tf.float32, shape=[2, 2]) - z = tf.matmul(x, y) - z = tf.nn.bias_add(z, [1, 2]) - z = tf.identity(z, name="op_to_store") - found_quantized_matmul = False - - with tf.Session() as sess: - sess.run(z, feed_dict={x: x_data, y: y_data}) - float_graph_def = sess.graph.as_graph_def() - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(2, 2), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.eval_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.model = float_graph_def - output_graph = quantizer.fit() - - for i in output_graph.graph_def.node: - if i.op == "_QuantizedMatMul" and i.attr["fused_ops"].list.s == [b"BiasAdd", b"Dequantize"]: - found_quantized_matmul = True - break - - self.assertEqual(found_quantized_matmul, True) - - @disable_random() - def test_matmul_biasadd_requantize_dequantize_last_fusion(self): - g = tf.Graph() - with g.as_default(): - x_data = np.array([[0.1, 0.2], [0.2, 0.3]]) - y_data = np.array([[1, 2], [3, 4]], dtype=np.float32) - x = tf.placeholder(tf.float32, shape=[2, 2], name="x") - y = tf.constant(y_data, dtype=tf.float32, shape=[2, 2]) - z = tf.matmul(x, y) - z = tf.nn.bias_add(z, [1, 2], name="op_to_store") - found_quantized_matmul = False - with tf.Session() as sess: - sess.run(z, feed_dict={x: x_data, y: y_data}) - float_graph_def = sess.graph.as_graph_def() - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(2, 2), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.eval_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.model = float_graph_def - output_graph = quantizer.fit() - - for i in output_graph.graph_def.node: - if ( - i.op == "_QuantizedMatMul" - and i.name == "op_to_store" - and i.attr["fused_ops"].list.s == [b"BiasAdd", b"Dequantize"] - ): - found_quantized_matmul = True - break - self.assertEqual(found_quantized_matmul, True) - - @disable_random() - def test_matmul_fusion_with_transpose_b_true(self): - g = tf.Graph() - with g.as_default(): - x_data = np.array([[0.1, 0.2], [0.2, 0.3]]) - y_data = np.array([[1, 2], [3, 4]], dtype=np.float32) - x = tf.placeholder(tf.float32, shape=[2, 2], name="x") - y = tf.constant(y_data, dtype=tf.float32, shape=[2, 2]) - z = tf.matmul(x, y, name="no_quant_matmul", transpose_b=True) - z = tf.nn.relu6(z, name="op_to_store") - found_quantized_matmul = False - - with tf.Session() as sess: - sess.run(z, feed_dict={x: x_data, y: y_data}) - float_graph_def = sess.graph.as_graph_def() - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(2, 2), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.eval_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.model = float_graph_def - output_graph = quantizer.fit() - - for i in output_graph.graph_def.node: - if i.op == "_QuantizedMatMul": - found_quantized_matmul = True - break - self.assertEqual(found_quantized_matmul, True) - - @disable_random() - def test_matmul_dummybiasadd_relu6_fusion(self): - g = tf.Graph() - with g.as_default(): - x_data = np.array([[0.1, 0.2], [0.2, 0.3]]) - y_data = np.array([[1, 2], [3, 4]], dtype=np.float32) - x = tf.placeholder(tf.float32, shape=[2, 2], name="x") - y = tf.constant(y_data, dtype=tf.float32, shape=[2, 2]) - z = tf.matmul(x, y, name="quant_matmul") - z = tf.nn.relu6(z, name="op_to_store") - found_quantized_matmul = False - - with tf.Session() as sess: - sess.run(z, feed_dict={x: x_data, y: y_data}) - float_graph_def = sess.graph.as_graph_def() - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(2, 2), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.eval_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.model = float_graph_def - output_graph = quantizer.fit() - - for i in output_graph.graph_def.node: - if i.op == "_QuantizedMatMul" and i.name == "op_to_store": - found_quantized_matmul = True - break - self.assertEqual(found_quantized_matmul, True) - - @disable_random() - def test_matmul_with_reshape_transpose(self): - g = tf.Graph() - with g.as_default(): - x_data = np.array([[0.1, 0.2], [0.2, 0.3]]) - y_data = np.array([[1, 2], [3, 4]], dtype=np.float32) - x = tf.placeholder(tf.float32, shape=[2, 2], name="x") - y = tf.constant(y_data, dtype=tf.float32, shape=[2, 2]) - transpose = tf.transpose(y, perm=[1, 0]) - reshape = tf.reshape(transpose, [2, 2]) - z = tf.matmul(x, reshape, name="no_quant_matmul") - z = tf.nn.bias_add(z, [1, 2], name="op_to_store") - found_quantized_matmul = True - - with tf.Session() as sess: - sess.run(z, feed_dict={x: x_data, y: y_data}) - float_graph_def = sess.graph.as_graph_def() - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(2, 2), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.eval_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.model = float_graph_def - output_graph = quantizer.fit() - for i in output_graph.graph_def.node: - if i.op == "MatMul": - found_quantized_matmul = False - break - self.assertEqual(found_quantized_matmul, True) - - @disable_random() - def test_matmul_with_add(self): - g = tf.Graph() - with g.as_default(): - x_data = np.array([[0.1, 0.2], [0.2, 0.3]]) - y_data = np.array([[1, 2], [3, 4]], dtype=np.float32) - x = tf.placeholder(tf.float32, shape=[2, 2], name="x") - y = tf.constant(y_data, dtype=tf.float32, shape=[2, 2]) - transpose = tf.transpose(y, perm=[1, 0]) - reshape = tf.reshape(transpose, [2, 2]) - z = tf.matmul(x, reshape, name="no_quant_matmul") - z = tf.math.add(z, [1, 2], name="op_to_store") - found_quantized_matmul = True - - with tf.Session() as sess: - sess.run(z, feed_dict={x: x_data, y: y_data}) - float_graph_def = sess.graph.as_graph_def() - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(2, 2), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.eval_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.model = float_graph_def - output_graph = quantizer.fit() - for i in output_graph.graph_def.node: - if i.op == "MatMul": - found_quantized_matmul = False - break - self.assertEqual(found_quantized_matmul, True) - - @disable_random() - def test_matmul_biasadd_requantize_dequantize_fusion_with_softmax(self): - g = tf.Graph() - with g.as_default(): - x_data = np.array([[0.1, 0.2], [0.2, 0.3]]) - y_data = np.array([[1, 2], [3, 4]], dtype=np.float32) - x = tf.placeholder(tf.float32, shape=[2, 2], name="x") - y = tf.constant(y_data, dtype=tf.float32, shape=[2, 2]) - z = tf.matmul(x, y) - biasadd = tf.nn.bias_add(z, [1, 2]) - biasadd1 = tf.nn.bias_add(biasadd, [1, 1]) - - y1 = tf.constant(x_data, dtype=tf.float32, shape=[2, 2]) - matmul1 = tf.matmul(biasadd1, y1) - - biasadd2 = tf.nn.bias_add(matmul1, [1, 1]) - - z = tf.nn.softmax(biasadd2, name="op_to_store") - found_quantized_matmul = False - if tf.version.VERSION < "2.2.0": - found_quantized_matmul = False - else: - with tf.Session() as sess: - sess.run(z, feed_dict={x: x_data, y: y_data}) - float_graph_def = sess.graph.as_graph_def() - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(2, 2), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.eval_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.model = float_graph_def - output_graph = quantizer.fit() - - count = 0 - for i in output_graph.model.as_graph_def().node: - if i.op == "_QuantizedMatMul": - count += 1 - found_quantized_matmul = bool(count > 1) - self.assertEqual(found_quantized_matmul, False) - - def test_matmul_biasadd_relu_non_const_weight(self): - g = tf.Graph() - with g.as_default(): - x_data = np.array([[0.1, 0.2], [0.2, 0.3]]) - x = tf.placeholder(tf.float32, shape=[2, 2], name="x") - y = tf.matmul(x, x, name="quant_matmul_non_const_weight") - biasadd = tf.nn.bias_add(y, [1, 2]) - z = tf.nn.relu(biasadd) - found_quantized_matmul = True - - with tf.Session() as sess: - sess.run(z, feed_dict={x: x_data}) - float_graph_def = sess.graph.as_graph_def() - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(2, 2), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.eval_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.model = float_graph_def - output_graph = quantizer.fit() - - for i in output_graph.graph_def.node: - if i.op == "MatMul": - found_quantized_matmul = False - break - self.assertEqual(found_quantized_matmul, True) - - def test_matmul_biasadd_non_const_weight(self): - g = tf.Graph() - with g.as_default(): - x_data = np.array([[0.1, 0.2], [0.2, 0.3]]) - x = tf.placeholder(tf.float32, shape=[2, 2], name="x") - y = tf.matmul(x, x, name="quant_matmul_non_const_weight") - z = tf.nn.bias_add(y, [1, 2]) - found_quantized_matmul = True - - with tf.Session() as sess: - sess.run(z, feed_dict={x: x_data}) - float_graph_def = sess.graph.as_graph_def() - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(2, 2), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.eval_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.model = float_graph_def - output_graph = quantizer.fit() - - for i in output_graph.graph_def.node: - if i.op == "MatMul": - found_quantized_matmul = False - break - self.assertEqual(found_quantized_matmul, True) - - @disable_random() - def test_matmul_with_dummy_biasadd(self): - g = tf.Graph() - with g.as_default(): - x_data = np.array([[0.1, 0.2], [0.2, 0.3]]) - y_data = np.array([[1, 2], [3, 4]], dtype=np.float32) - x = tf.placeholder(tf.float32, shape=[2, 2], name="x") - y = tf.constant(y_data, dtype=tf.float32, shape=[2, 2]) - z = tf.matmul(x, y, name="no_quant_matmul") - z = tf.identity(z, name="op_to_store") - found_quantized_matmul = True - - with tf.Session() as sess: - sess.run(z, feed_dict={x: x_data, y: y_data}) - float_graph_def = sess.graph.as_graph_def() - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(2, 2), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.eval_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.model = float_graph_def - output_graph = quantizer.fit() - - for i in output_graph.graph_def.node: - if i.op == "MatMul": - found_quantized_matmul = False - break - self.assertEqual(found_quantized_matmul, True) - - @disable_random() - def test_first_matmul_addv2_relu_fusion(self): - x_data = np.array([[0.1, 0.2], [0.2, 0.3]]) - y_data = np.array([[1, 2], [3, 4]], dtype=np.float32) - x = tf.placeholder(tf.float32, shape=[2, 2], name="x") - y = tf.constant(y_data, dtype=tf.float32, shape=[2, 2]) - a = tf.matmul(x, y) - b = tf.matmul(x, y) - c = tf.nn.relu(b) - add = tf.raw_ops.AddV2(x=a, y=c, name="addv2") - z = tf.nn.relu(add, name="op_to_store") - - with tf.Session() as sess: - sess.run(z, feed_dict={x: x_data, y: y_data}) - float_graph_def = sess.graph.as_graph_def() - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(2, 2), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.eval_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.model = float_graph_def - output_graph = quantizer.fit() - - found_quantized_matmul = False - for i in output_graph.graph_def.node: - if i.op == "_QuantizedMatMul": - found_quantized_matmul = True - break - - self.assertEqual(found_quantized_matmul, True) - - # batchmatmul quantization disabled temporarily for its bad performance - """ - @disable_random() - def test_batchmatmulv2_dequantize_fusion(self): - g = tf.Graph() - with g.as_default(): - x_data = np.array([[0.1, 0.2], [0.5, 0.6]]) - y_data = np.array([[1, 2], [3, 4]], dtype=np.float32) - x = tf.placeholder(tf.float32, shape=[2, 2], name='x') - y = tf.constant(y_data, dtype=tf.float32, shape=[2, 2]) - z = tf.raw_ops.BatchMatMulV2(x=x, y=y) - z = tf.nn.bias_add(z, [1, 2]) - z = tf.nn.relu(z, name='op_to_store') - found_quantized_matmul = False - with tf.Session() as sess: - sess.run(z, feed_dict={x: x_data, y: y_data}) - float_graph_def = sess.graph.as_graph_def() - - from neural_compressor.experimental import Quantization, common - quantizer = Quantization('fake_yaml.yaml') - dataset = quantizer.dataset('dummy', shape=(2, 2), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.eval_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.model = float_graph_def - output_graph = quantizer.fit() - for i in output_graph.graph_def.node: - if i.op == '_QuantizedBatchMatMul': - found_quantized_matmul = True - break - self.assertEqual(found_quantized_matmul, True) - - @disable_random() - def test_batchmatmulv2_mul_dequantize_fusion(self): - np_input = np.random.randn(1, 2, 4, 6).astype(np.float32) - np_filter = np.random.randn(1, 2, 6, 5).astype(np.float32) - input = tf.compat.v1.placeholder(dtype=tf.float32, shape=(1, 2, 4, 6)) - filter = tf.constant(np_filter) - mul = tf.constant(0.2) - z = tf.raw_ops.BatchMatMulV2(x=input, y=filter) - z = tf.raw_ops.Mul(x=z, y=mul) - z = tf.nn.relu(z, name='op_to_store') - - with tf.Session() as sess: - sess.run(z, feed_dict={input: np_input, filter: np_filter}) - float_graph_def = sess.graph.as_graph_def() - - from neural_compressor.experimental import Quantization, common - quantizer = Quantization('fake_yaml.yaml') - dataset = quantizer.dataset('dummy', shape=(1, 2, 4, 6), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.eval_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.model = float_graph_def - output_graph = quantizer.fit() - found_quantized_matmul = False - for i in output_graph.graph_def.node: - if i.op == '_QuantizedBatchMatMul': - found_quantized_matmul = True - break - - self.assertEqual(found_quantized_matmul, True) - - @disable_random() - def test_batchmatmulv2_add_dequantize_fusion(self): - np_input = np.random.randn(1, 2, 4, 6).astype(np.float32) - np_filter = np.random.randn(1, 2, 6, 5).astype(np.float32) - np_add = np.random.randn(1, 2, 4, 5).astype(np.float32) - input = tf.compat.v1.placeholder(dtype=tf.float32, shape=(1, 2, 4, 6)) - filter = tf.constant(np_filter) - add = tf.constant(np_add) - z = tf.raw_ops.BatchMatMulV2(x=input, y=filter) - z = tf.raw_ops.Add(x=z, y=add) - z = tf.nn.relu(z, name='op_to_store') - - with tf.Session() as sess: - sess.run(z, feed_dict={input: np_input, filter: np_filter}) - float_graph_def = sess.graph.as_graph_def() - - from neural_compressor.experimental import Quantization, common - quantizer = Quantization('fake_yaml.yaml') - dataset = quantizer.dataset('dummy', shape=(1, 2, 4, 6), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.eval_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.model = float_graph_def - output_graph = quantizer.fit() - found_quantized_matmul = False - for i in output_graph.graph_def.node: - if i.op == '_QuantizedBatchMatMul': - found_quantized_matmul = True - break - - self.assertEqual(found_quantized_matmul, True) - - @disable_random() - def test_batchmatmulv2_mul_add_dequantize_fusion(self): - np_input = np.random.randn(1, 2, 4, 6).astype(np.float32) - np_filter = np.random.randn(1, 2, 6, 5).astype(np.float32) - np_add = np.random.randn(1, 2, 4, 5).astype(np.float32) - input = tf.compat.v1.placeholder(dtype=tf.float32, shape=(1, 2, 4, 6)) - filter = tf.constant(np_filter) - mul = tf.constant(0.2) - add = tf.constant(np_add) - z = tf.raw_ops.BatchMatMulV2(x=input, y=filter) - z = tf.raw_ops.Mul(x=z, y=mul) - z = tf.raw_ops.Add(x=z, y=add) - z = tf.nn.relu(z, name='op_to_store') - - with tf.Session() as sess: - sess.run(z, feed_dict={input: np_input, filter: np_filter}) - float_graph_def = sess.graph.as_graph_def() - - from neural_compressor.experimental import Quantization, common - quantizer = Quantization('fake_yaml.yaml') - dataset = quantizer.dataset('dummy', shape=(1, 2, 4, 6), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.eval_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.model = float_graph_def - output_graph = quantizer.fit() - found_quantized_matmul = False - for i in output_graph.graph_def.node: - if i.op == '_QuantizedBatchMatMul': - found_quantized_matmul = True - break - - self.assertEqual(found_quantized_matmul, True) - """ - - @disable_random() - def test_matmul_biasadd_relu6_fusion(self): - g = tf.Graph() - with g.as_default(): - x_data = np.array([[0.1, 0.2], [0.2, 0.3]]) - y_data = np.array([[1, 2], [3, 4]], dtype=np.float32) - x = tf.placeholder(tf.float32, shape=[2, 2], name="x") - y = tf.constant(y_data, dtype=tf.float32, shape=[2, 2]) - z = tf.matmul(x, y) - z = tf.nn.bias_add(z, [1, 2]) - z = tf.nn.relu6(z, name="op_to_store") - found_quantized_matmul = False - with tf.Session() as sess: - sess.run(z, feed_dict={x: x_data, y: y_data}) - float_graph_def = sess.graph.as_graph_def() - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(2, 2), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.eval_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.model = float_graph_def - output_graph = quantizer.fit() - - for i in output_graph.graph_def.node: - if i.op == "_QuantizedMatMul" and i.attr["fused_ops"].list.s == [ - b"BiasAdd", - b"Relu6", - b"Dequantize", - ]: - found_quantized_matmul = True - break - self.assertEqual(found_quantized_matmul, True) - - @disable_random() - def test_matmul_biasadd_leakyrelu_fusion(self): - g = tf.Graph() - with g.as_default(): - x_data = np.array([[0.1, 0.2], [0.2, 0.3]]) - y_data = np.array([[1, 2], [3, 4]], dtype=np.float32) - x = tf.placeholder(tf.float32, shape=[2, 2], name="x") - y = tf.constant(y_data, dtype=tf.float32, shape=[2, 2]) - z = tf.matmul(x, y) - z = tf.nn.bias_add(z, [1, 2]) - z = tf.nn.leaky_relu(z, name="op_to_store") - found_quantized_matmul = False - with tf.Session() as sess: - sess.run(z, feed_dict={x: x_data, y: y_data}) - float_graph_def = sess.graph.as_graph_def() - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(2, 2), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.eval_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.model = float_graph_def - output_graph = quantizer.fit() - - for i in output_graph.graph_def.node: - if i.op == "_QuantizedMatMul" and i.attr["fused_ops"].list.s == [ - b"BiasAdd", - b"LeakyRelu", - b"Dequantize", - ]: - found_quantized_matmul = True - break - self.assertEqual(found_quantized_matmul, True) - - @disable_random() - def test_matmul_biasadd_geluapproximate_fusion(self): - g = tf.Graph() - with g.as_default(): - x_data = np.array([[0.1, 0.2], [0.2, 0.3]]) - y_data = np.array([[1, 2], [3, 4]], dtype=np.float32) - x = tf.placeholder(tf.float32, shape=[2, 2], name="x") - y = tf.constant(y_data, dtype=tf.float32, shape=[2, 2]) - z = tf.matmul(x, y) - z = tf.nn.bias_add(z, [1, 2]) - z = tf.nn.gelu(z, approximate=True, name="op_to_store") - found_quantized_matmul = False - with tf.Session() as sess: - sess.run(z, feed_dict={x: x_data, y: y_data}) - float_graph_def = sess.graph.as_graph_def() - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(2, 2), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.eval_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.model = float_graph_def - output_graph = quantizer.fit() - - for i in output_graph.graph_def.node: - if i.op == "_QuantizedMatMul" and i.attr["fused_ops"].list.s == [ - b"BiasAdd", - b"GeluApproximate", - b"Dequantize", - ]: - found_quantized_matmul = True - break - self.assertEqual(found_quantized_matmul, True) - - @disable_random() - def test_matmul_biasadd_geluexact_fusion(self): - g = tf.Graph() - with g.as_default(): - x_data = np.array([[0.1, 0.2], [0.2, 0.3]]) - y_data = np.array([[1, 2], [3, 4]], dtype=np.float32) - x = tf.placeholder(tf.float32, shape=[2, 2], name="x") - y = tf.constant(y_data, dtype=tf.float32, shape=[2, 2]) - z = tf.matmul(x, y) - z = tf.nn.bias_add(z, [1, 2]) - z = tf.nn.gelu(z, name="op_to_store") - found_quantized_matmul = False - with tf.Session() as sess: - sess.run(z, feed_dict={x: x_data, y: y_data}) - float_graph_def = sess.graph.as_graph_def() - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(2, 2), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.eval_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.model = float_graph_def - output_graph = quantizer.fit() - - for i in output_graph.graph_def.node: - if i.op == "_QuantizedMatMul" and i.attr["fused_ops"].list.s == [ - b"BiasAdd", - b"GeluExact", - b"Dequantize", - ]: - found_quantized_matmul = True - break - self.assertEqual(found_quantized_matmul, True) - - @disable_random() - def test_matmul_biasadd_elu_fusion(self): - g = tf.Graph() - with g.as_default(): - x_data = np.array([[0.1, 0.2], [0.2, 0.3]]) - y_data = np.array([[1, 2], [3, 4]], dtype=np.float32) - x = tf.placeholder(tf.float32, shape=[2, 2], name="x") - y = tf.constant(y_data, dtype=tf.float32, shape=[2, 2]) - z = tf.matmul(x, y) - z = tf.nn.bias_add(z, [1, 2]) - z = tf.nn.elu(z, name="op_to_store") - found_quantized_matmul = False - with tf.Session() as sess: - sess.run(z, feed_dict={x: x_data, y: y_data}) - float_graph_def = sess.graph.as_graph_def() - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(2, 2), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.eval_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.model = float_graph_def - output_graph = quantizer.fit() - - for i in output_graph.graph_def.node: - if i.op == "_QuantizedMatMul" and i.attr["fused_ops"].list.s == [b"BiasAdd", b"Elu", b"Dequantize"]: - found_quantized_matmul = True - break - self.assertEqual(found_quantized_matmul, True) - - @disable_random() - def test_matmul_biasadd_tanh_fusion(self): - g = tf.Graph() - with g.as_default(): - x_data = np.array([[0.1, 0.2], [0.2, 0.3]]) - y_data = np.array([[1, 2], [3, 4]], dtype=np.float32) - x = tf.placeholder(tf.float32, shape=[2, 2], name="x") - y = tf.constant(y_data, dtype=tf.float32, shape=[2, 2]) - z = tf.matmul(x, y) - z = tf.nn.bias_add(z, [1, 2]) - z = tf.math.tanh(z, name="op_to_store") - found_quantized_matmul = False - with tf.Session() as sess: - sess.run(z, feed_dict={x: x_data, y: y_data}) - float_graph_def = sess.graph.as_graph_def() - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(2, 2), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.eval_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.model = float_graph_def - output_graph = quantizer.fit() - - for i in output_graph.graph_def.node: - if i.op == "_QuantizedMatMul" and i.attr["fused_ops"].list.s == [ - b"BiasAdd", - b"Tanh", - b"Dequantize", - ]: - found_quantized_matmul = True - break - self.assertEqual(found_quantized_matmul, True) - - @disable_random() - def test_matmul_biasadd_sigmoid_fusion(self): - g = tf.Graph() - with g.as_default(): - x_data = np.array([[0.1, 0.2], [0.2, 0.3]]) - y_data = np.array([[1, 2], [3, 4]], dtype=np.float32) - x = tf.placeholder(tf.float32, shape=[2, 2], name="x") - y = tf.constant(y_data, dtype=tf.float32, shape=[2, 2]) - z = tf.matmul(x, y) - z = tf.nn.bias_add(z, [1, 2]) - z = tf.math.sigmoid(z, name="op_to_store") - found_quantized_matmul = False - with tf.Session() as sess: - sess.run(z, feed_dict={x: x_data, y: y_data}) - float_graph_def = sess.graph.as_graph_def() - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(2, 2), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.eval_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.model = float_graph_def - output_graph = quantizer.fit() - - for i in output_graph.graph_def.node: - if i.op == "_QuantizedMatMul" and i.attr["fused_ops"].list.s == [ - b"BiasAdd", - b"Sigmoid", - b"Dequantize", - ]: - found_quantized_matmul = True - break - self.assertEqual(found_quantized_matmul, True) - - @disable_random() - def test_matmul_dummy_biasadd_relu_fusion(self): - g = tf.Graph() - with g.as_default(): - x_data = np.array([[0.1, 0.2], [0.2, 0.3]]) - y_data = np.array([[1, 2], [3, 4]], dtype=np.float32) - x = tf.placeholder(tf.float32, shape=[2, 2], name="x") - y = tf.constant(y_data, dtype=tf.float32, shape=[2, 2]) - z = tf.matmul(x, y, name="quant_matmul") - z = tf.nn.relu(z, name="op_to_store") - found_quantized_matmul = False - - with tf.Session() as sess: - sess.run(z, feed_dict={x: x_data, y: y_data}) - float_graph_def = sess.graph.as_graph_def() - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(2, 2), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.eval_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.model = float_graph_def - output_graph = quantizer.fit() - - for i in output_graph.graph_def.node: - if i.op == "_QuantizedMatMul" and i.attr["fused_ops"].list.s == [ - b"BiasAdd", - b"Relu", - b"Dequantize", - ]: - found_quantized_matmul = True - break - self.assertEqual(found_quantized_matmul, True) - - @disable_random() - def test_matmul_dummy_biasadd_relu6_fusion(self): - g = tf.Graph() - with g.as_default(): - x_data = np.array([[0.1, 0.2], [0.2, 0.3]]) - y_data = np.array([[1, 2], [3, 4]], dtype=np.float32) - x = tf.placeholder(tf.float32, shape=[2, 2], name="x") - y = tf.constant(y_data, dtype=tf.float32, shape=[2, 2]) - z = tf.matmul(x, y) - z = tf.nn.relu6(z, name="op_to_store") - found_quantized_matmul = False - with tf.Session() as sess: - sess.run(z, feed_dict={x: x_data, y: y_data}) - float_graph_def = sess.graph.as_graph_def() - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(2, 2), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.eval_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.model = float_graph_def - output_graph = quantizer.fit() - - for i in output_graph.graph_def.node: - if i.op == "_QuantizedMatMul" and i.attr["fused_ops"].list.s == [ - b"BiasAdd", - b"Relu6", - b"Dequantize", - ]: - found_quantized_matmul = True - break - self.assertEqual(found_quantized_matmul, True) - - @disable_random() - def test_matmul_dummy_biasadd_leakyrelu_fusion(self): - g = tf.Graph() - with g.as_default(): - x_data = np.array([[0.1, 0.2], [0.2, 0.3]]) - y_data = np.array([[1, 2], [3, 4]], dtype=np.float32) - x = tf.placeholder(tf.float32, shape=[2, 2], name="x") - y = tf.constant(y_data, dtype=tf.float32, shape=[2, 2]) - z = tf.matmul(x, y) - z = tf.nn.leaky_relu(z, name="op_to_store") - found_quantized_matmul = False - with tf.Session() as sess: - sess.run(z, feed_dict={x: x_data, y: y_data}) - float_graph_def = sess.graph.as_graph_def() - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(2, 2), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.eval_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.model = float_graph_def - output_graph = quantizer.fit() - - for i in output_graph.graph_def.node: - if i.op == "_QuantizedMatMul" and i.attr["fused_ops"].list.s == [ - b"BiasAdd", - b"LeakyRelu", - b"Dequantize", - ]: - found_quantized_matmul = True - break - self.assertEqual(found_quantized_matmul, True) - - @disable_random() - def test_matmul_dummy_biasadd_geluapproximate_fusion(self): - g = tf.Graph() - with g.as_default(): - x_data = np.array([[0.1, 0.2], [0.2, 0.3]]) - y_data = np.array([[1, 2], [3, 4]], dtype=np.float32) - x = tf.placeholder(tf.float32, shape=[2, 2], name="x") - y = tf.constant(y_data, dtype=tf.float32, shape=[2, 2]) - z = tf.matmul(x, y) - z = tf.nn.gelu(z, approximate=True, name="op_to_store") - found_quantized_matmul = False - with tf.Session() as sess: - sess.run(z, feed_dict={x: x_data, y: y_data}) - float_graph_def = sess.graph.as_graph_def() - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(2, 2), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.eval_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.model = float_graph_def - output_graph = quantizer.fit() - - for i in output_graph.graph_def.node: - if i.op == "_QuantizedMatMul" and i.attr["fused_ops"].list.s == [ - b"BiasAdd", - b"GeluApproximate", - b"Dequantize", - ]: - found_quantized_matmul = True - break - self.assertEqual(found_quantized_matmul, True) - - @disable_random() - def test_matmul_dummy_biasadd_geluexact_fusion(self): - g = tf.Graph() - with g.as_default(): - x_data = np.array([[0.1, 0.2], [0.2, 0.3]]) - y_data = np.array([[1, 2], [3, 4]], dtype=np.float32) - x = tf.placeholder(tf.float32, shape=[2, 2], name="x") - y = tf.constant(y_data, dtype=tf.float32, shape=[2, 2]) - z = tf.matmul(x, y) - z = tf.nn.gelu(z, approximate=False, name="op_to_store") - found_quantized_matmul = False - with tf.Session() as sess: - sess.run(z, feed_dict={x: x_data, y: y_data}) - float_graph_def = sess.graph.as_graph_def() - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(2, 2), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.eval_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.model = float_graph_def - output_graph = quantizer.fit() - - for i in output_graph.graph_def.node: - if i.op == "_QuantizedMatMul" and i.attr["fused_ops"].list.s == [ - b"BiasAdd", - b"GeluExact", - b"Dequantize", - ]: - found_quantized_matmul = True - break - self.assertEqual(found_quantized_matmul, True) - - @disable_random() - def test_matmul_dummy_biasadd_elu_fusion(self): - g = tf.Graph() - with g.as_default(): - x_data = np.array([[0.1, 0.2], [0.2, 0.3]]) - y_data = np.array([[1, 2], [3, 4]], dtype=np.float32) - x = tf.placeholder(tf.float32, shape=[2, 2], name="x") - y = tf.constant(y_data, dtype=tf.float32, shape=[2, 2]) - z = tf.matmul(x, y) - z = tf.nn.elu(z, name="op_to_store") - found_quantized_matmul = False - with tf.Session() as sess: - sess.run(z, feed_dict={x: x_data, y: y_data}) - float_graph_def = sess.graph.as_graph_def() - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(2, 2), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.eval_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.model = float_graph_def - output_graph = quantizer.fit() - - for i in output_graph.graph_def.node: - if i.op == "_QuantizedMatMul" and i.attr["fused_ops"].list.s == [b"BiasAdd", b"Elu", b"Dequantize"]: - found_quantized_matmul = True - break - self.assertEqual(found_quantized_matmul, True) - - @disable_random() - def test_matmul_dummy_biasadd_tanh_fusion(self): - g = tf.Graph() - with g.as_default(): - x_data = np.array([[0.1, 0.2], [0.2, 0.3]]) - y_data = np.array([[1, 2], [3, 4]], dtype=np.float32) - x = tf.placeholder(tf.float32, shape=[2, 2], name="x") - y = tf.constant(y_data, dtype=tf.float32, shape=[2, 2]) - z = tf.matmul(x, y) - z = tf.math.tanh(z, name="op_to_store") - found_quantized_matmul = False - with tf.Session() as sess: - sess.run(z, feed_dict={x: x_data, y: y_data}) - float_graph_def = sess.graph.as_graph_def() - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(2, 2), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.eval_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.model = float_graph_def - output_graph = quantizer.fit() - - for i in output_graph.graph_def.node: - if i.op == "_QuantizedMatMul" and i.attr["fused_ops"].list.s == [ - b"BiasAdd", - b"Tanh", - b"Dequantize", - ]: - found_quantized_matmul = True - break - self.assertEqual(found_quantized_matmul, True) - - @disable_random() - def test_matmul_dummy_biasadd_sigmoid_fusion(self): - g = tf.Graph() - with g.as_default(): - x_data = np.array([[0.1, 0.2], [0.2, 0.3]]) - y_data = np.array([[1, 2], [3, 4]], dtype=np.float32) - x = tf.placeholder(tf.float32, shape=[2, 2], name="x") - y = tf.constant(y_data, dtype=tf.float32, shape=[2, 2]) - z = tf.matmul(x, y) - z = tf.math.sigmoid(z, name="op_to_store") - found_quantized_matmul = False - with tf.Session() as sess: - sess.run(z, feed_dict={x: x_data, y: y_data}) - float_graph_def = sess.graph.as_graph_def() - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(2, 2), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.eval_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.model = float_graph_def - output_graph = quantizer.fit() - - for i in output_graph.graph_def.node: - if i.op == "_QuantizedMatMul" and i.attr["fused_ops"].list.s == [ - b"BiasAdd", - b"Sigmoid", - b"Dequantize", - ]: - found_quantized_matmul = True - break - self.assertEqual(found_quantized_matmul, True) - - @disable_random() - def test_matmul_add_const_fusion(self): - g = tf.Graph() - with g.as_default(): - x_data = np.array([[0.1, 0.2], [0.2, 0.3]]) - y_data = np.array([[1, 2], [3, 4]], dtype=np.float32) - x = tf.placeholder(tf.float32, shape=[2, 2], name="x") - y = tf.constant(y_data, dtype=tf.float32, shape=[2, 2]) - transpose = tf.transpose(y, perm=[1, 0]) - reshape = tf.reshape(transpose, [2, 2]) - z = tf.matmul(x, reshape, name="quant_matmul") - z = tf.math.add(z, [1, 2], name="op_to_store") - found_quantized_matmul = False - - with tf.Session() as sess: - sess.run(z, feed_dict={x: x_data, y: y_data}) - float_graph_def = sess.graph.as_graph_def() - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(2, 2), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.eval_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.model = float_graph_def - output_graph = quantizer.fit() - - for i in output_graph.graph_def.node: - if i.op == "_QuantizedMatMul" and i.attr["fused_ops"].list.s == [b"BiasAdd", b"Dequantize"]: - found_quantized_matmul = True - break - self.assertEqual(found_quantized_matmul, True) - - @disable_random() - def test_matmul_add_non_const_fusion(self): - g = tf.Graph() - with g.as_default(): - x_data = np.array([[0.1, 0.2], [0.2, 0.3]]) - y_data = np.array([[1, 2], [3, 4]], dtype=np.float32) - x = tf.placeholder(tf.float32, shape=[2, 2], name="x") - y = tf.constant(y_data, dtype=tf.float32, shape=[2, 2]) - transpose = tf.transpose(y, perm=[1, 0]) - reshape = tf.reshape(transpose, [2, 2]) - z = tf.matmul(x, reshape, name="quant_matmul") - z = tf.math.add(z, x, name="addv2") - z = tf.nn.relu(z, name="op_to_store") - found_quantized_matmul = False - - with tf.Session() as sess: - sess.run(z, feed_dict={x: x_data, y: y_data}) - float_graph_def = sess.graph.as_graph_def() - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(2, 2), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.eval_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.model = float_graph_def - output_graph = quantizer.fit() - - for i in output_graph.graph_def.node: - if i.op == "_QuantizedMatMul" and i.attr["fused_ops"].list.s == [b"Dequantize"]: - found_quantized_matmul = True - break - self.assertEqual(found_quantized_matmul, True) - - @disable_random() - def test_matmul_biasadd_add_const_fusion(self): - g = tf.Graph() - with g.as_default(): - x_data = np.array([[0.1, 0.2], [0.2, 0.3]]) - y_data = np.array([[1, 2], [3, 4]], dtype=np.float32) - x = tf.placeholder(tf.float32, shape=[2, 2], name="x") - y = tf.constant(y_data, dtype=tf.float32, shape=[2, 2]) - z = tf.matmul(x, y) - z = tf.nn.bias_add(z, [1, 2]) - z = tf.math.add(z, [1, 2], name="op_to_store") - found_quantized_matmul = False - - with tf.Session() as sess: - sess.run(z, feed_dict={x: x_data, y: y_data}) - float_graph_def = sess.graph.as_graph_def() - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(2, 2), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.eval_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.model = float_graph_def - output_graph = quantizer.fit() - - for i in output_graph.graph_def.node: - if i.op == "_QuantizedMatMul" and i.attr["fused_ops"].list.s == [b"BiasAdd", b"Dequantize"]: - found_quantized_matmul = True - break - self.assertEqual(found_quantized_matmul, True) - - @disable_random() - def test_matmul_biasadd_add_non_const_fusion(self): - g = tf.Graph() - with g.as_default(): - x_data = np.array([[0.1, 0.2], [0.2, 0.3]]) - y_data = np.array([[1, 2], [3, 4]], dtype=np.float32) - x = tf.placeholder(tf.float32, shape=[2, 2], name="x") - y = tf.constant(y_data, dtype=tf.float32, shape=[2, 2]) - z = tf.matmul(x, y) - z = tf.nn.bias_add(z, [1, 2]) - z = tf.math.add(z, x, name="op_to_store") - found_quantized_matmul = False - - with tf.Session() as sess: - sess.run(z, feed_dict={x: x_data, y: y_data}) - float_graph_def = sess.graph.as_graph_def() - - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(2, 2), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.eval_dataloader = common.DataLoader(dataset, batch_size=2) - quantizer.model = float_graph_def - output_graph = quantizer.fit() - - for i in output_graph.graph_def.node: - if i.op == "_QuantizedMatMul" and i.attr["fused_ops"].list.s == [b"BiasAdd", b"Dequantize"]: - found_quantized_matmul = True - break - self.assertEqual(found_quantized_matmul, True) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/tfnewapi/test_tensorflow_graph_qdq_new_conv_fusion.py b/test/tfnewapi/test_tensorflow_graph_qdq_new_conv_fusion.py deleted file mode 100644 index 806171b7c56..00000000000 --- a/test/tfnewapi/test_tensorflow_graph_qdq_new_conv_fusion.py +++ /dev/null @@ -1,146 +0,0 @@ -# -# -*- coding: utf-8 -*- -# -import logging -import os -import unittest - -import tensorflow as tf -import yaml -from tensorflow.compat.v1 import graph_util -from tensorflow.python.framework import function - -from neural_compressor.adaptor.tf_utils.util import disable_random - - -def build_fake_yaml(): - fake_yaml = """ - model: - name: fake_yaml - framework: tensorflow - inputs: input - device: cpu - use_bf16: True - quantization: - model_wise: - weight: - granularity: per_tensor - scheme: sym - dtype: int8 - algorithm: minmax - evaluation: - accuracy: - metric: - topk: 1 - tuning: - strategy: - name: basic - accuracy_criterion: - relative: 0.1 - exit_policy: - performance_only: True - workspace: - path: saved - """ - - y = yaml.load(fake_yaml, Loader=yaml.SafeLoader) - - with open("fake_yaml.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - - f.close() - - -class TestTensorflowNewQdqConvFusion(unittest.TestCase): - @classmethod - def setUpClass(self): - build_fake_yaml() - - @classmethod - def tearDownClass(self): - os.remove("fake_yaml.yaml") - - @disable_random() - def test_conv_biasadd_add_leakyrelu_fusion(self): - logging.getLogger().info("test_conv_biasadd_add_leakyrelu_fusion") - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - conv_weights = tf.compat.v1.get_variable( - "weight", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv = tf.nn.conv2d(x, conv_weights, strides=[1, 2, 2, 1], padding="SAME") - normed = tf.compat.v1.layers.batch_normalization(conv) - conv2_weights = tf.compat.v1.get_variable( - "weight_conv2", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv2 = tf.nn.conv2d(x, conv2_weights, strides=[1, 2, 2, 1], padding="SAME") - sumadd = tf.raw_ops.AddV2(x=normed, y=conv2, name="addv2") - leaky_relu = tf.nn.leaky_relu(sumadd, name="op_to_store") - - out_name = leaky_relu.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - found_conv_fusion = True - - for i in output_graph.graph_def.node: - if i.op == "LeakyRelu": - found_conv_fusion = False - break - self.assertEqual(found_conv_fusion, False) - - @disable_random() - def test_resizebilinear_bf16_input(self): - os.environ["FORCE_BF16"] = "1" - x = tf.compat.v1.placeholder(tf.float32, [1, 56, 56, 16], name="input") - top_relu = tf.nn.relu(x) - paddings = tf.constant([[0, 0], [1, 1], [1, 1], [0, 0]]) - x_pad = tf.pad(top_relu, paddings, "CONSTANT") - conv1_weights = tf.compat.v1.get_variable( - "weight_conv1", [3, 3, 16, 16], initializer=tf.compat.v1.random_normal_initializer() - ) - conv1 = tf.nn.conv2d(x_pad, conv1_weights, strides=[1, 2, 2, 1], padding="VALID") - matmul_weights = tf.compat.v1.get_variable( - "weight_matmul", [1, 28, 16, 32], initializer=tf.compat.v1.random_normal_initializer() - ) - matmul = tf.linalg.matmul(conv1, matmul_weights) - conv2_weights = tf.compat.v1.get_variable( - "weight_conv2", [7, 7, 32, 1], initializer=tf.compat.v1.random_normal_initializer() - ) - conv2 = tf.nn.conv2d(matmul, conv2_weights, strides=[1, 2, 2, 1], padding="VALID") - leaky_relu = tf.nn.leaky_relu(conv2, name="op_to_store") - resize_bili1 = tf.raw_ops.ResizeBilinear(images=leaky_relu, size=(28, 28)) - resize_bili2 = tf.raw_ops.ResizeBilinear(images=resize_bili1, size=(14, 14)) - out_name = resize_bili2.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 56, 56, 16), label=True) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - cast_counter = 0 - - for i in output_graph.graph_def.node: - if i.op == "Cast": - cast_counter += 1 - self.assertEqual(cast_counter, 2) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/tfnewapi/test_tensorflow_graph_qdq_pooling_fusion.py b/test/tfnewapi/test_tensorflow_graph_qdq_pooling_fusion.py deleted file mode 100644 index e50e0e0e651..00000000000 --- a/test/tfnewapi/test_tensorflow_graph_qdq_pooling_fusion.py +++ /dev/null @@ -1,141 +0,0 @@ -# -# -*- coding: utf-8 -*- -# -import os -import unittest - -import numpy as np -import tensorflow.compat.v1 as tf -import yaml -from tensorflow.compat.v1 import graph_util -from tensorflow.python.framework import dtypes - -from neural_compressor.adaptor.tensorflow import TensorflowQuery -from neural_compressor.adaptor.tf_utils.util import disable_random - - -def build_fake_yaml(): - fake_yaml = """ - model: - name: fake_yaml - framework: tensorflow - inputs: x - outputs: op_to_store - device: cpu - evaluation: - accuracy: - metric: - topk: 1 - tuning: - strategy: - name: basic - accuracy_criterion: - relative: 0.01 - exit_policy: - performance_only: True - workspace: - path: saved - """ - y = yaml.load(fake_yaml, Loader=yaml.SafeLoader) - with open("fake_yaml.yaml", "w", encoding="utf-8") as f: - yaml.dump(y, f) - f.close() - - -class TestGraphQDQPoolingFusion(unittest.TestCase): - @classmethod - def setUpClass(self): - build_fake_yaml() - self.op_wise_sequences = TensorflowQuery( - local_config_file=os.path.join(os.path.dirname(__file__), "../../neural_compressor/adaptor/tensorflow.yaml") - ).get_eightbit_patterns(True) - - @classmethod - def tearDownClass(self): - os.remove("fake_yaml.yaml") - - @disable_random() - def test_qdq_maxpool_fusion(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 30, 30, 1], name="input") - conv_weights = tf.compat.v1.get_variable( - "weight", [2, 2, 1, 1], initializer=tf.compat.v1.random_normal_initializer() - ) - conv_bias = tf.compat.v1.get_variable("bias", [1], initializer=tf.compat.v1.random_normal_initializer()) - x = tf.nn.relu(x) - conv = tf.nn.conv2d(x, conv_weights, strides=[1, 2, 2, 1], padding="SAME", name="last") - normed = tf.compat.v1.layers.batch_normalization(conv) - - relu = tf.nn.relu(normed) - relu2 = tf.nn.relu(relu) - pool = tf.nn.max_pool(relu2, ksize=1, strides=[1, 2, 2, 1], name="maxpool", padding="SAME") - conv1 = tf.nn.conv2d(pool, conv_weights, strides=[1, 2, 2, 1], padding="SAME", name="last") - conv_bias = tf.nn.bias_add(conv1, conv_bias) - x = tf.nn.relu(conv_bias) - final_node = tf.nn.relu(x, name="op_to_store") - - out_name = final_node.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 30, 30, 1), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - - found_quantized_maxpool = False - for i in output_graph.graph_def.node: - if i.op == "QuantizedMaxPool": - found_quantized_maxpool = True - break - self.assertEqual(found_quantized_maxpool, True) - - @disable_random() - def test_qdq_avgpool_fusion(self): - x = tf.compat.v1.placeholder(tf.float32, [1, 30, 30, 1], name="input") - conv_weights = tf.compat.v1.get_variable( - "weight", [2, 2, 1, 1], initializer=tf.compat.v1.random_normal_initializer() - ) - conv_bias = tf.compat.v1.get_variable("bias", [1], initializer=tf.compat.v1.random_normal_initializer()) - x = tf.nn.relu(x) - conv = tf.nn.conv2d(x, conv_weights, strides=[1, 2, 2, 1], padding="SAME", name="last") - normed = tf.compat.v1.layers.batch_normalization(conv) - - relu = tf.nn.relu(normed) - relu2 = tf.nn.relu(relu) - pool = tf.nn.avg_pool(relu2, ksize=1, strides=[1, 2, 2, 1], name="avgpool", padding="SAME") - conv1 = tf.nn.conv2d(pool, conv_weights, strides=[1, 2, 2, 1], padding="SAME", name="last") - conv_bias = tf.nn.bias_add(conv1, conv_bias) - x = tf.nn.relu(conv_bias) - final_node = tf.nn.relu(x, name="op_to_store") - - out_name = final_node.name.split(":")[0] - with tf.compat.v1.Session() as sess: - sess.run(tf.compat.v1.global_variables_initializer()) - output_graph_def = graph_util.convert_variables_to_constants( - sess=sess, input_graph_def=sess.graph_def, output_node_names=[out_name] - ) - from neural_compressor.experimental import Quantization, common - - quantizer = Quantization("fake_yaml.yaml") - dataset = quantizer.dataset("dummy", shape=(100, 30, 30, 1), label=True) - quantizer.calib_dataloader = common.DataLoader(dataset) - quantizer.eval_dataloader = common.DataLoader(dataset) - quantizer.model = output_graph_def - output_graph = quantizer.fit() - - found_quantized_avgpool = False - for i in output_graph.graph_def.node: - if i.op == "QuantizedAvgPool": - found_quantized_avgpool = True - break - self.assertEqual(found_quantized_avgpool, True) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/tfnewapi/test_tf_spr_base_distributed_metrics.py b/test/tfnewapi/test_tf_spr_base_distributed_metrics.py deleted file mode 100644 index 000c3175e0d..00000000000 --- a/test/tfnewapi/test_tf_spr_base_distributed_metrics.py +++ /dev/null @@ -1,975 +0,0 @@ -"""Tests for the distributed metrics.""" - -import os -import re -import shutil -import signal -import subprocess -import unittest - -import tensorflow as tf - -from neural_compressor.adaptor.tf_utils.util import version1_lt_version2 - - -def build_fake_ut(): - fake_ut = """ -import numpy as np -import unittest -from neural_compressor.metric import METRICS -from neural_compressor.metric.f1 import evaluate -from neural_compressor.metric.evaluate_squad import evaluate as evaluate_squad -from neural_compressor.metric import bleu -import horovod.tensorflow as hvd -import os -import json -import tensorflow as tf - -tf.compat.v1.enable_eager_execution() - -class TestMetrics(unittest.TestCase): - - @classmethod - def setUpClass(cls): - os.environ["CUDA_DEVICE_ORDER"] = "PCI_BUS_ID" - os.environ["CUDA_VISIBLE_DEVICES"] = "-1" - hvd.init() - if hvd.rank() == 0: - if os.path.exists('anno_0.yaml'): - os.remove('anno_0.yaml') - if os.path.exists('anno_1.yaml'): - os.remove('anno_1.yaml') - if os.path.exists('anno_2.yaml'): - os.remove('anno_2.yaml') - while hvd.rank() == 1: - if not os.path.exists('anno_0.yaml') \\ - and not os.path.exists('anno_1.yaml') \\ - and not os.path.exists('anno_2.yaml'): - break - - @classmethod - def tearDownClass(cls): - if hvd.rank() == 1: - if os.path.exists('anno_0.yaml'): - os.remove('anno_0.yaml') - if os.path.exists('anno_1.yaml'): - os.remove('anno_1.yaml') - if os.path.exists('anno_2.yaml'): - os.remove('anno_2.yaml') - - def test_mIOU(self): - metrics = METRICS('tensorflow') - miou = metrics['mIOU']() - miou.hvd = hvd - if hvd.rank() == 0: - preds = np.array([0]) - labels = np.array([0]) - else: - preds = np.array([0, 1, 1]) - labels = np.array([1, 0, 1]) - miou.update(preds, labels) - self.assertAlmostEqual(miou.result(), 0.33333334) - - miou.reset() - if hvd.rank() == 0: - preds = np.array([0, 0]) - labels = np.array([0, 1]) - else: - preds = np.array([1, 1]) - labels = np.array([1, 1]) - miou.update(preds, labels) - self.assertAlmostEqual(miou.result(), 0.58333333) - - def test_onnxrt_GLUE(self): - metrics = METRICS('onnxrt_qlinearops') - glue = metrics['GLUE']('mrpc') - glue.hvd = hvd - preds = [np.array( - [[-3.2443411, 3.0909934], - [2.0500996, -2.3100944], - [1.870293 , -2.0741048], - [-2.8377204, 2.617834], - [2.008347 , -2.0215416], - [-2.9693947, 2.7782154], - [-2.9949608, 2.7887983], - [-3.0623112, 2.8748074]]) - ] - labels = [np.array([1, 0, 0, 1, 0, 1, 0, 1])] - self.assertRaises(NotImplementedError, glue.update, preds, labels) - preds_2 = [np.array( - [[-3.1296735, 2.8356276], - [-3.172515 , 2.9173899], - [-3.220131 , 3.0916846], - [2.1452675, -1.9398905], - [1.5475761, -1.9101546], - [-2.9797182, 2.721741], - [-3.2052834, 2.9934788], - [-2.7451005, 2.622343]]) - ] - labels_2 = [np.array([1, 1, 1, 0, 0, 1, 1, 1])] - self.assertRaises(NotImplementedError, glue.update, preds_2, labels_2) - glue.reset() - self.assertRaises(NotImplementedError, glue.update, preds, labels) - - def test_tensorflow_F1(self): - metrics = METRICS('tensorflow') - F1 = metrics['F1']() - F1.hvd = hvd - if hvd.rank() == 0: - preds = [1, 1, 1, 1] - labels = [0, 1, 1, 1] - else: - preds = [1, 1, 1, 1, 1, 1] - labels = [1, 1, 1, 1, 1, 1] - - F1.update(preds, labels) - self.assertEqual(F1.result(), 0.9) - - def test_squad_evaluate(self): - evaluate.hvd = hvd - label = [{'paragraphs':\\ - [{'qas':[{'answers': [{'answer_start': 177, 'text': 'Denver Broncos'}, \\ - {'answer_start': 177, 'text': 'Denver Broncos'}, \\ - {'answer_start': 177, 'text': 'Denver Broncos'}], \\ - 'question': 'Which NFL team represented the AFC at Super Bowl 50?', \\ - 'id': '56be4db0acb8001400a502ec'}]}]}] - preds = {'56be4db0acb8001400a502ec': 'Denver Broncos'} - f1 = evaluate(preds, label) - self.assertEqual(f1, 100.) - dataset = [{'paragraphs':\\ - [{'qas':[{'answers': [{'answer_start': 177, 'text': 'Denver Broncos'}, \\ - {'answer_start': 177, 'text': 'Denver Broncos'}, \\ - {'answer_start': 177, 'text': 'Denver Broncos'}], \\ - 'question': 'Which NFL team represented the AFC at Super Bowl 50?', \\ - 'id': '56be4db0acb8001400a502ec'}]}]}] - predictions = {'56be4db0acb8001400a502ec': 'Denver Broncos'} - f1_squad = evaluate_squad(dataset,predictions) - self.assertEqual(f1_squad['f1'], 100.) - self.assertEqual(f1_squad['exact_match'], 100.) - - def test_pytorch_F1(self): - metrics = METRICS('pytorch') - F1 = metrics['F1']() - F1.hvd = hvd - F1.reset() - if hvd.rank() == 0: - preds = [1] - labels = [2] - else: - preds = [1] - labels = [1, 1] - F1.update(preds, labels) - self.assertEqual(F1.result(), 0.8) - - def test_tensorflow_topk(self): - metrics = METRICS('tensorflow') - top1 = metrics['topk']() - top1.reset() - self.assertEqual(top1.result(), 0) - top2 = metrics['topk'](k=2) - top3 = metrics['topk'](k=3) - top1.hvd = hvd - top2.hvd = hvd - top3.hvd = hvd - - if hvd.rank() == 0: - predicts = [[0, 0.2, 0.9, 0.3]] - labels = [[0, 1, 0, 0]] - single_predict = [0, 0.2, 0.9, 0.3] - sparse_labels = [2] - single_label = 2 - else: - predicts = [[0, 0.9, 0.8, 0]] - labels = [[0, 0, 1, 0]] - single_predict = [0, 0.2, 0.9, 0.3] - sparse_labels = [2] - single_label = 2 - - # test functionality of one-hot label - top1.update(predicts, labels) - top2.update(predicts, labels) - top3.update(predicts, labels) - self.assertEqual(top1.result(), 0.0) - self.assertEqual(top2.result(), 0.5) - self.assertEqual(top3.result(), 1) - - # test functionality of sparse label - top1.reset() - top2.reset() - top3.reset() - top1.update(predicts, sparse_labels) - top2.update(predicts, sparse_labels) - top3.update(predicts, sparse_labels) - self.assertEqual(top1.result(), 0.5) - self.assertEqual(top2.result(), 1) - self.assertEqual(top3.result(), 1) - - # test functionality of single label - top1.reset() - top2.reset() - top3.reset() - top1.update(single_predict, single_label) - top2.update(single_predict, single_label) - top3.update(single_predict, single_label) - self.assertEqual(top1.result(), 1) - self.assertEqual(top2.result(), 1) - self.assertEqual(top3.result(), 1) - - def test_tensorflow_mAP(self): - metrics = METRICS('tensorflow') - fake_dict = 'dog: 1' - if hvd.rank() == 0: - with open('anno_0.yaml', 'w', encoding = "utf-8") as f: - f.write(fake_dict) - while True: - file_exists = hvd.allgather_object(os.path.exists('anno_0.yaml')) - if file_exists == [True, True]: - break - mAP = metrics['mAP']('anno_0.yaml') - mAP.hvd = hvd - self.assertEqual(mAP.category_map_reverse['dog'], 1) - detection = [ - np.array([[5]]), - np.array([[5]]), - np.array([[[0.16117382, 0.59801614, 0.81511605, 0.7858219 ], - [0.5589304 , 0. , 0.98301625, 0.520178 ], - [0.62706745, 0.35748824, 0.6892729 , 0.41513762], - [0.40032804, 0.01218696, 0.6924763 , 0.30341768], - [0.62706745, 0.35748824, 0.6892729 , 0.41513762]]]), - np.array([[0.9267181 , 0.8510787 , 0.60418576, 0.35155892, 0.31158054]]), - np.array([[ 1., 67., 51., 79., 47.]]) - ] - ground_truth = [ - np.array([[[0.5633255 , 0.34003124, 0.69857144, 0.4009531 ], - [0.4763466 , 0.7769531 , 0.54334897, 0.9675937 ]]]), - np.array([['a', 'b']]), - np.array([[]]), - np.array([b'000000397133.jpg']) - ] - self.assertRaises(NotImplementedError, mAP.update, detection, ground_truth) - - detection = [ - np.array([[[0.16117382, 0.59801614, 0.81511605, 0.7858219 ], - [0.62706745, 0.35748824, 0.6892729 , 0.41513762]]]), - np.array([[0.9267181 , 0.8510787]]), - np.array([[ 1., 1.]]) - ] - ground_truth = [ - np.array([[[0.16117382, 0.59801614, 0.81511605, 0.7858219 ], - [0.62706745, 0.35748824, 0.6892729 , 0.41513762]]]), - np.array([[b'dog', b'dog']]), - np.array([[]]), - np.array([b'000000397133.jpg']) - ] - self.assertRaises(NotImplementedError, mAP.update, detection, ground_truth) - mAP.result() - self.assertEqual(format(mAP.result(), '.5f'), - '0.00000') - - detection = [ - np.array([[[0.16117382, 0.59801614, 0.81511605, 0.7858219 ], - [0.5589304 , 0. , 0.98301625, 0.520178 ], - [0.62706745, 0.35748824, 0.6892729 , 0.41513762], - [0.40032804, 0.01218696, 0.6924763 , 0.30341768], - [0.62706745, 0.35748824, 0.6892729 , 0.41513762]]]), - np.array([[0.9267181 , 0.8510787 , 0.60418576, 0.35155892, 0.31158054]]), - np.array([[ 1., 67., 51., 79., 47.]]) - ] - detection_2 = [ - np.array([[8]]), - np.array([[[0.82776225, 0.5865939 , 0.8927653 , 0.6302338 ], - [0.8375764 , 0.6424138 , 0.9055594 , 0.6921875 ], - [0.57902956, 0.39394334, 0.8342961 , 0.5577197 ], - [0.7949219 , 0.6513021 , 0.8472295 , 0.68427753], - [0.809729 , 0.5947042 , 0.8539927 , 0.62916476], - [0.7258591 , 0.08907133, 1. , 0.86224866], - [0.43100086, 0.37782395, 0.8384069 , 0.5616918 ], - [0.32005906, 0.84334356, 1. , 1. ]]]), - np.array([[0.86698544, 0.7562499 , 0.66414887, 0.64498234,\\ - 0.63083494,0.46618757, 0.3914739 , 0.3094324 ]]), - np.array([[55., 55., 79., 55., 55., 67., 79., 82.]]) - ] - ground_truth = [ - np.array([[[0.5633255 , 0.34003124, 0.69857144, 0.4009531 ], - [0.56262296, 0.0015625 , 1. , 0.5431719 ], - [0.16374707, 0.60728127, 0.813911 , 0.77823436], - [0.5841452 , 0.21182813, 0.65156907, 0.24670312], - [0.8056206 , 0.048875 , 0.90124124, 0.1553125 ], - [0.6729742 , 0.09317187, 0.7696956 , 0.21203125], - [0.3848478 , 0.002125 , 0.61522245, 0.303 ], - [0.61548007, 0. , 0.7015925 , 0.097125 ], - [0.6381967 , 0.1865625 , 0.7184075 , 0.22534375], - [0.6274239 , 0.22104688, 0.71140516, 0.27134374], - [0.39566743, 0.24370313, 0.43578455, 0.284375 ], - [0.2673302 , 0.245625 , 0.3043794 , 0.27353126], - [0.7137705 , 0.15429688, 0.726815 , 0.17114063], - [0.6003747 , 0.25942189, 0.6438876 , 0.27320313], - [0.68845433, 0.13501562, 0.714637 , 0.17245312], - [0.69358313, 0.10959375, 0.7043091 , 0.12409375], - [0.493911 , 0. , 0.72571427, 0.299 ], - [0.69576114, 0.15107812, 0.70714283, 0.16332813], - [0.4763466 , 0.7769531 , 0.54334897, 0.9675937 ]]]), - np.array([[]]), - np.array([[44, 67, 1, 49, 51, 51, 79, 1, 47, 47, 51, 51,\\ - 56, 50, 56, 56, 79, 57, 81]]), - np.array([b'000000397133.jpg']) - ] - ground_truth_2 = [ - np.array([[[0.51508695, 0.2911648 , 0.5903478 , 0.31360796], - [0.9358696 , 0.07528409, 0.99891305, 0.25 ], - [0.8242174 , 0.3309659 , 0.93508697, 0.47301137], - [0.77413046, 0.22599432, 0.9858696 , 0.8179261 ], - [0.32582608, 0.8575 , 0.98426086, 0.9984659 ], - [0.77795655, 0.6268466 , 0.89930433, 0.73434657], - [0.5396087 , 0.39053977, 0.8483913 , 0.5615057 ], - [0.58473915, 0.75661933, 0.5998261 , 0.83579546], - [0.80391306, 0.6129829 , 0.8733478 , 0.66201705], - [0.8737391 , 0.6579546 , 0.943 , 0.7053693 ], - [0.775 , 0.6549716 , 0.8227391 , 0.6882955 ], - [0.8130869 , 0.58292615, 0.90526086, 0.62551135], - [0.7844348 , 0.68735796, 0.98182607, 0.83329546], - [0.872 , 0.6190057 , 0.9306522 , 0.6591761 ]]]), - np.array([[]]), - np.array([[64, 62, 62, 67, 82, 52, 79, 81, 55, 55, 55, 55, 62, 55]]), - np.array([b'000000037777.jpg']) - ] - - mAP = metrics['mAP']() - - self.assertEqual(mAP.result(), 0) - - mAP.update(detection, ground_truth) - - mAP.update(detection, ground_truth) - self.assertEqual(format(mAP.result(), '.5f'), - '0.18182') - - mAP.update(detection_2, ground_truth_2) - self.assertEqual(format(mAP.result(), '.5f'), - '0.20347') - mAP.reset() - mAP.update(detection, ground_truth) - self.assertEqual(format(mAP.result(), '.5f'), - '0.18182') - - ground_truth_1 = [ - np.array([[[0.51508695, 0.2911648 , 0.5903478 , 0.31360796], - [0.872 , 0.6190057 , 0.9306522 , 0.6591761 ]]]), - np.array([[]]), - np.array([[[64, 62]]]), - np.array([b'000000037777.jpg']) - ] - self.assertRaises(ValueError, mAP.update, detection, ground_truth_1) - ground_truth_2 = [ - np.array([[[0.51508695, 0.2911648 , 0.5903478 , 0.31360796], - [0.872 , 0.6190057 , 0.9306522 , 0.6591761 ]]]), - np.array([[]]), - np.array([[64]]), - np.array([b'000000037700.jpg']) - ] - self.assertRaises(ValueError, mAP.update, detection, ground_truth_2) - detection_1 = [ - np.array([[[0.16117382, 0.59801614, 0.81511605, 0.7858219 ], - [0.5589304 , 0. , 0.98301625, 0.520178 ]]]), - np.array([[0.9267181 , 0.8510787 , 0.60418576, 0.35155892, 0.31158054]]), - np.array([[ 1., 67., 51., 79., 47.]]) - ] - ground_truth_1 = [ - np.array([[[0.51508695, 0.2911648 , 0.5903478 , 0.31360796], - [0.872 , 0.6190057 , 0.9306522 , 0.6591761 ]]]), - np.array([[]]), - np.array([[64, 62]]), - np.array([b'000000011.jpg']) - ] - self.assertRaises(ValueError, mAP.update, detection_1, ground_truth_1) - ground_truth_2 = [ - np.array([[[0.51508695, 0.2911648 , 0.5903478 , 0.31360796], - [0.872 , 0.6190057 , 0.9306522 , 0.6591761 ]]]), - np.array([[]]), - np.array([[64, 62]]), - np.array([b'000000012.jpg']) - ] - detection_2 = [ - np.array([[[0.16117382, 0.59801614, 0.81511605, 0.7858219 ], - [0.5589304 , 0. , 0.98301625, 0.520178 ]]]), - np.array([[0.9267181 , 0.8510787]]), - np.array([[ 1., 67., 51., 79., 47.]]) - ] - self.assertRaises(ValueError, mAP.update, detection_2, ground_truth_2) - - def test_tensorflow_VOCmAP(self): - metrics = METRICS('tensorflow') - fake_dict = 'dog: 1' - if hvd.rank() == 0: - with open('anno_1.yaml', 'w', encoding = "utf-8") as f: - f.write(fake_dict) - while True: - file_exists = hvd.allgather_object(os.path.exists('anno_1.yaml')) - if file_exists == [True, True]: - break - mAP = metrics['VOCmAP']('anno_1.yaml') - mAP.hvd = hvd - self.assertEqual(mAP.iou_thrs, 0.5) - self.assertEqual(mAP.map_points, 0) - self.assertEqual(mAP.category_map_reverse['dog'], 1) - detection = [ - np.array([[5]]), - np.array([[5]]), - np.array([[[0.16117382, 0.59801614, 0.81511605, 0.7858219 ], - [0.5589304 , 0. , 0.98301625, 0.520178 ], - [0.62706745, 0.35748824, 0.6892729 , 0.41513762], - [0.40032804, 0.01218696, 0.6924763 , 0.30341768], - [0.62706745, 0.35748824, 0.6892729 , 0.41513762]]]), - np.array([[0.9267181 , 0.8510787 , 0.60418576, 0.35155892, 0.31158054]]), - np.array([[ 1., 67., 51., 79., 47.]]) - ] - ground_truth = [ - np.array([[[0.5633255 , 0.34003124, 0.69857144, 0.4009531 ], - [0.4763466 , 0.7769531 , 0.54334897, 0.9675937 ]]]), - np.array([['a', 'b']]), - np.array([[]]), - np.array([b'000000397133.jpg']) - ] - self.assertRaises(NotImplementedError, mAP.update, detection, ground_truth) - - mAP = metrics['VOCmAP']() - detection = [ - np.array([[[0.16117382, 0.59801614, 0.81511605, 0.7858219 ], - [0.5589304 , 0. , 0.98301625, 0.520178 ], - [0.62706745, 0.35748824, 0.6892729 , 0.41513762], - [0.40032804, 0.01218696, 0.6924763 , 0.30341768], - [0.62706745, 0.35748824, 0.6892729 , 0.41513762]]]), - np.array([[0.9267181 , 0.8510787 , 0.60418576, 0.35155892, 0.31158054]]), - np.array([[ 1., 67., 51., 79., 47.]]) - ] - detection_2 = [ - np.array([[8]]), - np.array([[[0.82776225, 0.5865939 , 0.8927653 , 0.6302338 ], - [0.8375764 , 0.6424138 , 0.9055594 , 0.6921875 ], - [0.57902956, 0.39394334, 0.8342961 , 0.5577197 ], - [0.7949219 , 0.6513021 , 0.8472295 , 0.68427753], - [0.809729 , 0.5947042 , 0.8539927 , 0.62916476], - [0.7258591 , 0.08907133, 1. , 0.86224866], - [0.43100086, 0.37782395, 0.8384069 , 0.5616918 ], - [0.32005906, 0.84334356, 1. , 1. ]]]), - np.array([[0.86698544, 0.7562499 , 0.66414887, 0.64498234,\\ - 0.63083494,0.46618757, 0.3914739 , 0.3094324 ]]), - np.array([[55., 55., 79., 55., 55., 67., 79., 82.]]) - ] - ground_truth = [ - np.array([[[0.5633255 , 0.34003124, 0.69857144, 0.4009531 ], - [0.56262296, 0.0015625 , 1. , 0.5431719 ], - [0.16374707, 0.60728127, 0.813911 , 0.77823436], - [0.5841452 , 0.21182813, 0.65156907, 0.24670312], - [0.8056206 , 0.048875 , 0.90124124, 0.1553125 ], - [0.6729742 , 0.09317187, 0.7696956 , 0.21203125], - [0.3848478 , 0.002125 , 0.61522245, 0.303 ], - [0.61548007, 0. , 0.7015925 , 0.097125 ], - [0.6381967 , 0.1865625 , 0.7184075 , 0.22534375], - [0.6274239 , 0.22104688, 0.71140516, 0.27134374], - [0.39566743, 0.24370313, 0.43578455, 0.284375 ], - [0.2673302 , 0.245625 , 0.3043794 , 0.27353126], - [0.7137705 , 0.15429688, 0.726815 , 0.17114063], - [0.6003747 , 0.25942189, 0.6438876 , 0.27320313], - [0.68845433, 0.13501562, 0.714637 , 0.17245312], - [0.69358313, 0.10959375, 0.7043091 , 0.12409375], - [0.493911 , 0. , 0.72571427, 0.299 ], - [0.69576114, 0.15107812, 0.70714283, 0.16332813], - [0.4763466 , 0.7769531 , 0.54334897, 0.9675937 ]]]), - np.array([[]]), - np.array([[44, 67, 1, 49, 51, 51, 79, 1, 47, 47, 51, 51,\\ - 56, 50, 56, 56, 79, 57, 81]]), - np.array([b'000000397133.jpg']) - ] - ground_truth_2 = [ - np.array([[[0.51508695, 0.2911648 , 0.5903478 , 0.31360796], - [0.9358696 , 0.07528409, 0.99891305, 0.25 ], - [0.8242174 , 0.3309659 , 0.93508697, 0.47301137], - [0.77413046, 0.22599432, 0.9858696 , 0.8179261 ], - [0.32582608, 0.8575 , 0.98426086, 0.9984659 ], - [0.77795655, 0.6268466 , 0.89930433, 0.73434657], - [0.5396087 , 0.39053977, 0.8483913 , 0.5615057 ], - [0.58473915, 0.75661933, 0.5998261 , 0.83579546], - [0.80391306, 0.6129829 , 0.8733478 , 0.66201705], - [0.8737391 , 0.6579546 , 0.943 , 0.7053693 ], - [0.775 , 0.6549716 , 0.8227391 , 0.6882955 ], - [0.8130869 , 0.58292615, 0.90526086, 0.62551135], - [0.7844348 , 0.68735796, 0.98182607, 0.83329546], - [0.872 , 0.6190057 , 0.9306522 , 0.6591761 ]]]), - np.array([[]]), - np.array([[64, 62, 62, 67, 82, 52, 79, 81, 55, 55, 55, 55, 62, 55]]), - np.array([b'000000037777.jpg']) - ] - - self.assertEqual(mAP.result(), 0) - - mAP.update(detection, ground_truth) - - mAP.update(detection, ground_truth) - self.assertEqual(format(mAP.result(), '.5f'), - '0.18182') - - mAP.update(detection_2, ground_truth_2) - self.assertEqual(format(mAP.result(), '.5f'), - '0.20347') - mAP.reset() - mAP.update(detection, ground_truth) - self.assertEqual(format(mAP.result(), '.5f'), - '0.18182') - - ground_truth_1 = [ - np.array([[[0.51508695, 0.2911648 , 0.5903478 , 0.31360796], - [0.872 , 0.6190057 , 0.9306522 , 0.6591761 ]]]), - np.array([[]]), - np.array([[[64, 62]]]), - np.array([b'000000037777.jpg']) - ] - self.assertRaises(ValueError, mAP.update, detection, ground_truth_1) - ground_truth_2 = [ - np.array([[[0.51508695, 0.2911648 , 0.5903478 , 0.31360796], - [0.872 , 0.6190057 , 0.9306522 , 0.6591761 ]]]), - np.array([[]]), - np.array([[64]]), - np.array([b'000000037700.jpg']) - ] - self.assertRaises(ValueError, mAP.update, detection, ground_truth_2) - detection_1 = [ - np.array([[[0.16117382, 0.59801614, 0.81511605, 0.7858219 ], - [0.5589304 , 0. , 0.98301625, 0.520178 ]]]), - np.array([[0.9267181 , 0.8510787 , 0.60418576, 0.35155892, 0.31158054]]), - np.array([[ 1., 67., 51., 79., 47.]]) - ] - ground_truth_1 = [ - np.array([[[0.51508695, 0.2911648 , 0.5903478 , 0.31360796], - [0.872 , 0.6190057 , 0.9306522 , 0.6591761 ]]]), - np.array([[]]), - np.array([[64, 62]]), - np.array([b'000000011.jpg']) - ] - self.assertRaises(ValueError, mAP.update, detection_1, ground_truth_1) - ground_truth_2 = [ - np.array([[[0.51508695, 0.2911648 , 0.5903478 , 0.31360796], - [0.872 , 0.6190057 , 0.9306522 , 0.6591761 ]]]), - np.array([[]]), - np.array([[64, 62]]), - np.array([b'000000012.jpg']) - ] - detection_2 = [ - np.array([[[0.16117382, 0.59801614, 0.81511605, 0.7858219 ], - [0.5589304 , 0. , 0.98301625, 0.520178 ]]]), - np.array([[0.9267181 , 0.8510787]]), - np.array([[ 1., 67., 51., 79., 47.]]) - ] - self.assertRaises(ValueError, mAP.update, detection_2, ground_truth_2) - - def test_tensorflow_COCOmAP(self): - metrics = METRICS('tensorflow') - fake_dict = 'dog: 1' - if hvd.rank() == 0: - with open('anno_2.yaml', 'w', encoding = "utf-8") as f: - f.write(fake_dict) - while True: - file_exists = hvd.allgather_object(os.path.exists('anno_2.yaml')) - if file_exists == [True, True]: - break - mAP = metrics['COCOmAP']('anno_2.yaml') - mAP.hvd = hvd - self.assertEqual(mAP.category_map_reverse['dog'], 1) - detection = [ - np.array([[5]]), - np.array([[5]]), - np.array([[[0.16117382, 0.59801614, 0.81511605, 0.7858219 ], - [0.5589304 , 0. , 0.98301625, 0.520178 ], - [0.62706745, 0.35748824, 0.6892729 , 0.41513762], - [0.40032804, 0.01218696, 0.6924763 , 0.30341768], - [0.62706745, 0.35748824, 0.6892729 , 0.41513762]]]), - np.array([[0.9267181 , 0.8510787 , 0.60418576, 0.35155892, 0.31158054]]), - np.array([[ 1., 67., 51., 79., 47.]]) - ] - ground_truth = [ - np.array([[[0.5633255 , 0.34003124, 0.69857144, 0.4009531 ], - [0.4763466 , 0.7769531 , 0.54334897, 0.9675937 ]]]), - np.array([['a', 'b']]), - np.array([[]]), - np.array([b'000000397133.jpg']) - ] - self.assertRaises(NotImplementedError, mAP.update, detection, ground_truth) - mAP = metrics['COCOmAP']() - detection = [ - np.array([[[0.16117382, 0.59801614, 0.81511605, 0.7858219 ], - [0.5589304 , 0. , 0.98301625, 0.520178 ], - [0.62706745, 0.35748824, 0.6892729 , 0.41513762], - [0.40032804, 0.01218696, 0.6924763 , 0.30341768], - [0.62706745, 0.35748824, 0.6892729 , 0.41513762]]]), - np.array([[0.9267181 , 0.8510787 , 0.60418576, 0.35155892, 0.31158054]]), - np.array([[ 1., 67., 51., 79., 47.]]) - ] - detection_2 = [ - np.array([[8]]), - np.array([[[0.82776225, 0.5865939 , 0.8927653 , 0.6302338 ], - [0.8375764 , 0.6424138 , 0.9055594 , 0.6921875 ], - [0.57902956, 0.39394334, 0.8342961 , 0.5577197 ], - [0.7949219 , 0.6513021 , 0.8472295 , 0.68427753], - [0.809729 , 0.5947042 , 0.8539927 , 0.62916476], - [0.7258591 , 0.08907133, 1. , 0.86224866], - [0.43100086, 0.37782395, 0.8384069 , 0.5616918 ], - [0.32005906, 0.84334356, 1. , 1. ]]]), - np.array([[0.86698544, 0.7562499 , 0.66414887, 0.64498234,\\ - 0.63083494,0.46618757, 0.3914739 , 0.3094324 ]]), - np.array([[55., 55., 79., 55., 55., 67., 79., 82.]]) - ] - ground_truth = [ - np.array([[[0.5633255 , 0.34003124, 0.69857144, 0.4009531 ], - [0.56262296, 0.0015625 , 1. , 0.5431719 ], - [0.16374707, 0.60728127, 0.813911 , 0.77823436], - [0.5841452 , 0.21182813, 0.65156907, 0.24670312], - [0.8056206 , 0.048875 , 0.90124124, 0.1553125 ], - [0.6729742 , 0.09317187, 0.7696956 , 0.21203125], - [0.3848478 , 0.002125 , 0.61522245, 0.303 ], - [0.61548007, 0. , 0.7015925 , 0.097125 ], - [0.6381967 , 0.1865625 , 0.7184075 , 0.22534375], - [0.6274239 , 0.22104688, 0.71140516, 0.27134374], - [0.39566743, 0.24370313, 0.43578455, 0.284375 ], - [0.2673302 , 0.245625 , 0.3043794 , 0.27353126], - [0.7137705 , 0.15429688, 0.726815 , 0.17114063], - [0.6003747 , 0.25942189, 0.6438876 , 0.27320313], - [0.68845433, 0.13501562, 0.714637 , 0.17245312], - [0.69358313, 0.10959375, 0.7043091 , 0.12409375], - [0.493911 , 0. , 0.72571427, 0.299 ], - [0.69576114, 0.15107812, 0.70714283, 0.16332813], - [0.4763466 , 0.7769531 , 0.54334897, 0.9675937 ]]]), - np.array([[]]), - np.array([[44, 67, 1, 49, 51, 51, 79, 1, 47, 47, 51, 51,\\ - 56, 50, 56, 56, 79, 57, 81]]), - np.array([b'000000397133.jpg']) - ] - ground_truth_2 = [ - np.array([[[0.51508695, 0.2911648 , 0.5903478 , 0.31360796], - [0.9358696 , 0.07528409, 0.99891305, 0.25 ], - [0.8242174 , 0.3309659 , 0.93508697, 0.47301137], - [0.77413046, 0.22599432, 0.9858696 , 0.8179261 ], - [0.32582608, 0.8575 , 0.98426086, 0.9984659 ], - [0.77795655, 0.6268466 , 0.89930433, 0.73434657], - [0.5396087 , 0.39053977, 0.8483913 , 0.5615057 ], - [0.58473915, 0.75661933, 0.5998261 , 0.83579546], - [0.80391306, 0.6129829 , 0.8733478 , 0.66201705], - [0.8737391 , 0.6579546 , 0.943 , 0.7053693 ], - [0.775 , 0.6549716 , 0.8227391 , 0.6882955 ], - [0.8130869 , 0.58292615, 0.90526086, 0.62551135], - [0.7844348 , 0.68735796, 0.98182607, 0.83329546], - [0.872 , 0.6190057 , 0.9306522 , 0.6591761 ]]]), - np.array([[]]), - np.array([[64, 62, 62, 67, 82, 52, 79, 81, 55, 55, 55, 55, 62, 55]]), - np.array([b'000000037777.jpg']) - ] - - self.assertEqual(mAP.result(), 0) - - mAP.update(detection, ground_truth) - - mAP.update(detection, ground_truth) - self.assertEqual(format(mAP.result(), '.5f'), - '0.14149') - - mAP.update(detection_2, ground_truth_2) - self.assertEqual(format(mAP.result(), '.5f'), - '0.13366') - mAP.reset() - mAP.update(detection, ground_truth) - self.assertEqual(format(mAP.result(), '.5f'), - '0.14149') - - ground_truth_1 = [ - np.array([[[0.51508695, 0.2911648 , 0.5903478 , 0.31360796], - [0.872 , 0.6190057 , 0.9306522 , 0.6591761 ]]]), - np.array([[]]), - np.array([[[64, 62]]]), - np.array([b'000000037777.jpg']) - ] - self.assertRaises(ValueError, mAP.update, detection, ground_truth_1) - ground_truth_2 = [ - np.array([[[0.51508695, 0.2911648 , 0.5903478 , 0.31360796], - [0.872 , 0.6190057 , 0.9306522 , 0.6591761 ]]]), - np.array([[]]), - np.array([[64]]), - np.array([b'000000037700.jpg']) - ] - self.assertRaises(ValueError, mAP.update, detection, ground_truth_2) - detection_1 = [ - np.array([[[0.16117382, 0.59801614, 0.81511605, 0.7858219 ], - [0.5589304 , 0. , 0.98301625, 0.520178 ]]]), - np.array([[0.9267181 , 0.8510787 , 0.60418576, 0.35155892, 0.31158054]]), - np.array([[ 1., 67., 51., 79., 47.]]) - ] - ground_truth_1 = [ - np.array([[[0.51508695, 0.2911648 , 0.5903478 , 0.31360796], - [0.872 , 0.6190057 , 0.9306522 , 0.6591761 ]]]), - np.array([[]]), - np.array([[64, 62]]), - np.array([b'000000011.jpg']) - ] - self.assertRaises(ValueError, mAP.update, detection_1, ground_truth_1) - ground_truth_2 = [ - np.array([[[0.51508695, 0.2911648 , 0.5903478 , 0.31360796], - [0.872 , 0.6190057 , 0.9306522 , 0.6591761 ]]]), - np.array([[]]), - np.array([[64, 62]]), - np.array([b'000000012.jpg']) - ] - detection_2 = [ - np.array([[[0.16117382, 0.59801614, 0.81511605, 0.7858219 ], - [0.5589304 , 0. , 0.98301625, 0.520178 ]]]), - np.array([[0.9267181 , 0.8510787]]), - np.array([[ 1., 67., 51., 79., 47.]]) - ] - self.assertRaises(ValueError, mAP.update, detection_2, ground_truth_2) - - def test__accuracy(self): - if hvd.rank() == 0: - predicts1 = [1] - labels1 = [0] - predicts2 = [[0, 0]] - labels2 = [[0, 1]] - predicts3 = [[[0, 1], [0, 0], [0, 1]]] - labels3 = [[[0, 1], [0, 1], [1, 0]]] - predicts4 = [[0.2, 0.8]] - labels4 = [0] - else: - predicts1 = [0, 1, 1] - labels1 = [1, 1, 1] - predicts2 = [[0, 0]] - labels2 = [[1, 1]] - predicts3 = [[[0, 1], [0, 1], [0, 1]]] - labels3 = [[[1, 0], [1, 0], [1, 0]]] - predicts4 = [[0.1, 0.9], [0.3, 0.7], [0.4, 0.6]] - labels4 = [1, 0, 0] - - metrics = METRICS('tensorflow') - acc = metrics['Accuracy']() - acc.hvd = hvd - acc.update(predicts1, labels1) - acc_result = acc.result() - self.assertEqual(acc_result, 0.5) - acc.reset() - acc.update(predicts2, labels2) - self.assertEqual(acc.result(), 0.25) - acc.reset() - acc.update(predicts3, labels3) - self.assertEqual(acc.result(), 0.25) - acc.reset() - acc.update(predicts4, labels4) - self.assertEqual(acc.result(), 0.25) - acc.reset() - acc.update(1, 1) - self.assertEqual(acc.result(), 1.0) - - wrong_predictions = [1, 0, 0] - wrong_labels = [[0, 1, 1]] - self.assertRaises(ValueError, acc.update, wrong_predictions, wrong_labels) - - metrics = METRICS('pytorch') - acc = metrics['Accuracy']() - acc.hvd = hvd - acc.update(predicts1, labels1) - acc_result = acc.result() - self.assertEqual(acc_result, 0.5) - acc.reset() - acc.update(predicts2, labels2) - self.assertEqual(acc.result(), 0.25) - acc.reset() - acc.update(predicts3, labels3) - self.assertEqual(acc.result(), 0.25) - acc.reset() - acc.update(predicts4, labels4) - self.assertEqual(acc.result(), 0.25) - - def test_mse(self): - if hvd.rank() == 0: - predicts1 = [1] - labels1 = [0] - predicts2 = [1, 1] - labels2 = [0, 1] - else: - predicts1 = [0, 0, 1] - labels1 = [1, 0, 0] - predicts2 = [1, 1] - labels2 = [1, 0] - - metrics = METRICS('tensorflow') - mse = metrics['MSE'](compare_label=False) - mse.hvd = hvd - mse.update(predicts1, labels1) - mse_result = mse.result() - self.assertEqual(mse_result, 0.75) - mse.update(predicts2, labels2) - mse_result = mse.result() - self.assertEqual(mse_result, 0.625) - - metrics = METRICS('pytorch') - mse = metrics['MSE']() - mse.hvd = hvd - mse.update(predicts1, labels1) - mse_result = mse.result() - self.assertEqual(mse_result, 0.75) - mse.update(predicts2, labels2) - mse_result = mse.result() - self.assertEqual(mse_result, 0.625) - - def test_mae(self): - if hvd.rank() == 0: - predicts1 = [1] - labels1 = [0] - predicts2 = [1, 1] - labels2 = [1, 1] - else: - predicts1 = [0, 0, 1] - labels1 = [1, 0, 0] - predicts2 = [1, 1] - labels2 = [1, 0] - - metrics = METRICS('tensorflow') - mae = metrics['MAE']() - mae.hvd = hvd - mae.update(predicts1, labels1) - mae_result = mae.result() - self.assertEqual(mae_result, 0.75) - if hvd.rank() == 1: - mae.update(0, 1) - mae_result = mae.result() - self.assertEqual(mae_result, 0.8) - mae.reset() - mae.update(predicts2, labels2) - mae_result = mae.result() - self.assertEqual(mae_result, 0.25) - - metrics = METRICS('pytorch') - mae = metrics['MAE']() - mae.hvd = hvd - mae.update(predicts1, labels1) - mae_result = mae.result() - self.assertEqual(mae_result, 0.75) - mae.update(predicts2, labels2) - mae_result = mae.result() - self.assertEqual(mae_result, 0.5) - - self.assertRaises(AssertionError, mae.update, [1], [1, 2]) - self.assertRaises(AssertionError, mae.update, 1, [1,2]) - self.assertRaises(AssertionError, mae.update, [1, 2], [1]) - self.assertRaises(AssertionError, mae.update, 1, np.array([1,2])) - - def test_rmse(self): - if hvd.rank() == 0: - predicts1 = [1] - labels1 = [1] - predicts2 = [1, 1] - labels2 = [1, 0] - else: - predicts1 = [0, 0, 1] - labels1 = [0, 0, 0] - predicts2 = [1, 1] - labels2 = [0, 0] - - metrics = METRICS('tensorflow') - rmse = metrics['RMSE']() - rmse.hvd = hvd - rmse.update(predicts1, labels1) - rmse_result = rmse.result() - self.assertEqual(rmse_result, 0.5) - rmse.reset() - rmse.update(predicts2, labels2) - rmse_result = rmse.result() - self.assertAlmostEqual(rmse_result, np.sqrt(0.75)) - - metrics = METRICS('pytorch') - rmse = metrics['RMSE']() - rmse.hvd = hvd - rmse.update(predicts1, labels1) - rmse_result = rmse.result() - self.assertEqual(rmse_result, 0.5) - rmse.update(predicts2, labels2) - rmse_result = rmse.result() - self.assertAlmostEqual(rmse_result, np.sqrt(0.5)) - - def test_loss(self): - if hvd.rank() == 0: - predicts1 = [1] - labels1 = [0] - predicts2 = [1, 0, 1] - labels2 = [1, 0, 0] - predicts3 = [1, 0] - labels3 = [0, 1] - else: - predicts1 = [0, 0, 1] - labels1 = [1, 0, 0] - predicts2 = [1] - labels2 = [0] - predicts3 = [0, 1] - labels3 = [0, 0] - - metrics = METRICS('tensorflow') - loss = metrics['Loss']() - loss.hvd = hvd - loss.update(predicts1, labels1) - loss_result = loss.result() - self.assertEqual(loss_result, 0.5) - loss.update(predicts2, labels2) - loss_result = loss.result() - self.assertEqual(loss_result, 0.625) - loss.reset() - loss.update(predicts3, labels3) - self.assertEqual(loss.result(), 0.5) - - metrics = METRICS('pytorch') - loss = metrics['Loss']() - loss.hvd = hvd - loss.update(predicts1, labels1) - loss_result = loss.result() - self.assertEqual(loss_result, 0.5) - loss.update(predicts2, labels2) - loss_result = loss.result() - self.assertEqual(loss_result, 0.625) - loss.reset() - loss.update(predicts3, labels3) - self.assertEqual(loss.result(), 0.5) - - -if __name__ == "__main__": - unittest.main() - """ - - with open("fake_ut.py", "w", encoding="utf-8") as f: - f.write(fake_ut) - - -class TestDistributed(unittest.TestCase): - @classmethod - def setUpClass(cls): - build_fake_ut() - - @classmethod - def tearDownClass(cls): - os.remove("fake_ut.py") - shutil.rmtree("./saved", ignore_errors=True) - shutil.rmtree("runs", ignore_errors=True) - - @unittest.skipIf(version1_lt_version2(tf.version.VERSION, "2.10.0"), "Only test TF 2.10.0 or above") - def test_distributed(self): - distributed_cmd = "horovodrun -np 2 python fake_ut.py" - p = subprocess.Popen( - distributed_cmd, preexec_fn=os.setsid, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True - ) # nosec - try: - out, error = p.communicate() - matches = re.findall(r"FAILED", error.decode("utf-8")) - self.assertEqual(matches, []) - - matches = re.findall(r"OK", error.decode("utf-8")) - self.assertTrue(len(matches) > 0) - - except KeyboardInterrupt: - os.killpg(os.getpgid(p.pid), signal.SIGKILL) - assert 0 - - -if __name__ == "__main__": - unittest.main() diff --git a/test/tfnewapi/test_tf_spr_base_distributed_pruning.py b/test/tfnewapi/test_tf_spr_base_distributed_pruning.py deleted file mode 100644 index 9a52f3bc5e5..00000000000 --- a/test/tfnewapi/test_tf_spr_base_distributed_pruning.py +++ /dev/null @@ -1,464 +0,0 @@ -"""Tests for the TensorFlow pruning with distributed training and inference.""" - -import hashlib -import os -import re -import shutil -import signal -import subprocess -import sys -import time -import unittest -from platform import platform, system - -import cpuinfo -import tensorflow as tf - -from neural_compressor.adaptor.tf_utils.util import version1_lt_version2 -from neural_compressor.utils import logger - - -def build_fake_ut(): - fake_ut = ''' -from __future__ import print_function -import tensorflow -from tensorflow.keras.layers import Dense, Conv2D, BatchNormalization, Activation -from tensorflow.keras.layers import AveragePooling2D, Input, Flatten -from tensorflow.keras.callbacks import LearningRateScheduler -from tensorflow.keras.callbacks import ReduceLROnPlateau -from tensorflow.keras.regularizers import l2 -from tensorflow.keras.models import Model -from tensorflow.keras.datasets import cifar10 -import numpy as np -import os -import sys -import cpuinfo -import shutil -import unittest -from neural_compressor.adaptor.tf_utils.util import version1_lt_version2 -from neural_compressor.utils import logger -from neural_compressor.utils.utility import CpuInfo - -def lr_schedule(epoch): - """Learning Rate Schedule - Learning rate is scheduled to be reduced after 80, 120, 160, 180 epochs. - Called automatically every epoch as part of callbacks during training. - # Arguments - epoch (int): The number of epochs - # Returns - lr (float32): learning rate - """ - lr = 1e-3 - if epoch > 180: - lr *= 0.5e-3 - elif epoch > 160: - lr *= 1e-3 - elif epoch > 120: - lr *= 1e-2 - elif epoch > 80: - lr *= 1e-1 - print('Learning rate: ', lr) - return lr - -def resnet_layer(inputs, - num_filters=8, - kernel_size=3, - strides=1, - activation='relu', - batch_normalization=True, - conv_first=True): - """2D Convolution-Batch Normalization-Activation stack builder - # Arguments - inputs (tensor): input tensor from input image or previous layer - num_filters (int): Conv2D number of filters - kernel_size (int): Conv2D square kernel dimensions - strides (int): Conv2D square stride dimensions - activation (string): activation name - batch_normalization (bool): whether to include batch normalization - conv_first (bool): conv-bn-activation (True) or - bn-activation-conv (False) - # Returns - x (tensor): tensor as input to the next layer - """ - conv = Conv2D(num_filters, - kernel_size=kernel_size, - strides=strides, - padding='same', - use_bias=True, - kernel_initializer='he_normal', - kernel_regularizer=l2(1e-4)) - - x = inputs - if conv_first: - x = conv(x) - # if batch_normalization: - # x = BatchNormalization()(x) - if activation is not None: - x = Activation(activation)(x) - else: - # if batch_normalization: - # x = BatchNormalization()(x) - if activation is not None: - x = Activation(activation)(x) - x = conv(x) - return x - -def resnet_v2(input_shape, depth, num_classes=10): - """ResNet Version 2 Model builder [b] - Stacks of (1 x 1)-(3 x 3)-(1 x 1) BN-ReLU-Conv2D or also known as - bottleneck layer - First shortcut connection per layer is 1 x 1 Conv2D. - Second and onwards shortcut connection is identity. - At the beginning of each stage, the feature map size is halved (downsampled) - by a convolutional layer with strides=2, while the number of filter maps is - doubled. Within each stage, the layers have the same number filters and the - same filter map sizes. - Features maps sizes: - conv1 : 32x32, 16 - stage 0: 32x32, 64 - stage 1: 16x16, 128 - stage 2: 8x8, 256 - # Arguments - input_shape (tensor): shape of input image tensor - depth (int): number of core convolutional layers - num_classes (int): number of classes (CIFAR10 has 10) - # Returns - model (Model): Keras model instance - """ - if (depth - 2) % 9 != 0: - raise ValueError('depth should be 9n+2 (eg 56 or 110 in [b])') - # Start model definition. - num_filters_in = 4 - num_res_blocks = int((depth - 2) / 9) - - inputs = Input(shape=input_shape) - # v2 performs Conv2D with BN-ReLU on input before splitting into 2 paths - x = resnet_layer(inputs=inputs, - num_filters=num_filters_in, - conv_first=True) - - # Instantiate the stack of residual units - for stage in range(1): - for res_block in range(num_res_blocks): - activation = 'relu' - batch_normalization = True - strides = 1 - if stage == 0: - num_filters_out = num_filters_in * 4 - if res_block == 0: # first layer and first stage - activation = None - batch_normalization = False - else: - num_filters_out = num_filters_in * 2 - if res_block == 0: # first layer but not first stage - strides = 2 # downsample - - # bottleneck residual unit - y = resnet_layer(inputs=x, - num_filters=num_filters_in, - kernel_size=1, - strides=strides, - activation=activation, - batch_normalization=batch_normalization, - conv_first=False) - y = resnet_layer(inputs=y, - num_filters=num_filters_in, - conv_first=False) - - y = resnet_layer(inputs=y, - num_filters=num_filters_out, - kernel_size=1, - conv_first=False) - if res_block == 0: - # linear projection residual shortcut connection to match - # changed dims - x = resnet_layer(inputs=x, - num_filters=num_filters_out, - kernel_size=1, - strides=strides, - activation=None, - batch_normalization=False) - x = tensorflow.keras.layers.add([x, y]) - - num_filters_in = num_filters_out - - # Add classifier on top. - # v2 has BN-ReLU before Pooling - # x = BatchNormalization()(x) - x = Activation('relu')(x) - x = AveragePooling2D(pool_size=8)(x) - y = Flatten()(x) - outputs = Dense(num_classes, - activation='softmax', - kernel_initializer='he_normal')(y) - - # Instantiate model. - model = Model(inputs=inputs, outputs=outputs) - return model - -# Training parameters -batch_size = 128 # orig paper trained all networks with batch_size=128 -epochs = 1 -num_classes = 10 - -# Subtracting pixel mean improves accuracy -subtract_pixel_mean = True - -n = 1 -depth = n * 9 + 2 - -def train(): - # Load the CIFAR10 data. - (x_train, y_train), (x_test, y_test) = cifar10.load_data() - - # Input image dimensions. - input_shape = x_train.shape[1:] - # Normalize data. - x_train = x_train.astype('float32') / 255 - x_test = x_test.astype('float32') / 255 - - x_train_mean = np.mean(x_train, axis=0) - x_train -= x_train_mean - x_test -= x_train_mean - - # Convert class vectors to binary class matrices. - y_train = tensorflow.keras.utils.to_categorical(y_train, num_classes) - y_test = tensorflow.keras.utils.to_categorical(y_test, num_classes) - - model = resnet_v2(input_shape=input_shape, depth=depth) - - model.compile(loss='categorical_crossentropy', - optimizer=tensorflow.keras.optimizers.Adam(learning_rate=0.01), - metrics=['accuracy']) - model.summary() - - lr_scheduler = LearningRateScheduler(lr_schedule) - - lr_reducer = ReduceLROnPlateau(factor=np.sqrt(0.1), - cooldown=0, - patience=5, - min_lr=0.5e-6) - - callbacks = [lr_reducer, lr_scheduler] - - # Run training, with or without data augmentation. - model.fit(x_train, y_train, - batch_size=batch_size, - epochs=epochs, - validation_data=(x_test, y_test), - shuffle=True, - callbacks=callbacks) - - # Score trained model. - scores = model.evaluate(x_test, y_test, verbose=1) - print('Test loss:', scores[0]) - print('Test accuracy:', scores[1]) - model.save("baseline_model") - -class TrainDataset(object): - def __init__(self): - (x_train, y_train), (x_test, y_test) = cifar10.load_data() - x_train, y_train = x_train[:100], y_train[:100] - x_train = x_train.astype('float32') / 255 - x_test = x_test.astype('float32') / 255 - - # If subtract pixel mean is enabled - x_train_mean = np.mean(x_train, axis=0) - x_train -= x_train_mean - x_test -= x_train_mean - - # Convert class vectors to binary class matrices. - y_train = tensorflow.keras.utils.to_categorical(y_train, num_classes) - y_test = tensorflow.keras.utils.to_categorical(y_test, num_classes) - self.test_images = x_test - self.test_labels = y_test - self.train_images = x_train - self.train_labels = y_train - - def __len__(self): - return len(self.train_images) - - def __getitem__(self, idx): - return self.train_images[idx], self.train_labels[idx] - -class EvalDataset(object): - def __init__(self): - (x_train, y_train), (x_test, y_test) = cifar10.load_data() - - x_train = x_train.astype('float32') / 255 - x_test = x_test.astype('float32') / 255 - - # If subtract pixel mean is enabled - x_train_mean = np.mean(x_train, axis=0) - x_train -= x_train_mean - x_test -= x_train_mean - - # Convert class vectors to binary class matrices. - y_train = tensorflow.keras.utils.to_categorical(y_train, num_classes) - y_test = tensorflow.keras.utils.to_categorical(y_test, num_classes) - self.test_images = x_test - self.test_labels = y_test - - def __len__(self): - return len(self.test_images) - - def __getitem__(self, idx): - return self.test_images[idx], self.test_labels[idx] - -class TestTensorflowPruning(unittest.TestCase): - def setUp(self): - logger.info(f"CPU: {cpuinfo.get_cpu_info()['brand_raw']}") - logger.info(f"Test: {sys.modules[__name__].__file__}-{self.__class__.__name__}-{self._testMethodName}") - - def tearDown(self): - logger.info(f"{self._testMethodName} done.\\n") - - def test_tensorflow_pruning(self): - from neural_compressor.experimental import Pruning, common - from neural_compressor.utils import logger - prune = Pruning("./fake_yaml.yaml") - prune.train_distributed = True - prune.evaluation_distributed = True - prune.train_dataloader = common.DataLoader(TrainDataset(), batch_size=16) - prune.eval_dataloader = common.DataLoader(EvalDataset(), batch_size=32) - prune.model = './baseline_model' - pruned_model = prune() - stats, sparsity = pruned_model.report_sparsity() - logger.info(stats) - logger.info(sparsity) - self.assertGreater(sparsity, 20) - self.assertGreater(prune.baseline_score, 0.729) - if bool(CpuInfo().bf16): - self.assertGreater(prune.last_score, 0.742) - else: - self.assertGreater(prune.last_score, 0.743) - - -if __name__ == '__main__': - unittest.main() - ''' - with open("fake_ut.py", "w", encoding="utf-8") as f: - f.write(fake_ut) - build_fake_yaml() - - -def build_fake_yaml(): - fake_yaml = """ - model: - name: resnet_v2_prune - framework: tensorflow - pruning: - train: - epoch: 4 - optimizer: - SGD: - learning_rate: 0.001 - momentum: 0.1 - nesterov: True - weight_decay: 0.1 - criterion: - CrossEntropyLoss: - reduction: sum - approach: - weight_compression: - initial_sparsity: 0.0 - target_sparsity: 0.2 - start_epoch: 0 - end_epoch: 4 - pruners: - - !Pruner - start_epoch: 1 - end_epoch: 3 - prune_type: basic_magnitude - evaluation: - accuracy: - metric: - topk: 1 - """ - with open("fake_yaml.yaml", "w", encoding="utf-8") as f: - f.write(fake_yaml) - - -def dir_md5_check(dir): - files_list = [] - md5_list = [] - - def get_files_list(path, list_name): - for file in sorted(os.listdir(path)): - file_path = os.path.join(path, file) - if os.path.isdir(file_path): - get_files_list(file_path, list_name) - else: - list_name.append(file_path) - - get_files_list(dir, files_list) - for file_path in files_list: - with open(file_path, "rb") as fp: - data = fp.read() - file_md5 = hashlib.md5(data).hexdigest() - md5_list.append(file_md5) - return md5_list - - -class TestDistributed(unittest.TestCase): - dst_path = "./baseline_model" - - @classmethod - def setUpClass(cls): - build_fake_ut() - build_fake_yaml() - if system().lower() == "windows": - src_path = "C:\\tmp\\.neural_compressor\\inc_ut\\resnet_v2\\" - elif system().lower() == "linux": - src_path = "/tmp/.neural_compressor/inc_ut/resnet_v2/" - if os.path.exists(src_path): - shutil.copytree(src_path, os.getcwd(), dirs_exist_ok=True) - if not os.path.exists(cls.dst_path): - raise FileNotFoundError(f"'{cls.dst_path}' doesn't exist.") - elif dir_md5_check(cls.dst_path) != [ - "65625fef42f44e6853d4d6d5e4188a49", - "a783396652bf62db3db4c9f647953175", - "c7259753419d9fc053df5b2059aef8c0", - "77f2a1045cffee9f6a43f2594a5627ba", - ]: - logger.warning("resnet_v2 baseline_model md5 verification failed.") - raise ValueError(f"'{cls.dst_path}' md5 verification failed.") - else: - logger.info("resnet_v2 baseline_model for TF-spr-base distributed pruning md5 verification succeeded.") - - @classmethod - def tearDownClass(cls): - os.remove("fake_ut.py") - os.remove("fake_yaml.yaml") - shutil.rmtree("nc_workspace", ignore_errors=True) - shutil.rmtree("baseline_model", ignore_errors=True) - - def setUp(self): - logger.info(f"CPU: {cpuinfo.get_cpu_info()['brand_raw']}") - logger.info(f"Test: {sys.modules[__name__].__file__}-{self.__class__.__name__}-{self._testMethodName}") - - def tearDown(self): - logger.info(f"{self._testMethodName} done.\n") - - @unittest.skipIf(version1_lt_version2(tf.version.VERSION, "2.10.0"), "Only test TF 2.10.0 or above") - def test_tf_distributed_pruning(self): - distributed_cmd = "horovodrun -np 2 python fake_ut.py" - p = subprocess.Popen( - distributed_cmd, preexec_fn=os.setsid, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True - ) - try: - out, _ = p.communicate() - for line in out.splitlines(): - print(line.decode().strip()) - matches = re.findall(r"FAILED", out.decode("utf-8")) - self.assertEqual(matches, []) - - matches = re.findall(r"OK", out.decode("utf-8")) - self.assertTrue(len(matches) > 0) - except KeyboardInterrupt: - os.killpg(os.getpgid(p.pid), signal.SIGKILL) - assert 0 - - -if __name__ == "__main__": - unittest.main() diff --git a/test/tfnewapi/test_tf_spr_base_distributed_tf_dataloader.py b/test/tfnewapi/test_tf_spr_base_distributed_tf_dataloader.py deleted file mode 100644 index 5003b876c0c..00000000000 --- a/test/tfnewapi/test_tf_spr_base_distributed_tf_dataloader.py +++ /dev/null @@ -1,667 +0,0 @@ -"""Tests for Distributed TensorFlow Dataloader.""" - -import collections -import json -import os -import shutil -import sys -import unittest - -import cpuinfo -import numpy as np -import tensorflow as tf - -from neural_compressor import data -from neural_compressor.adaptor.tf_utils.util import version1_lt_version2 -from neural_compressor.data import DATALOADERS, TRANSFORMS, Datasets -from neural_compressor.data.dataloaders.dataloader import DataLoader -from neural_compressor.utils import logger -from neural_compressor.utils.create_obj_from_config import create_dataloader, create_dataset - - -@unittest.skipIf(version1_lt_version2(tf.version.VERSION, "2.10.0"), "Only test TF 2.10.0 or above") -class TestDistributedTFDataDataloader(unittest.TestCase): - def setUp(self): - os.environ["CUDA_DEVICE_ORDER"] = "PCI_BUS_ID" - os.environ["CUDA_VISIBLE_DEVICES"] = "-1" - tf.compat.v1.enable_eager_execution() - self.dataset = tf.data.Dataset.from_tensors((tf.ones([3, 224, 224]), tf.ones([1000]))).repeat(600) - self.count = 0 - logger.info(f"CPU: {cpuinfo.get_cpu_info()['brand_raw']}") - logger.info(f"Test: {sys.modules[__name__].__file__}-{self.__class__.__name__}-{self._testMethodName}") - - def tearDown(self): - logger.info(f"{self._testMethodName} done.\n") - - def check_tf_dataset_with_batch_raise(self, batch_size, last_batch, distributed): - dataset_with_batch = ( - tf.data.Dataset.from_tensors((tf.ones([3, 224, 224]), tf.ones([1000]))).repeat(600).batch(2) - ) - dataloader = DATALOADERS["tensorflow"]( - dataset_with_batch, batch_size=batch_size, last_batch=last_batch, distributed=distributed - ) - for batch in dataloader: - x, y = batch - if self.count < len(dataloader) - 1: - self.assertEqual(len(x), batch_size) - else: - self.assertTrue(len(x) == dataloader.dis_dataset_len % batch_size or len(x) == batch_size) - self.assertEqual(x[0].shape, (3, 224, 224)) - self.assertEqual(x[-1].shape, (3, 224, 224)) - self.assertIsInstance(x, np.ndarray) - self.count += 1 - - def check_distributed_raise(self, batch_size, last_batch, distributed): - dataloader = DATALOADERS["tensorflow"]( - self.dataset, batch_size=batch_size, last_batch=last_batch, distributed=distributed - ) - for batch in dataloader: - x, y = batch - if self.count < len(dataloader) - 1: - self.assertEqual(len(x), batch_size) - else: - self.assertTrue(len(x) == dataloader.dis_dataset_len % batch_size or len(x) == batch_size) - self.assertEqual(x[0].shape, (3, 224, 224)) - self.assertEqual(x[-1].shape, (3, 224, 224)) - self.assertIsInstance(x, np.ndarray) - self.count += 1 - - def test_dis_tf_data_dataloader_1(self): - self.assertRaises(TypeError, self.check_tf_dataset_with_batch_raise, 32, "rollover", True) - - def test_dis_tf_data_dataloader_2(self): - self.assertRaises(TypeError, self.check_tf_dataset_with_batch_raise, 32, "no_rollover", True) - - def test_dis_tf_data_dataloader_3(self): - self.assertRaises(TypeError, self.check_tf_dataset_with_batch_raise, 1, "rollover", True) - - def test_dis_tf_data_dataloader_4(self): - self.assertRaises(TypeError, self.check_tf_dataset_with_batch_raise, 1, "no_rollover", True) - - def test_dis_tf_data_dataloader_5(self): - self.assertRaises(EnvironmentError, self.check_distributed_raise, 32, "rollover", True) - - def test_dis_tf_data_dataloader_6(self): - self.assertRaises(EnvironmentError, self.check_distributed_raise, 32, "no_rollover", True) - - def test_dis_tf_data_dataloader_7(self): - self.assertRaises(EnvironmentError, self.check_distributed_raise, 1, "rollover", True) - - def test_dis_tf_data_dataloader_8(self): - self.assertRaises(EnvironmentError, self.check_distributed_raise, 1, "no_rollover", True) - - def test_dis_tf_data_dataloader_9(self): - batch_size = 32 - dataloader = DATALOADERS["tensorflow"](self.dataset, batch_size=batch_size, last_batch="rollover") - for batch in dataloader: - x, y = batch - if self.count < len(dataloader) - 1: - self.assertEqual(len(x), batch_size) - else: - self.assertTrue(len(x) == dataloader.dis_dataset_len % batch_size or len(x) == batch_size) - self.assertEqual(x[0].shape, (3, 224, 224)) - self.assertEqual(x[-1].shape, (3, 224, 224)) - self.assertIsInstance(x, np.ndarray) - self.count += 1 - - def test_dis_tf_data_dataloader_10(self): - batch_size = 32 - dataloader = DATALOADERS["tensorflow"](self.dataset, batch_size=batch_size, last_batch="no_rollover") - for batch in dataloader: - x, y = batch - if self.count < len(dataloader) - 1: - self.assertEqual(len(x), batch_size) - else: - self.assertTrue(len(x) == dataloader.dis_dataset_len % batch_size or len(x) == batch_size) - self.assertEqual(x[0].shape, (3, 224, 224)) - self.assertEqual(x[-1].shape, (3, 224, 224)) - self.assertIsInstance(x, np.ndarray) - self.count += 1 - - def test_dis_tf_data_dataloader_11(self): - batch_size = 1 - dataloader = DATALOADERS["tensorflow"](self.dataset, batch_size=batch_size, last_batch="rollover") - for batch in dataloader: - x, y = batch - if self.count < len(dataloader) - 1: - self.assertEqual(len(x), batch_size) - else: - self.assertTrue(len(x) == dataloader.dis_dataset_len % batch_size or len(x) == batch_size) - self.assertEqual(x[0].shape, (3, 224, 224)) - self.assertEqual(x[-1].shape, (3, 224, 224)) - self.assertIsInstance(x, np.ndarray) - self.count += 1 - - def test_dis_tf_data_dataloader_12(self): - batch_size = 1 - dataloader = DATALOADERS["tensorflow"](self.dataset, batch_size=batch_size, last_batch="no_rollover") - for batch in dataloader: - x, y = batch - if self.count < len(dataloader) - 1: - self.assertEqual(len(x), batch_size) - else: - self.assertTrue(len(x) == dataloader.dis_dataset_len % batch_size or len(x) == batch_size) - self.assertEqual(x[0].shape, (3, 224, 224)) - self.assertEqual(x[-1].shape, (3, 224, 224)) - self.assertIsInstance(x, np.ndarray) - self.count += 1 - - def test_dis_tf_data_dataloader_13(self): - batch_size = 600 - dataloader = DATALOADERS["tensorflow"](self.dataset, batch_size=batch_size, last_batch="rollover") - for batch in dataloader: - x, y = batch - if self.count < len(dataloader) - 1: - self.assertEqual(len(x), batch_size) - else: - self.assertTrue(len(x) == dataloader.dis_dataset_len % batch_size or len(x) == batch_size) - self.assertEqual(x[0].shape, (3, 224, 224)) - self.assertEqual(x[-1].shape, (3, 224, 224)) - self.assertIsInstance(x, np.ndarray) - self.count += 1 - - def test_dis_tf_data_dataloader_14(self): - batch_size = 600 - dataloader = DATALOADERS["tensorflow"](self.dataset, batch_size=batch_size, last_batch="no_rollover") - for batch in dataloader: - x, y = batch - if self.count < len(dataloader) - 1: - self.assertEqual(len(x), batch_size) - else: - self.assertTrue(len(x) == dataloader.dis_dataset_len % batch_size or len(x) == batch_size) - self.assertEqual(x[0].shape, (3, 224, 224)) - self.assertEqual(x[-1].shape, (3, 224, 224)) - self.assertIsInstance(x, np.ndarray) - self.count += 1 - - -@unittest.skipIf(version1_lt_version2(tf.version.VERSION, "2.10.0"), "Only test TF 2.10.0 or above") -class TestDefaultDataLoaderSequentialSampler(unittest.TestCase): - @classmethod - def tearDownClass(cls): - if os.path.exists("minist"): - shutil.rmtree("minist") - - def setUp(self): - self.count = 0 - logger.info(f"CPU: {cpuinfo.get_cpu_info()['brand_raw']}") - logger.info(f"Test: {sys.modules[__name__].__file__}-{self.__class__.__name__}-{self._testMethodName}") - - def tearDown(self): - logger.info(f"{self._testMethodName} done.\n") - - def check_get_len_raise(self, batch_size, last_batch, distributed): - dataloader_args = { - "batch_size": batch_size, - "dataset": {"MNIST": {"root": "./minist", "train": False, "download": True}}, - "transform": {"Resize": {"size": 24}}, - "filter": None, - "last_batch": last_batch, - "distributed": distributed, - } - dataloader = create_dataloader("tensorflow", dataloader_args) - len_dataloader = len(dataloader) - - def check_distributed_raise(self, batch_size, last_batch, distributed): - dataloader_args = { - "batch_size": batch_size, - "dataset": {"MNIST": {"root": "./minist", "train": False, "download": True}}, - "transform": {"Resize": {"size": 24}}, - "filter": None, - "last_batch": last_batch, - "distributed": distributed, - } - dataloader = create_dataloader("tensorflow", dataloader_args) - for batch in dataloader: - x, y = batch - if self.count < len(dataloader) - 1: - self.assertEqual(len(x), batch_size) - else: - self.assertTrue(len(x) == dataloader.dis_dataset_len % batch_size or len(x) == batch_size) - self.assertEqual(x[0].shape, (24, 24)) - self.assertEqual(x[-1].shape, (24, 24)) - self.assertIsInstance(x, np.ndarray) - self.count += 1 - - def test_sequential_sampler1(self): - self.assertRaises(EnvironmentError, self.check_get_len_raise, 32, "rollover", True) - - def test_sequential_sampler2(self): - self.assertRaises(EnvironmentError, self.check_get_len_raise, 32, "no_rollover", True) - - def test_sequential_sampler3(self): - self.assertRaises(EnvironmentError, self.check_get_len_raise, 1, "rollover", True) - - def test_sequential_sampler4(self): - self.assertRaises(EnvironmentError, self.check_get_len_raise, 1, "no_rollover", True) - - def test_sequential_sampler5(self): - self.assertRaises(EnvironmentError, self.check_distributed_raise, 32, "rollover", True) - - def test_sequential_sampler6(self): - self.assertRaises(EnvironmentError, self.check_distributed_raise, 32, "no_rollover", True) - - def test_sequential_sampler7(self): - self.assertRaises(EnvironmentError, self.check_distributed_raise, 1, "rollover", True) - - def test_sequential_sampler8(self): - self.assertRaises(EnvironmentError, self.check_distributed_raise, 1, "no_rollover", True) - - def test_sequential_sampler9(self): - batch_size = 3332 - dataloader_args = { - "batch_size": batch_size, - "dataset": {"MNIST": {"root": "./minist", "train": False, "download": True}}, - "transform": {"Resize": {"size": 24}}, - "filter": None, - "last_batch": "rollover", - } - dataloader = create_dataloader("tensorflow", dataloader_args) - for batch in dataloader: - x, y = batch - if self.count < len(dataloader) - 1: - self.assertEqual(len(x), batch_size) - else: - self.assertTrue(len(x) == dataloader.dis_dataset_len % batch_size or len(x) == batch_size) - self.assertEqual(x[0].shape, (24, 24)) - self.assertEqual(x[-1].shape, (24, 24)) - self.assertIsInstance(x, np.ndarray) - self.count += 1 - - def test_sequential_sampler10(self): - batch_size = 3332 - dataloader_args = { - "batch_size": batch_size, - "dataset": {"MNIST": {"root": "./minist", "train": False, "download": True}}, - "transform": {"Resize": {"size": 24}}, - "filter": None, - "last_batch": "no_rollover", - } - dataloader = create_dataloader("tensorflow", dataloader_args) - for batch in dataloader: - x, y = batch - if self.count < len(dataloader) - 1: - self.assertEqual(len(x), batch_size) - else: - self.assertTrue(len(x) == dataloader.dis_dataset_len % batch_size or len(x) == batch_size) - self.assertEqual(x[0].shape, (24, 24)) - self.assertEqual(x[-1].shape, (24, 24)) - self.assertIsInstance(x, np.ndarray) - self.count += 1 - - def test_sequential_sampler11(self): - batch_size = 1 - dataloader_args = { - "batch_size": batch_size, - "dataset": {"MNIST": {"root": "./minist", "train": False, "download": True}}, - "transform": {"Resize": {"size": 24}}, - "filter": None, - "last_batch": "rollover", - } - dataloader = create_dataloader("tensorflow", dataloader_args) - for batch in dataloader: - x, y = batch - if self.count < len(dataloader) - 1: - self.assertEqual(len(x), batch_size) - else: - self.assertTrue(len(x) == dataloader.dis_dataset_len % batch_size or len(x) == batch_size) - self.assertEqual(x[0].shape, (24, 24)) - self.assertEqual(x[-1].shape, (24, 24)) - self.assertIsInstance(x, np.ndarray) - self.count += 1 - - def test_sequential_sampler12(self): - batch_size = 1 - dataloader_args = { - "batch_size": batch_size, - "dataset": {"MNIST": {"root": "./minist", "train": False, "download": True}}, - "transform": {"Resize": {"size": 24}}, - "filter": None, - "last_batch": "no_rollover", - } - dataloader = create_dataloader("tensorflow", dataloader_args) - for batch in dataloader: - x, y = batch - if self.count < len(dataloader) - 1: - self.assertEqual(len(x), batch_size) - else: - self.assertTrue(len(x) == dataloader.dis_dataset_len % batch_size or len(x) == batch_size) - self.assertEqual(x[0].shape, (24, 24)) - self.assertEqual(x[-1].shape, (24, 24)) - self.assertIsInstance(x, np.ndarray) - self.count += 1 - - def test_sequential_sampler13(self): - batch_size = 10000 - dataloader_args = { - "batch_size": batch_size, - "dataset": {"MNIST": {"root": "./minist", "train": False, "download": True}}, - "transform": {"Resize": {"size": 24}}, - "filter": None, - "last_batch": "rollover", - } - dataloader = create_dataloader("tensorflow", dataloader_args) - for batch in dataloader: - x, y = batch - if self.count < len(dataloader) - 1: - self.assertEqual(len(x), batch_size) - else: - self.assertTrue(len(x) == dataloader.dis_dataset_len % batch_size or len(x) == batch_size) - self.assertEqual(x[0].shape, (24, 24)) - self.assertEqual(x[-1].shape, (24, 24)) - self.assertIsInstance(x, np.ndarray) - self.count += 1 - - def test_sequential_sampler14(self): - batch_size = 10000 - dataloader_args = { - "batch_size": batch_size, - "dataset": {"MNIST": {"root": "./minist", "train": False, "download": True}}, - "transform": {"Resize": {"size": 24}}, - "filter": None, - "last_batch": "no_rollover", - } - dataloader = create_dataloader("tensorflow", dataloader_args) - for batch in dataloader: - x, y = batch - if self.count < len(dataloader) - 1: - self.assertEqual(len(x), batch_size) - else: - self.assertTrue(len(x) == dataloader.dis_dataset_len % batch_size or len(x) == batch_size) - self.assertEqual(x[0].shape, (24, 24)) - self.assertEqual(x[-1].shape, (24, 24)) - self.assertIsInstance(x, np.ndarray) - self.count += 1 - - -@unittest.skipIf(version1_lt_version2(tf.version.VERSION, "2.10.0"), "Only test TF 2.10.0 or above") -class TestDefaultDataLoaderIterableSampler(unittest.TestCase): - class iter_dataset(object): - def __iter__(self): - sample_size = 250 - for i in range(1, sample_size + 1): - yield np.array([i]) - - def setUp(self): - self.rank = 0 - self.size = 1 - self.count = 1 - self.dataset = self.iter_dataset() - logger.info(f"CPU: {cpuinfo.get_cpu_info()['brand_raw']}") - logger.info(f"Test: {sys.modules[__name__].__file__}-{self.__class__.__name__}-{self._testMethodName}") - - def tearDown(self): - logger.info(f"{self._testMethodName} done.\n") - - def check_get_len_raise(self, batch_size, last_batch, distributed): - dataloader = DATALOADERS["tensorflow"]( - self.dataset, batch_size=batch_size, last_batch=last_batch, distributed=distributed - ) - len_dataloader = len(dataloader) - - def check_distributed_raise(self, batch_size, last_batch, distributed): - dataloader = DATALOADERS["tensorflow"]( - self.dataset, batch_size=batch_size, last_batch=last_batch, distributed=distributed - ) - for batch in dataloader: - if self.count < len(dataloader): - self.assertEqual(len(batch), batch_size) - self.assertEqual(self.count * batch_size * self.size - self.size + self.rank + 1, batch[-1][0]) - else: - self.assertTrue(len(batch) == dataloader.dis_dataset_len % batch_size or len(batch) == batch_size) - self.assertEqual( - ((self.count - 1) * batch_size + len(batch) - 1) * self.size + self.rank + 1, batch[-1][0] - ) - break - self.count += 1 - - def test_iterable_sampler1(self): - self.assertRaises(EnvironmentError, self.check_get_len_raise, 32, "rollover", True) - - def test_iterable_sampler2(self): - self.assertRaises(EnvironmentError, self.check_get_len_raise, 32, "no_rollover", True) - - def test_iterable_sampler3(self): - self.assertRaises(EnvironmentError, self.check_get_len_raise, 1, "rollover", True) - - def test_iterable_sampler4(self): - self.assertRaises(EnvironmentError, self.check_get_len_raise, 1, "no_rollover", True) - - def test_iterable_sampler5(self): - self.assertRaises(EnvironmentError, self.check_distributed_raise, 32, "rollover", True) - - def test_iterable_sampler6(self): - self.assertRaises(EnvironmentError, self.check_distributed_raise, 32, "no_rollover", True) - - def test_iterable_sampler7(self): - self.assertRaises(EnvironmentError, self.check_distributed_raise, 1, "rollover", True) - - def test_iterable_sampler8(self): - self.assertRaises(EnvironmentError, self.check_distributed_raise, 1, "no_rollover", True) - - def test_iterable_sampler9(self): - batch_size = 128 - last_batch = "rollover" - dataloader = DATALOADERS["tensorflow"](self.dataset, batch_size=batch_size, last_batch=last_batch) - for batch in dataloader: - if self.count < len(dataloader): - self.assertEqual(len(batch), batch_size) - self.assertEqual(self.count * batch_size * self.size - self.size + self.rank + 1, batch[-1][0]) - else: - self.assertTrue(len(batch) == dataloader.dis_dataset_len % batch_size or len(batch) == batch_size) - self.assertEqual( - ((self.count - 1) * batch_size + len(batch) - 1) * self.size + self.rank + 1, batch[-1][0] - ) - break - self.count += 1 - - def test_iterable_sampler10(self): - batch_size = 128 - last_batch = "no_rollover" - dataloader = DATALOADERS["tensorflow"](self.dataset, batch_size=batch_size, last_batch=last_batch) - for batch in dataloader: - if self.count < len(dataloader): - self.assertEqual(len(batch), batch_size) - self.assertEqual(self.count * batch_size * self.size - self.size + self.rank + 1, batch[-1][0]) - else: - self.assertTrue(len(batch) == dataloader.dis_dataset_len % batch_size or len(batch) == batch_size) - self.assertEqual( - ((self.count - 1) * batch_size + len(batch) - 1) * self.size + self.rank + 1, batch[-1][0] - ) - break - self.count += 1 - - def test_iterable_sampler11(self): - batch_size = 1 - last_batch = "rollover" - dataloader = DATALOADERS["tensorflow"](self.dataset, batch_size=batch_size, last_batch=last_batch) - for batch in dataloader: - if self.count < len(dataloader): - self.assertEqual(len(batch), batch_size) - self.assertEqual(self.count * batch_size * self.size - self.size + self.rank + 1, batch[-1][0]) - else: - self.assertTrue(len(batch) == dataloader.dis_dataset_len % batch_size or len(batch) == batch_size) - self.assertEqual( - ((self.count - 1) * batch_size + len(batch) - 1) * self.size + self.rank + 1, batch[-1][0] - ) - break - self.count += 1 - - def test_iterable_sampler12(self): - batch_size = 1 - last_batch = "no_rollover" - dataloader = DATALOADERS["tensorflow"](self.dataset, batch_size=batch_size, last_batch=last_batch) - for batch in dataloader: - if self.count < len(dataloader): - self.assertEqual(len(batch), batch_size) - self.assertEqual(self.count * batch_size * self.size - self.size + self.rank + 1, batch[-1][0]) - else: - self.assertTrue(len(batch) == dataloader.dis_dataset_len % batch_size or len(batch) == batch_size) - self.assertEqual( - ((self.count - 1) * batch_size + len(batch) - 1) * self.size + self.rank + 1, batch[-1][0] - ) - break - self.count += 1 - - def test_iterable_sampler13(self): - batch_size = 1000 - last_batch = "rollover" - dataloader = DATALOADERS["tensorflow"](self.dataset, batch_size=batch_size, last_batch=last_batch) - for batch in dataloader: - if self.count < len(dataloader): - self.assertEqual(len(batch), batch_size) - self.assertEqual(self.count * batch_size * self.size - self.size + self.rank + 1, batch[-1][0]) - else: - self.assertTrue(len(batch) == dataloader.dis_dataset_len % batch_size or len(batch) == batch_size) - self.assertEqual( - ((self.count - 1) * batch_size + len(batch) - 1) * self.size + self.rank + 1, batch[-1][0] - ) - break - self.count += 1 - - def test_iterable_sampler14(self): - batch_size = 1000 - last_batch = "no_rollover" - dataloader = DATALOADERS["tensorflow"](self.dataset, batch_size=batch_size, last_batch=last_batch) - for batch in dataloader: - if self.count < len(dataloader): - self.assertEqual(len(batch), batch_size) - self.assertEqual(self.count * batch_size * self.size - self.size + self.rank + 1, batch[-1][0]) - else: - self.assertTrue(len(batch) == dataloader.dis_dataset_len % batch_size or len(batch) == batch_size) - self.assertEqual( - ((self.count - 1) * batch_size + len(batch) - 1) * self.size + self.rank + 1, batch[-1][0] - ) - break - self.count += 1 - - -@unittest.skipIf(version1_lt_version2(tf.version.VERSION, "2.10.0"), "Only test TF 2.10.0 or above") -class TestTensorflowBertDataLoader(unittest.TestCase): - label = [ - { - "paragraphs0": [ - { - "context": "Super Bowl 50 was an American football game to determine the champion of the National Football League (NFL) for the 2015 season.", - "qas": [ - { - "answers": [ - {"answer_start": 177, "text": "Denver Broncos"}, - {"answer_start": 177, "text": "Denver Broncos"}, - {"answer_start": 177, "text": "Denver Broncos"}, - ], - "question": "Which NFL team represented the AFC at Super Bowl 50?", - "id": "56be4db0acb8001400a502ec", - } - ], - } - ] - } - ] - unique_id = 1000000000 - input_ids = [ - 101, - 2029, - 5088, - 2136, - 3421, - 1996, - 10511, - 2012, - 3565, - 4605, - 2753, - 1029, - 102, - 3565, - 4605, - 2753, - 1007, - 2005, - 1996, - 2325, - 2161, - 1012, - 1996, - 2137, - 2374, - 3034, - 1006, - ] - input_mask = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] - segment_ids = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] - fake_json = json.dumps({"data": label, "version": "1.1"}) - with open("dev.json", "w") as f: - f.write(fake_json) - - @classmethod - def tearDownClass(cls): - os.remove("test.record") - os.remove("dev.json") - - def setUp(self): - logger.info(f"CPU: {cpuinfo.get_cpu_info()['brand_raw']}") - logger.info(f"Test: {sys.modules[__name__].__file__}-{self.__class__.__name__}-{self._testMethodName}") - - def tearDown(self): - logger.info(f"{self._testMethodName} done.\n") - - def check_not_implement(self, batch_size, distributed): - with tf.io.TFRecordWriter("./test.record") as writer: - features = collections.OrderedDict() - features["unique_ids"] = tf.train.Feature(int64_list=tf.train.Int64List(value=list([self.unique_id]))) - features["input_ids"] = tf.train.Feature(int64_list=tf.train.Int64List(value=list(self.input_ids))) - features["input_mask"] = tf.train.Feature(int64_list=tf.train.Int64List(value=list(self.input_mask))) - features["segment_ids"] = tf.train.Feature(int64_list=tf.train.Int64List(value=list(self.segment_ids))) - tf_example = tf.train.Example(features=tf.train.Features(feature=features)) - writer.write(tf_example.SerializeToString()) - eval_dataset = create_dataset( - "tensorflow", {"bert": {"root": "test.record", "label_file": "./dev.json"}}, None, None - ) - dataloader = DATALOADERS["tensorflow"](dataset=eval_dataset, batch_size=batch_size, distributed=distributed) - self.assertNotEqual(dataloader, None) - - def test_tf_bert_dataloader_1(self): - self.assertRaises(NotImplementedError, self.check_not_implement, 32, True) - - def test_tf_bert_dataloader_2(self): - batch_size = 128 - with tf.io.TFRecordWriter("./test.record") as writer: - features = collections.OrderedDict() - features["unique_ids"] = tf.train.Feature(int64_list=tf.train.Int64List(value=list([self.unique_id]))) - features["input_ids"] = tf.train.Feature(int64_list=tf.train.Int64List(value=list(self.input_ids))) - features["input_mask"] = tf.train.Feature(int64_list=tf.train.Int64List(value=list(self.input_mask))) - features["segment_ids"] = tf.train.Feature(int64_list=tf.train.Int64List(value=list(self.segment_ids))) - tf_example = tf.train.Example(features=tf.train.Features(feature=features)) - writer.write(tf_example.SerializeToString()) - eval_dataset = create_dataset( - "tensorflow", {"bert": {"root": "test.record", "label_file": "./dev.json"}}, None, None - ) - dataloader = DATALOADERS["tensorflow"](dataset=eval_dataset, batch_size=batch_size) - for inputs, labels in dataloader: - self.assertEqual(inputs[0], "test.record") - self.assertEqual(inputs[1], batch_size) - self.assertEqual(len(labels), 1) - - def test_tf_bert_dataloader_3(self): - batch_size = 1 - with tf.io.TFRecordWriter("./test.record") as writer: - features = collections.OrderedDict() - features["unique_ids"] = tf.train.Feature(int64_list=tf.train.Int64List(value=list([self.unique_id]))) - features["input_ids"] = tf.train.Feature(int64_list=tf.train.Int64List(value=list(self.input_ids))) - features["input_mask"] = tf.train.Feature(int64_list=tf.train.Int64List(value=list(self.input_mask))) - features["segment_ids"] = tf.train.Feature(int64_list=tf.train.Int64List(value=list(self.segment_ids))) - tf_example = tf.train.Example(features=tf.train.Features(feature=features)) - writer.write(tf_example.SerializeToString()) - eval_dataset = create_dataset( - "tensorflow", {"bert": {"root": "test.record", "label_file": "./dev.json"}}, None, None - ) - dataloader = DATALOADERS["tensorflow"](dataset=eval_dataset, batch_size=batch_size) - for inputs, labels in dataloader: - self.assertEqual(inputs[0], "test.record") - self.assertEqual(inputs[1], batch_size) - self.assertEqual(len(labels), 1) - - -if __name__ == "__main__": - unittest.main()