diff --git a/external/mmdetection/build/lib/detection_tasks/__init__.py b/external/mmdetection/build/lib/detection_tasks/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/external/mmdetection/build/lib/detection_tasks/apis/__init__.py b/external/mmdetection/build/lib/detection_tasks/apis/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/external/mmdetection/build/lib/detection_tasks/apis/detection/__init__.py b/external/mmdetection/build/lib/detection_tasks/apis/detection/__init__.py deleted file mode 100644 index 03c0dce1f69..00000000000 --- a/external/mmdetection/build/lib/detection_tasks/apis/detection/__init__.py +++ /dev/null @@ -1,42 +0,0 @@ -# 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 .config_utils import (config_from_string, config_to_string, patch_config, - prepare_for_testing, prepare_for_training, - save_config_to_file, set_hyperparams) -from .configuration import OTEDetectionConfig -from .inference_task import OTEDetectionInferenceTask -from .nncf_task import OTEDetectionNNCFTask -from .openvino_task import OpenVINODetectionTask -from .ote_utils import generate_label_schema, get_task_class, load_template -from .train_task import OTEDetectionTrainingTask - -__all__ = [ - config_from_string, - config_to_string, - generate_label_schema, - get_task_class, - load_template, - OpenVINODetectionTask, - OTEDetectionConfig, - OTEDetectionInferenceTask, - OTEDetectionNNCFTask, - OTEDetectionTrainingTask, - patch_config, - prepare_for_testing, - prepare_for_training, - save_config_to_file, - set_hyperparams, - ] diff --git a/external/mmdetection/build/lib/detection_tasks/apis/detection/config_utils.py b/external/mmdetection/build/lib/detection_tasks/apis/detection/config_utils.py deleted file mode 100644 index 57eabc94bf2..00000000000 --- a/external/mmdetection/build/lib/detection_tasks/apis/detection/config_utils.py +++ /dev/null @@ -1,339 +0,0 @@ -# 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 glob -import math -import os -import tempfile -from collections import defaultdict -from typing import List, Optional - -import torch -from mmcv import Config, ConfigDict -from ote_sdk.entities.datasets import DatasetEntity -from ote_sdk.entities.label import LabelEntity, Domain -from ote_sdk.usecases.reporting.time_monitor_callback import TimeMonitorCallback - -from detection_tasks.extension.datasets.data_utils import get_anchor_boxes, \ - get_sizes_from_dataset_entity, format_list_to_str -from mmdet.models.detectors import BaseDetector -from mmdet.utils.logger import get_root_logger - -from .configuration import OTEDetectionConfig - -try: - from sklearn.cluster import KMeans - kmeans_import = True -except ImportError: - kmeans_import = False - - -logger = get_root_logger() - - -def is_epoch_based_runner(runner_config: ConfigDict): - return 'Epoch' in runner_config.type - - -def patch_config(config: Config, work_dir: str, labels: List[LabelEntity], domain: Domain, random_seed: Optional[int] = None): - # Set runner if not defined. - if 'runner' not in config: - config.runner = {'type': 'EpochBasedRunner'} - - # Check that there is no conflict in specification of number of training epochs. - # Move global definition of epochs inside runner config. - if 'total_epochs' in config: - if is_epoch_based_runner(config.runner): - if config.runner.max_epochs != config.total_epochs: - logger.warning('Conflicting declaration of training epochs number.') - config.runner.max_epochs = config.total_epochs - else: - logger.warning(f'Total number of epochs set for an iteration based runner {config.runner.type}.') - remove_from_config(config, 'total_epochs') - - # Change runner's type. - if is_epoch_based_runner(config.runner): - logger.info(f'Replacing runner from {config.runner.type} to EpochRunnerWithCancel.') - config.runner.type = 'EpochRunnerWithCancel' - else: - logger.info(f'Replacing runner from {config.runner.type} to IterBasedRunnerWithCancel.') - config.runner.type = 'IterBasedRunnerWithCancel' - - # Add training cancelation hook. - if 'custom_hooks' not in config: - config.custom_hooks = [] - if 'CancelTrainingHook' not in {hook.type for hook in config.custom_hooks}: - config.custom_hooks.append({'type': 'CancelTrainingHook'}) - - # Remove high level data pipelines definition leaving them only inside `data` section. - remove_from_config(config, 'train_pipeline') - remove_from_config(config, 'test_pipeline') - - # Patch data pipeline, making it OTE-compatible. - patch_datasets(config, domain) - - # Remove FP16 config if running on CPU device and revert to FP32 - # https://github.com/pytorch/pytorch/issues/23377 - if not torch.cuda.is_available() and 'fp16' in config: - logger.info(f'Revert FP16 to FP32 on CPU device') - remove_from_config(config, 'fp16') - - if 'log_config' not in config: - config.log_config = ConfigDict() - # config.log_config.hooks = [] - - if 'evaluation' not in config: - config.evaluation = ConfigDict() - evaluation_metric = config.evaluation.get('metric') - if evaluation_metric is not None: - config.evaluation.save_best = evaluation_metric - - if 'checkpoint_config' not in config: - config.checkpoint_config = ConfigDict() - config.checkpoint_config.max_keep_ckpts = 5 - config.checkpoint_config.interval = config.evaluation.get('interval', 1) - - set_data_classes(config, labels) - - config.gpu_ids = range(1) - config.work_dir = work_dir - config.seed = random_seed - - -def set_hyperparams(config: Config, hyperparams: OTEDetectionConfig): - config.optimizer.lr = float(hyperparams.learning_parameters.learning_rate) - config.lr_config.warmup_iters = int(hyperparams.learning_parameters.learning_rate_warmup_iters) - if config.lr_config.warmup_iters == 0: - config.lr_config.warmup = None - config.data.samples_per_gpu = int(hyperparams.learning_parameters.batch_size) - config.data.workers_per_gpu = int(hyperparams.learning_parameters.num_workers) - total_iterations = int(hyperparams.learning_parameters.num_iters) - if is_epoch_based_runner(config.runner): - config.runner.max_epochs = total_iterations - else: - config.runner.max_iters = total_iterations - - -def patch_adaptive_repeat_dataset(config: Config, num_samples: int, - decay: float = -0.002, factor: float = 30): - """ Patch the repeat times and training epochs adatively - - Frequent dataloading inits and evaluation slow down training when the - sample size is small. Adjusting epoch and dataset repetition based on - empirical exponential decay improves the training time by applying high - repeat value to small sample size dataset and low repeat value to large - sample. - - :param config: mmcv config - :param num_samples: number of training samples - :param decay: decaying rate - :param factor: base repeat factor - """ - data_train = config.data.train - if data_train.type == 'MultiImageMixDataset': - data_train = data_train.dataset - if data_train.type == 'RepeatDataset' and getattr(data_train, 'adaptive_repeat_times', False): - if is_epoch_based_runner(config.runner): - cur_epoch = config.runner.max_epochs - new_repeat = max(round(math.exp(decay * num_samples) * factor), 1) - new_epoch = math.ceil(cur_epoch / new_repeat) - if new_epoch == 1: - return - config.runner.max_epochs = new_epoch - data_train.times = new_repeat - - -def prepare_for_testing(config: Config, dataset: DatasetEntity) -> Config: - config = copy.deepcopy(config) - # FIXME. Should working directories be modified here? - config.data.test.ote_dataset = dataset - return config - - -def prepare_for_training(config: Config, train_dataset: DatasetEntity, val_dataset: DatasetEntity, - time_monitor: TimeMonitorCallback, learning_curves: defaultdict) -> Config: - config = copy.deepcopy(config) - prepare_work_dir(config) - data_train = get_data_cfg(config) - data_train.ote_dataset = train_dataset - config.data.val.ote_dataset = val_dataset - patch_adaptive_repeat_dataset(config, len(train_dataset)) - config.custom_hooks.append({'type': 'OTEProgressHook', 'time_monitor': time_monitor, 'verbose': True}) - config.log_config.hooks.append({'type': 'OTELoggerHook', 'curves': learning_curves}) - return config - - -def config_to_string(config: Config) -> str: - """ - Convert a full mmdetection config to a string. - - :param config: configuration object to convert - :return str: string representation of the configuration - """ - config_copy = copy.deepcopy(config) - # Clean config up by removing dataset as this causes the pretty text parsing to fail. - config_copy.data.test.ote_dataset = None - config_copy.data.test.labels = None - config_copy.data.val.ote_dataset = None - config_copy.data.val.labels = None - data_train = get_data_cfg(config_copy) - data_train.ote_dataset = None - data_train.labels = None - return Config(config_copy).pretty_text - - -def config_from_string(config_string: str) -> Config: - """ - Generate an mmdetection config dict object from a string. - - :param config_string: string to parse - :return config: configuration object - """ - with tempfile.NamedTemporaryFile('w', suffix='.py') as temp_file: - temp_file.write(config_string) - temp_file.flush() - return Config.fromfile(temp_file.name) - - -def save_config_to_file(config: Config): - """ Dump the full config to a file. Filename is 'config.py', it is saved in the current work_dir. """ - filepath = os.path.join(config.work_dir, 'config.py') - config_string = config_to_string(config) - with open(filepath, 'w') as f: - f.write(config_string) - - -def prepare_work_dir(config: Config) -> str: - base_work_dir = config.work_dir - checkpoint_dirs = glob.glob(os.path.join(base_work_dir, "checkpoints_round_*")) - train_round_checkpoint_dir = os.path.join(base_work_dir, f"checkpoints_round_{len(checkpoint_dirs)}") - os.makedirs(train_round_checkpoint_dir) - logger.info(f"Checkpoints and logs for this training run are stored in {train_round_checkpoint_dir}") - config.work_dir = train_round_checkpoint_dir - if 'meta' not in config.runner: - config.runner.meta = ConfigDict() - config.runner.meta.exp_name = f"train_round_{len(checkpoint_dirs)}" - # Save training config for debugging. It is saved in the checkpoint dir for this training round. - # save_config_to_file(config) - return train_round_checkpoint_dir - - -def set_data_classes(config: Config, labels: List[LabelEntity]): - # Save labels in data configs. - for subset in ('train', 'val', 'test'): - cfg = get_data_cfg(config, subset) - cfg.labels = labels - config.data[subset].labels = labels - - # Set proper number of classes in model's detection heads. - head_names = ('mask_head', 'bbox_head', 'segm_head') - num_classes = len(labels) - if 'roi_head' in config.model: - for head_name in head_names: - if head_name in config.model.roi_head: - if isinstance(config.model.roi_head[head_name], List): - for head in config.model.roi_head[head_name]: - head.num_classes = num_classes - else: - config.model.roi_head[head_name].num_classes = num_classes - else: - for head_name in head_names: - if head_name in config.model: - config.model[head_name].num_classes = num_classes - # FIXME. ? - # self.config.model.CLASSES = label_names - - -def patch_datasets(config: Config, domain): - - def patch_color_conversion(pipeline): - # Default data format for OTE is RGB, while mmdet uses BGR, so negate the color conversion flag. - for pipeline_step in pipeline: - if pipeline_step.type == 'Normalize': - to_rgb = False - if 'to_rgb' in pipeline_step: - to_rgb = pipeline_step.to_rgb - to_rgb = not bool(to_rgb) - pipeline_step.to_rgb = to_rgb - elif pipeline_step.type == 'MultiScaleFlipAug': - patch_color_conversion(pipeline_step.transforms) - - assert 'data' in config - for subset in ('train', 'val', 'test'): - cfg = get_data_cfg(config, subset) - cfg.type = 'OTEDataset' - cfg.domain = domain - cfg.ote_dataset = None - cfg.labels = None - remove_from_config(cfg, 'ann_file') - remove_from_config(cfg, 'img_prefix') - for pipeline_step in cfg.pipeline: - if pipeline_step.type == 'LoadImageFromFile': - pipeline_step.type = 'LoadImageFromOTEDataset' - if pipeline_step.type == 'LoadAnnotations': - pipeline_step.type = 'LoadAnnotationFromOTEDataset' - pipeline_step.domain = domain - pipeline_step.min_size = cfg.pop('min_size', -1) - patch_color_conversion(cfg.pipeline) - - -def remove_from_config(config, key: str): - if key in config: - if isinstance(config, Config): - del config._cfg_dict[key] - elif isinstance(config, ConfigDict): - del config[key] - else: - raise ValueError(f'Unknown config type {type(config)}') - -def cluster_anchors(config: Config, dataset: DatasetEntity, model: BaseDetector): - if not kmeans_import: - raise ImportError('Sklearn package is not installed. To enable anchor boxes clustering, please install ' - 'packages from requirements/optional.txt or just scikit-learn package.') - - logger.info('Collecting statistics from training dataset to cluster anchor boxes...') - [target_wh] = [transforms.img_scale for transforms in config.data.test.pipeline - if transforms.type == 'MultiScaleFlipAug'] - prev_generator = config.model.bbox_head.anchor_generator - group_as = [len(width) for width in prev_generator.widths] - wh_stats = get_sizes_from_dataset_entity(dataset, target_wh) - - if len(wh_stats) < sum(group_as): - logger.warning(f'There are not enough objects to cluster: {len(wh_stats)} were detected, while it should be ' - f'at least {sum(group_as)}. Anchor box clustering was skipped.') - return config, model - - widths, heights = get_anchor_boxes(wh_stats, group_as) - logger.info(f'Anchor boxes widths have been updated from {format_list_to_str(prev_generator.widths)} ' - f'to {format_list_to_str(widths)}') - logger.info(f'Anchor boxes heights have been updated from {format_list_to_str(prev_generator.heights)} ' - f'to {format_list_to_str(heights)}') - config_generator = config.model.bbox_head.anchor_generator - config_generator.widths, config_generator.heights = widths, heights - - model_generator = model.bbox_head.anchor_generator - model_generator.widths, model_generator.heights = widths, heights - model_generator.base_anchors = model_generator.gen_base_anchors() - - config.model.bbox_head.anchor_generator = config_generator - model.bbox_head.anchor_generator = model_generator - return config, model - - -def get_data_cfg(config: Config, subset: str = 'train') -> Config: - data_cfg = config.data[subset] - while 'dataset' in data_cfg: - data_cfg = data_cfg.dataset - return data_cfg diff --git a/external/mmdetection/build/lib/detection_tasks/apis/detection/configuration.py b/external/mmdetection/build/lib/detection_tasks/apis/detection/configuration.py deleted file mode 100644 index de8ba9f8eb5..00000000000 --- a/external/mmdetection/build/lib/detection_tasks/apis/detection/configuration.py +++ /dev/null @@ -1,173 +0,0 @@ -# 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 attr import attrs -from sys import maxsize - -from ote_sdk.configuration.elements import (ParameterGroup, - add_parameter_group, - boolean_attribute, - configurable_boolean, - configurable_float, - configurable_integer, - selectable, - string_attribute) -from ote_sdk.configuration import ConfigurableParameters -from ote_sdk.configuration.enums import ModelLifecycle, AutoHPOState - -from .configuration_enums import POTQuantizationPreset - - -@attrs -class OTEDetectionConfig(ConfigurableParameters): - header = string_attribute("Configuration for an object detection task") - description = header - - @attrs - class __LearningParameters(ParameterGroup): - header = string_attribute("Learning Parameters") - description = header - - batch_size = configurable_integer( - default_value=5, - min_value=1, - max_value=512, - header="Batch size", - description="The number of training samples seen in each iteration of training. Increasing this value " - "improves training time and may make the training more stable. A larger batch size has higher " - "memory requirements.", - warning="Increasing this value may cause the system to use more memory than available, " - "potentially causing out of memory errors, please update with caution.", - affects_outcome_of=ModelLifecycle.TRAINING, - auto_hpo_state=AutoHPOState.POSSIBLE - ) - - num_iters = configurable_integer( - default_value=1, - min_value=1, - max_value=100000, - header="Number of training iterations", - description="Increasing this value causes the results to be more robust but training time will be longer.", - affects_outcome_of=ModelLifecycle.TRAINING - ) - - learning_rate = configurable_float( - default_value=0.01, - min_value=1e-07, - max_value=1e-01, - header="Learning rate", - description="Increasing this value will speed up training convergence but might make it unstable.", - affects_outcome_of=ModelLifecycle.TRAINING, - auto_hpo_state=AutoHPOState.POSSIBLE - ) - - learning_rate_warmup_iters = configurable_integer( - default_value=100, - min_value=0, - max_value=10000, - header="Number of iterations for learning rate warmup", - description="", - affects_outcome_of=ModelLifecycle.TRAINING - ) - - num_workers = configurable_integer( - default_value=4, - min_value=0, - max_value=36, - header="Number of cpu threads to use during batch generation", - description="Increasing this value might improve training speed however it might cause out of memory " - "errors. If the number of workers is set to zero, data loading will happen in the main " - "training thread.", - affects_outcome_of=ModelLifecycle.NONE - ) - - @attrs - class __Postprocessing(ParameterGroup): - header = string_attribute("Postprocessing") - description = header - - result_based_confidence_threshold = configurable_boolean( - default_value=True, - header="Result based confidence threshold", - description="Confidence threshold is derived from the results", - affects_outcome_of=ModelLifecycle.INFERENCE - ) - - confidence_threshold = configurable_float( - default_value=0.35, - min_value=0, - max_value=1, - header="Confidence threshold", - description="This threshold only takes effect if the threshold is not set based on the result.", - affects_outcome_of=ModelLifecycle.INFERENCE - ) - - @attrs - class __NNCFOptimization(ParameterGroup): - header = string_attribute("Optimization by NNCF") - description = header - visible_in_ui = boolean_attribute(False) - - enable_quantization = configurable_boolean( - default_value=True, - header="Enable quantization algorithm", - description="Enable quantization algorithm", - affects_outcome_of=ModelLifecycle.TRAINING - ) - - enable_pruning = configurable_boolean( - default_value=False, - header="Enable filter pruning algorithm", - description="Enable filter pruning algorithm", - affects_outcome_of=ModelLifecycle.TRAINING - ) - - pruning_supported = configurable_boolean( - default_value=False, - header="Whether filter pruning is supported", - description="Whether filter pruning is supported", - affects_outcome_of=ModelLifecycle.TRAINING - ) - - maximal_accuracy_degradation = configurable_float( - default_value=1.0, - min_value=0.0, - max_value=100.0, - header="Maximum accuracy degradation", - description="The maximal allowed accuracy metric drop", - affects_outcome_of=ModelLifecycle.TRAINING - ) - - @attrs - class __POTParameter(ParameterGroup): - header = string_attribute("POT Parameters") - description = header - visible_in_ui = boolean_attribute(False) - - stat_subset_size = configurable_integer( - header="Number of data samples", - description="Number of data samples used for post-training optimization", - default_value=300, - min_value=1, - max_value=maxsize - ) - - preset = selectable(default_value=POTQuantizationPreset.PERFORMANCE, header="Preset", - description="Quantization preset that defines quantization scheme", - editable=True, visible_in_ui=True) - - learning_parameters = add_parameter_group(__LearningParameters) - postprocessing = add_parameter_group(__Postprocessing) - nncf_optimization = add_parameter_group(__NNCFOptimization) - pot_parameters = add_parameter_group(__POTParameter) diff --git a/external/mmdetection/build/lib/detection_tasks/apis/detection/configuration_enums.py b/external/mmdetection/build/lib/detection_tasks/apis/detection/configuration_enums.py deleted file mode 100644 index 067e193a374..00000000000 --- a/external/mmdetection/build/lib/detection_tasks/apis/detection/configuration_enums.py +++ /dev/null @@ -1,22 +0,0 @@ -# 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 ote_sdk.configuration import ConfigurableEnum - -class POTQuantizationPreset(ConfigurableEnum): - """ - This Enum represents the quantization preset for post training optimization - """ - PERFORMANCE = 'Performance' - MIXED = 'Mixed' diff --git a/external/mmdetection/build/lib/detection_tasks/apis/detection/inference_task.py b/external/mmdetection/build/lib/detection_tasks/apis/detection/inference_task.py deleted file mode 100644 index 667fcca55bb..00000000000 --- a/external/mmdetection/build/lib/detection_tasks/apis/detection/inference_task.py +++ /dev/null @@ -1,408 +0,0 @@ -# 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 io -import os -import shutil -import tempfile -import warnings -from subprocess import run # nosec -from typing import List, Optional, Tuple - -import cv2 -import numpy as np -import torch -from mmcv.parallel import MMDataParallel -from mmcv.runner import load_checkpoint, load_state_dict -from mmcv.utils import Config -from ote_sdk.entities.annotation import Annotation -from ote_sdk.entities.datasets import DatasetEntity -from ote_sdk.entities.id import ID -from ote_sdk.entities.inference_parameters import InferenceParameters, default_progress_callback -from ote_sdk.entities.model import ModelEntity, ModelFormat, ModelOptimizationType, ModelPrecision, OptimizationMethod -from ote_sdk.entities.model_template import TaskType, task_type_to_label_domain -from ote_sdk.entities.resultset import ResultSetEntity -from ote_sdk.entities.scored_label import ScoredLabel -from ote_sdk.entities.shapes.polygon import Point, Polygon -from ote_sdk.entities.shapes.rectangle import Rectangle -from ote_sdk.entities.task_environment import TaskEnvironment -from ote_sdk.entities.tensor import TensorEntity -from ote_sdk.usecases.evaluation.metrics_helper import MetricsHelper -from ote_sdk.usecases.tasks.interfaces.evaluate_interface import IEvaluationTask -from ote_sdk.usecases.tasks.interfaces.export_interface import ExportType, IExportTask -from ote_sdk.usecases.tasks.interfaces.inference_interface import IInferenceTask -from ote_sdk.usecases.tasks.interfaces.unload_interface import IUnload -from ote_sdk.serialization.label_mapper import label_schema_to_bytes - -from mmdet.apis import export_model -from detection_tasks.apis.detection.config_utils import patch_config, prepare_for_testing, set_hyperparams -from detection_tasks.apis.detection.configuration import OTEDetectionConfig -from detection_tasks.apis.detection.ote_utils import InferenceProgressCallback -from mmdet.datasets import build_dataloader, build_dataset -from mmdet.models import build_detector -from mmdet.parallel import MMDataCPU -from mmdet.utils.collect_env import collect_env -from mmdet.utils.logger import get_root_logger - -logger = get_root_logger() - - -class OTEDetectionInferenceTask(IInferenceTask, IExportTask, IEvaluationTask, IUnload): - - _task_environment: TaskEnvironment - - def __init__(self, task_environment: TaskEnvironment): - """" - Task for inference object detection models using OTEDetection. - """ - logger.info('Loading OTEDetectionTask') - - print('ENVIRONMENT:') - for name, val in collect_env().items(): - print(f'{name}: {val}') - print('pip list:') - run('pip list', shell=True, check=True) - - self._task_environment = task_environment - self._task_type = task_environment.model_template.task_type - self._scratch_space = tempfile.mkdtemp(prefix="ote-det-scratch-") - logger.info(f'Scratch space created at {self._scratch_space}') - - self._model_name = task_environment.model_template.name - self._labels = task_environment.get_labels(False) - - template_file_path = task_environment.model_template.model_template_path - - # Get and prepare mmdet config. - self._base_dir = os.path.abspath(os.path.dirname(template_file_path)) - config_file_path = os.path.join(self._base_dir, "model.py") - self._config = Config.fromfile(config_file_path) - patch_config(self._config, self._scratch_space, self._labels, task_type_to_label_domain(self._task_type), random_seed=42) - set_hyperparams(self._config, self._hyperparams) - self.confidence_threshold: float = self._hyperparams.postprocessing.confidence_threshold - - # Set default model attributes. - self._optimization_methods = [] - self._precision = [ModelPrecision.FP32] - self._optimization_type = ModelOptimizationType.MO - - # Create and initialize PyTorch model. - logger.info('Loading the model') - self._model = self._load_model(task_environment.model) - - # Extra control variables. - self._training_work_dir = None - self._is_training = False - self._should_stop = False - logger.info('Task initialization completed') - - @property - def _hyperparams(self): - return self._task_environment.get_hyper_parameters(OTEDetectionConfig) - - def _load_model(self, model: ModelEntity): - if model is not None: - # If a model has been trained and saved for the task already, create empty model and load weights here - buffer = io.BytesIO(model.get_data("weights.pth")) - model_data = torch.load(buffer, map_location=torch.device('cpu')) - - self.confidence_threshold = model_data.get('confidence_threshold', self.confidence_threshold) - if model_data.get('anchors'): - anchors = model_data['anchors'] - self._config.model.bbox_head.anchor_generator.heights = anchors['heights'] - self._config.model.bbox_head.anchor_generator.widths = anchors['widths'] - - model = self._create_model(self._config, from_scratch=True) - - try: - load_state_dict(model, model_data['model']) - logger.info(f"Loaded model weights from Task Environment") - logger.info(f"Model architecture: {self._model_name}") - except BaseException as ex: - raise ValueError("Could not load the saved model. The model file structure is invalid.") \ - from ex - else: - # If there is no trained model yet, create model with pretrained weights as defined in the model config - # file. - model = self._create_model(self._config, from_scratch=False) - logger.info(f"No trained model in project yet. Created new model with '{self._model_name}' " - f"architecture and general-purpose pretrained weights.") - return model - - - @staticmethod - def _create_model(config: Config, from_scratch: bool = False): - """ - Creates a model, based on the configuration in config - - :param config: mmdetection configuration from which the model has to be built - :param from_scratch: bool, if True does not load any weights - - :return model: ModelEntity in training mode - """ - model_cfg = copy.deepcopy(config.model) - - init_from = None if from_scratch else config.get('load_from', None) - logger.warning(init_from) - if init_from is not None: - # No need to initialize backbone separately, if all weights are provided. - model_cfg.pretrained = None - logger.warning('build detector') - model = build_detector(model_cfg) - # Load all weights. - logger.warning('load checkpoint') - load_checkpoint(model, init_from, map_location='cpu') - else: - logger.warning('build detector') - model = build_detector(model_cfg) - return model - - - def _add_predictions_to_dataset(self, prediction_results, dataset, confidence_threshold=0.0): - """ Loop over dataset again to assign predictions. Convert from MMDetection format to OTE format. """ - for dataset_item, (all_results, feature_vector) in zip(dataset, prediction_results): - width = dataset_item.width - height = dataset_item.height - - shapes = [] - if self._task_type == TaskType.DETECTION: - for label_idx, detections in enumerate(all_results): - for i in range(detections.shape[0]): - probability = float(detections[i, 4]) - coords = detections[i, :4].astype(float).copy() - coords /= np.array([width, height, width, height], dtype=float) - coords = np.clip(coords, 0, 1) - - if probability < confidence_threshold: - continue - - assigned_label = [ScoredLabel(self._labels[label_idx], - probability=probability)] - if coords[3] - coords[1] <= 0 or coords[2] - coords[0] <= 0: - continue - - shapes.append(Annotation( - Rectangle(x1=coords[0], y1=coords[1], x2=coords[2], y2=coords[3]), - labels=assigned_label)) - elif self._task_type in {TaskType.INSTANCE_SEGMENTATION, TaskType.ROTATED_DETECTION}: - for label_idx, (boxes, masks) in enumerate(zip(*all_results)): - for mask, probability in zip(masks, boxes[:, 4]): - mask = mask.astype(np.uint8) - probability = float(probability) - contours, hierarchies = cv2.findContours(mask, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE) - if hierarchies is None: - continue - for contour, hierarchy in zip(contours, hierarchies[0]): - if hierarchy[3] != -1: - continue - if len(contour) <= 2 or probability < confidence_threshold: - continue - if self._task_type == TaskType.INSTANCE_SEGMENTATION: - points = [Point(x=point[0][0] / width, y=point[0][1] / height) for point in contour] - else: - box_points = cv2.boxPoints(cv2.minAreaRect(contour)) - points = [Point(x=point[0] / width, y=point[1] / height) for point in box_points] - labels = [ScoredLabel(self._labels[label_idx], probability=probability)] - polygon = Polygon(points=points) - if polygon.get_area() > 1e-12: - shapes.append(Annotation(polygon, labels=labels, id=ID(f"{label_idx:08}"))) - else: - raise RuntimeError( - f"Detection results assignment not implemented for task: {self._task_type}") - - dataset_item.append_annotations(shapes) - - if feature_vector is not None: - active_score = TensorEntity(name="representation_vector", numpy=feature_vector) - dataset_item.append_metadata_item(active_score, model=self._task_environment.model) - - - def infer(self, dataset: DatasetEntity, inference_parameters: Optional[InferenceParameters] = None) -> DatasetEntity: - """ Analyzes a dataset using the latest inference model. """ - - logger.info('Infer the model on the dataset') - set_hyperparams(self._config, self._hyperparams) - # There is no need to have many workers for a couple of images. - self._config.data.workers_per_gpu = max(min(self._config.data.workers_per_gpu, len(dataset) - 1), 0) - - # If confidence threshold is adaptive then up-to-date value should be stored in the model - # and should not be changed during inference. Otherwise user-specified value should be taken. - if not self._hyperparams.postprocessing.result_based_confidence_threshold: - self.confidence_threshold = self._hyperparams.postprocessing.confidence_threshold - - update_progress_callback = default_progress_callback - if inference_parameters is not None: - update_progress_callback = inference_parameters.update_progress - - time_monitor = InferenceProgressCallback(len(dataset), update_progress_callback) - - def pre_hook(module, input): - time_monitor.on_test_batch_begin(None, None) - - def hook(module, input, output): - time_monitor.on_test_batch_end(None, None) - - logger.info(f'Confidence threshold {self.confidence_threshold}') - model = self._model - with model.register_forward_pre_hook(pre_hook), model.register_forward_hook(hook): - prediction_results, _ = self._infer_detector(model, self._config, dataset, dump_features=True, eval=False) - self._add_predictions_to_dataset(prediction_results, dataset, self.confidence_threshold) - - logger.info('Inference completed') - return dataset - - - @staticmethod - def _infer_detector(model: torch.nn.Module, config: Config, dataset: DatasetEntity, dump_features: bool = False, - eval: Optional[bool] = False, metric_name: Optional[str] = 'mAP') -> Tuple[List, float]: - model.eval() - test_config = prepare_for_testing(config, dataset) - mm_val_dataset = build_dataset(test_config.data.test) - batch_size = 1 - mm_val_dataloader = build_dataloader(mm_val_dataset, - samples_per_gpu=batch_size, - workers_per_gpu=test_config.data.workers_per_gpu, - num_gpus=1, - dist=False, - shuffle=False) - if torch.cuda.is_available(): - eval_model = MMDataParallel(model.cuda(test_config.gpu_ids[0]), - device_ids=test_config.gpu_ids) - else: - eval_model = MMDataCPU(model) - - eval_predictions = [] - feature_vectors = [] - - def dump_features_hook(mod, inp, out): - with torch.no_grad(): - feature_map = out[-1] - feature_vector = torch.nn.functional.adaptive_avg_pool2d(feature_map, (1, 1)) - assert feature_vector.size(0) == 1 - feature_vectors.append(feature_vector.view(-1).detach().cpu().numpy()) - - def dummy_dump_features_hook(mod, inp, out): - feature_vectors.append(None) - - hook = dump_features_hook if dump_features else dummy_dump_features_hook - - # Use a single gpu for testing. Set in both mm_val_dataloader and eval_model - with eval_model.module.backbone.register_forward_hook(hook): - for data in mm_val_dataloader: - with torch.no_grad(): - result = eval_model(return_loss=False, rescale=True, **data) - eval_predictions.extend(result) - - # hard-code way to remove EvalHook args - for key in [ - 'interval', 'tmpdir', 'start', 'gpu_collect', 'save_best', - 'rule', 'dynamic_intervals' - ]: - config.evaluation.pop(key, None) - - metric = None - if eval: - metric = mm_val_dataset.evaluate(eval_predictions, **config.evaluation)[metric_name] - - assert len(eval_predictions) == len(feature_vectors), f'{len(eval_predictions)} != {len(feature_vectors)}' - eval_predictions = zip(eval_predictions, feature_vectors) - return eval_predictions, metric - - - def evaluate(self, - output_result_set: ResultSetEntity, - evaluation_metric: Optional[str] = None): - """ Computes performance on a resultset """ - logger.info('Evaluating the metric') - if evaluation_metric is not None: - logger.warning(f'Requested to use {evaluation_metric} metric, but parameter is ignored. Use F-measure instead.') - metric = MetricsHelper.compute_f_measure(output_result_set) - logger.info(f"F-measure after evaluation: {metric.f_measure.value}") - output_result_set.performance = metric.get_performance() - logger.info('Evaluation completed') - - - @staticmethod - def _is_docker(): - """ - Checks whether the task runs in docker container - - :return bool: True if task runs in docker - """ - path = '/proc/self/cgroup' - is_in_docker = False - if os.path.isfile(path): - with open(path) as f: - is_in_docker = is_in_docker or any('docker' in line for line in f) - is_in_docker = is_in_docker or os.path.exists('/.dockerenv') - return is_in_docker - - def unload(self): - """ - Unload the task - """ - self._delete_scratch_space() - if self._is_docker(): - logger.warning( - "Got unload request. Unloading models. Throwing Segmentation Fault on purpose") - import ctypes - ctypes.string_at(0) - else: - logger.warning("Got unload request, but not on Docker. Only clearing CUDA cache") - torch.cuda.empty_cache() - logger.warning(f"Done unloading. " - f"Torch is still occupying {torch.cuda.memory_allocated()} bytes of GPU memory") - - def export(self, - export_type: ExportType, - output_model: ModelEntity): - logger.info('Exporting the model') - assert export_type == ExportType.OPENVINO - output_model.model_format = ModelFormat.OPENVINO - output_model.optimization_type = self._optimization_type - with tempfile.TemporaryDirectory() as tempdir: - optimized_model_dir = os.path.join(tempdir, 'export') - logger.info(f'Optimized model will be temporarily saved to "{optimized_model_dir}"') - os.makedirs(optimized_model_dir, exist_ok=True) - try: - from torch.jit._trace import TracerWarning - warnings.filterwarnings('ignore', category=TracerWarning) - if torch.cuda.is_available(): - model = self._model.cuda(self._config.gpu_ids[0]) - else: - model = self._model.cpu() - pruning_transformation = OptimizationMethod.FILTER_PRUNING in self._optimization_methods - export_model(model, self._config, tempdir, target='openvino', - pruning_transformation=pruning_transformation) - bin_file = [f for f in os.listdir(tempdir) if f.endswith('.bin')][0] - xml_file = [f for f in os.listdir(tempdir) if f.endswith('.xml')][0] - with open(os.path.join(tempdir, bin_file), "rb") as f: - output_model.set_data('openvino.bin', f.read()) - with open(os.path.join(tempdir, xml_file), "rb") as f: - output_model.set_data('openvino.xml', f.read()) - output_model.set_data('confidence_threshold', np.array([self.confidence_threshold], dtype=np.float32).tobytes()) - output_model.precision = self._precision - output_model.optimization_methods = self._optimization_methods - except Exception as ex: - raise RuntimeError('Optimization was unsuccessful.') from ex - output_model.set_data("label_schema.json", label_schema_to_bytes(self._task_environment.label_schema)) - logger.info('Exporting completed') - - def _delete_scratch_space(self): - """ - Remove model checkpoints and mmdet logs - """ - if os.path.exists(self._scratch_space): - shutil.rmtree(self._scratch_space, ignore_errors=False) diff --git a/external/mmdetection/build/lib/detection_tasks/apis/detection/model_wrappers/__init__.py b/external/mmdetection/build/lib/detection_tasks/apis/detection/model_wrappers/__init__.py deleted file mode 100644 index 570552ae1af..00000000000 --- a/external/mmdetection/build/lib/detection_tasks/apis/detection/model_wrappers/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -# 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. - -__all__ = [] diff --git a/external/mmdetection/build/lib/detection_tasks/apis/detection/nncf_task.py b/external/mmdetection/build/lib/detection_tasks/apis/detection/nncf_task.py deleted file mode 100644 index 2d6aa61c8db..00000000000 --- a/external/mmdetection/build/lib/detection_tasks/apis/detection/nncf_task.py +++ /dev/null @@ -1,283 +0,0 @@ -# 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 io -import json -import os -from collections import defaultdict -from typing import Optional - -import torch -from ote_sdk.configuration import cfg_helper -from ote_sdk.configuration.helper.utils import ids_to_strings -from ote_sdk.entities.datasets import DatasetEntity -from ote_sdk.entities.model import ( - ModelEntity, - ModelFormat, - ModelOptimizationType, - ModelPrecision, - OptimizationMethod, -) -from ote_sdk.entities.optimization_parameters import default_progress_callback, OptimizationParameters -from ote_sdk.entities.subset import Subset -from ote_sdk.entities.task_environment import TaskEnvironment -from ote_sdk.serialization.label_mapper import label_schema_to_bytes -from ote_sdk.usecases.tasks.interfaces.export_interface import ExportType -from ote_sdk.usecases.tasks.interfaces.optimization_interface import IOptimizationTask -from ote_sdk.usecases.tasks.interfaces.optimization_interface import OptimizationType - -from mmdet.apis import train_detector -from mmdet.apis.fake_input import get_fake_input -from detection_tasks.apis.detection.config_utils import prepare_for_training -from detection_tasks.apis.detection.configuration import OTEDetectionConfig -from detection_tasks.apis.detection.inference_task import OTEDetectionInferenceTask -from detection_tasks.apis.detection.ote_utils import OptimizationProgressCallback -from detection_tasks.extension.utils.hooks import OTELoggerHook -from mmdet.apis.train import build_val_dataloader -from mmdet.datasets import build_dataloader, build_dataset -from mmdet.integration.nncf import check_nncf_is_enabled -from mmdet.integration.nncf import is_state_nncf -from mmdet.integration.nncf import wrap_nncf_model -from mmdet.integration.nncf import is_accuracy_aware_training_set -from mmdet.integration.nncf.config import compose_nncf_config -from mmdet.utils.logger import get_root_logger - - -logger = get_root_logger() - - -class OTEDetectionNNCFTask(OTEDetectionInferenceTask, IOptimizationTask): - - def __init__(self, task_environment: TaskEnvironment): - """" - Task for compressing object detection models using NNCF. - """ - self._val_dataloader = None - self._compression_ctrl = None - self._nncf_preset = "nncf_quantization" - check_nncf_is_enabled() - super().__init__(task_environment) - self._optimization_type = ModelOptimizationType.NNCF - - def _set_attributes_by_hyperparams(self): - quantization = self._hyperparams.nncf_optimization.enable_quantization - pruning = self._hyperparams.nncf_optimization.enable_pruning - if quantization and pruning: - self._nncf_preset = "nncf_quantization_pruning" - self._optimization_methods = [OptimizationMethod.QUANTIZATION, OptimizationMethod.FILTER_PRUNING] - self._precision = [ModelPrecision.INT8] - return - if quantization and not pruning: - self._nncf_preset = "nncf_quantization" - self._optimization_methods = [OptimizationMethod.QUANTIZATION] - self._precision = [ModelPrecision.INT8] - return - if not quantization and pruning: - self._nncf_preset = "nncf_pruning" - self._optimization_methods = [OptimizationMethod.FILTER_PRUNING] - self._precision = [ModelPrecision.FP32] - return - raise RuntimeError('Not selected optimization algorithm') - - def _load_model(self, model: ModelEntity): - # NNCF parts - nncf_config_path = os.path.join(self._base_dir, "compression_config.json") - - with open(nncf_config_path) as nncf_config_file: - common_nncf_config = json.load(nncf_config_file) - - self._set_attributes_by_hyperparams() - - optimization_config = compose_nncf_config(common_nncf_config, [self._nncf_preset]) - - max_acc_drop = self._hyperparams.nncf_optimization.maximal_accuracy_degradation / 100 - if "accuracy_aware_training" in optimization_config["nncf_config"]: - # Update maximal_absolute_accuracy_degradation - (optimization_config["nncf_config"]["accuracy_aware_training"] - ["params"]["maximal_absolute_accuracy_degradation"]) = max_acc_drop - # Force evaluation interval - self._config.evaluation.interval = 1 - else: - logger.info("NNCF config has no accuracy_aware_training parameters") - - self._config.update(optimization_config) - - compression_ctrl = None - if model is not None: - # If a model has been trained and saved for the task already, create empty model and load weights here - buffer = io.BytesIO(model.get_data("weights.pth")) - model_data = torch.load(buffer, map_location=torch.device('cpu')) - - self.confidence_threshold = model_data.get('confidence_threshold', - self._hyperparams.postprocessing.confidence_threshold) - if model_data.get('anchors'): - anchors = model_data['anchors'] - self._config.model.bbox_head.anchor_generator.heights = anchors['heights'] - self._config.model.bbox_head.anchor_generator.widths = anchors['widths'] - - model = self._create_model(self._config, from_scratch=True) - try: - if is_state_nncf(model_data): - compression_ctrl, model = wrap_nncf_model( - model, - self._config, - init_state_dict=model_data, - get_fake_input_func=get_fake_input - ) - logger.info("Loaded model weights from Task Environment and wrapped by NNCF") - else: - try: - model.load_state_dict(model_data['model']) - logger.info(f"Loaded model weights from Task Environment") - logger.info(f"Model architecture: {self._model_name}") - except BaseException as ex: - raise ValueError("Could not load the saved model. The model file structure is invalid.") \ - from ex - - logger.info(f"Loaded model weights from Task Environment") - logger.info(f"Model architecture: {self._model_name}") - except BaseException as ex: - raise ValueError("Could not load the saved model. The model file structure is invalid.") \ - from ex - else: - raise ValueError(f"No trained model in project. NNCF require pretrained weights to compress the model") - - self._compression_ctrl = compression_ctrl - return model - - def _create_compressed_model(self, dataset, config): - init_dataloader = build_dataloader( - dataset, - config.data.samples_per_gpu, - config.data.workers_per_gpu, - len(config.gpu_ids), - dist=False, - seed=config.seed) - is_acc_aware_training_set = is_accuracy_aware_training_set(config.get("nncf_config")) - - if is_acc_aware_training_set: - self._val_dataloader = build_val_dataloader(config, False) - - self._compression_ctrl, self._model = wrap_nncf_model( - self._model, - config, - val_dataloader=self._val_dataloader, - dataloader_for_init=init_dataloader, - get_fake_input_func=get_fake_input, - is_accuracy_aware=is_acc_aware_training_set) - - def optimize( - self, - optimization_type: OptimizationType, - dataset: DatasetEntity, - output_model: ModelEntity, - optimization_parameters: Optional[OptimizationParameters], - ): - if optimization_type is not OptimizationType.NNCF: - raise RuntimeError("NNCF is the only supported optimization") - - train_dataset = dataset.get_subset(Subset.TRAINING) - val_dataset = dataset.get_subset(Subset.VALIDATION) - - config = self._config - - if optimization_parameters is not None: - update_progress_callback = optimization_parameters.update_progress - else: - update_progress_callback = default_progress_callback - - time_monitor = OptimizationProgressCallback(update_progress_callback, - loading_stage_progress_percentage=5, - initialization_stage_progress_percentage=5) - learning_curves = defaultdict(OTELoggerHook.Curve) - training_config = prepare_for_training(config, train_dataset, val_dataset, time_monitor, learning_curves) - mm_train_dataset = build_dataset(training_config.data.train) - - if torch.cuda.is_available(): - self._model.cuda(training_config.gpu_ids[0]) - - # Initialize NNCF parts if start from not compressed model - if not self._compression_ctrl: - self._create_compressed_model(mm_train_dataset, training_config) - - time_monitor.on_initialization_end() - - # Run training. - self._training_work_dir = training_config.work_dir - self._is_training = True - self._model.train() - - train_detector(model=self._model, - dataset=mm_train_dataset, - cfg=training_config, - validate=True, - val_dataloader=self._val_dataloader, - compression_ctrl=self._compression_ctrl) - - # Check for stop signal when training has stopped. If should_stop is true, training was cancelled - if self._should_stop: - logger.info('Training cancelled.') - self._should_stop = False - self._is_training = False - return - - self.save_model(output_model) - - output_model.model_format = ModelFormat.BASE_FRAMEWORK - output_model.optimization_type = self._optimization_type - output_model.optimization_methods = self._optimization_methods - output_model.precision = self._precision - - self._is_training = False - - def export(self, export_type: ExportType, output_model: ModelEntity): - if self._compression_ctrl is None: - super().export(export_type, output_model) - else: - self._compression_ctrl.prepare_for_export() - self._model.disable_dynamic_graph_building() - super().export(export_type, output_model) - self._model.enable_dynamic_graph_building() - - def save_model(self, output_model: ModelEntity): - buffer = io.BytesIO() - hyperparams = self._task_environment.get_hyper_parameters(OTEDetectionConfig) - hyperparams_str = ids_to_strings(cfg_helper.convert(hyperparams, dict, enum_to_str=True)) - labels = {label.name: label.color.rgb_tuple for label in self._labels} - # WA for scheduler resetting in NNCF - compression_state = self._compression_ctrl.get_compression_state() - for algo_state in compression_state.get('ctrl_state', {}).values(): - if not algo_state.get('scheduler_state'): - algo_state['scheduler_state'] = {'current_step': 0, 'current_epoch': 0} - modelinfo = { - 'compression_state': compression_state, - 'meta': { - 'config': self._config, - 'nncf_enable_compression': True, - }, - 'model': self._model.state_dict(), - 'config': hyperparams_str, - 'labels': labels, - 'confidence_threshold': self.confidence_threshold, - 'VERSION': 1, - } - - if hasattr(self._config.model, 'bbox_head') and hasattr(self._config.model.bbox_head, 'anchor_generator'): - if getattr(self._config.model.bbox_head.anchor_generator, 'reclustering_anchors', False): - generator = self._model.bbox_head.anchor_generator - modelinfo['anchors'] = {'heights': generator.heights, 'widths': generator.widths} - - torch.save(modelinfo, buffer) - output_model.set_data("weights.pth", buffer.getvalue()) - output_model.set_data("label_schema.json", label_schema_to_bytes(self._task_environment.label_schema)) diff --git a/external/mmdetection/build/lib/detection_tasks/apis/detection/openvino_task.py b/external/mmdetection/build/lib/detection_tasks/apis/detection/openvino_task.py deleted file mode 100644 index 9c84aa40fdf..00000000000 --- a/external/mmdetection/build/lib/detection_tasks/apis/detection/openvino_task.py +++ /dev/null @@ -1,367 +0,0 @@ -# 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 attr -import copy -import io -import json -import numpy as np -import os -import ote_sdk.usecases.exportable_code.demo as demo -import tempfile -from addict import Dict as ADDict -from compression.api import DataLoader -from compression.engines.ie_engine import IEEngine -from compression.graph import load_model, save_model -from compression.graph.model_utils import compress_model_weights, get_nodes_by_type -from compression.pipeline.initializer import create_pipeline -from openvino.model_zoo.model_api.adapters import OpenvinoAdapter, create_core -from openvino.model_zoo.model_api.models import Model -from ote_sdk.entities.annotation import AnnotationSceneEntity -from ote_sdk.entities.datasets import DatasetEntity -from ote_sdk.entities.inference_parameters import InferenceParameters, default_progress_callback -from ote_sdk.entities.label_schema import LabelSchemaEntity -from ote_sdk.entities.model import ( - ModelEntity, - ModelFormat, - ModelOptimizationType, - ModelPrecision, - OptimizationMethod, -) -from ote_sdk.entities.model_template import TaskType -from ote_sdk.entities.optimization_parameters import OptimizationParameters -from ote_sdk.entities.resultset import ResultSetEntity -from ote_sdk.entities.task_environment import TaskEnvironment -from ote_sdk.serialization.label_mapper import LabelSchemaMapper, label_schema_to_bytes -from ote_sdk.usecases.evaluation.metrics_helper import MetricsHelper -from ote_sdk.usecases.exportable_code.inference import BaseInferencer -from ote_sdk.usecases.exportable_code.prediction_to_annotation_converter import ( - DetectionBoxToAnnotationConverter, - MaskToAnnotationConverter, - RotatedRectToAnnotationConverter, -) -from ote_sdk.usecases.tasks.interfaces.deployment_interface import IDeploymentTask -from ote_sdk.usecases.tasks.interfaces.evaluate_interface import IEvaluationTask -from ote_sdk.usecases.tasks.interfaces.inference_interface import IInferenceTask -from ote_sdk.usecases.tasks.interfaces.optimization_interface import IOptimizationTask, OptimizationType -from typing import Any, Dict, List, Optional, Tuple, Union -from zipfile import ZipFile - -from mmdet.utils.logger import get_root_logger -from .configuration import OTEDetectionConfig - -logger = get_root_logger() - - -class BaseInferencerWithConverter(BaseInferencer): - - def __init__(self, configuration, model, converter) -> None: - self.configuration = configuration - self.model = model - self.converter = converter - - def pre_process(self, image: np.ndarray) -> Tuple[Dict[str, np.ndarray], Dict[str, Any]]: - return self.model.preprocess(image) - - def post_process(self, prediction: Dict[str, np.ndarray], metadata: Dict[str, Any]) -> AnnotationSceneEntity: - detections = self.model.postprocess(prediction, metadata) - - return self.converter.convert_to_annotation(detections, metadata) - - def forward(self, inputs: Dict[str, np.ndarray]) -> Dict[str, np.ndarray]: - return self.model.infer_sync(inputs) - - -class OpenVINODetectionInferencer(BaseInferencerWithConverter): - def __init__( - self, - hparams: OTEDetectionConfig, - label_schema: LabelSchemaEntity, - model_file: Union[str, bytes], - weight_file: Union[str, bytes, None] = None, - device: str = "CPU", - num_requests: int = 1, - ): - """ - Inferencer implementation for OTEDetection using OpenVINO backend. - - :param hparams: Hyper parameters that the model should use. - :param label_schema: LabelSchemaEntity that was used during model training. - :param model_file: Path OpenVINO IR model definition file. - :param weight_file: Path OpenVINO IR model weights file. - :param device: Device to run inference on, such as CPU, GPU or MYRIAD. Defaults to "CPU". - :param num_requests: Maximum number of requests that the inferencer can make. Defaults to 1. - - """ - - model_adapter = OpenvinoAdapter(create_core(), model_file, weight_file, device=device, max_num_requests=num_requests) - configuration = {**attr.asdict(hparams.postprocessing, - filter=lambda attr, value: attr.name not in ['header', 'description', 'type', 'visible_in_ui'])} - model = Model.create_model('ssd', model_adapter, configuration, preload=True) - converter = DetectionBoxToAnnotationConverter(label_schema) - - super().__init__(configuration, model, converter) - - -class OpenVINOMaskInferencer(BaseInferencerWithConverter): - def __init__( - self, - hparams: OTEDetectionConfig, - label_schema: LabelSchemaEntity, - model_file: Union[str, bytes], - weight_file: Union[str, bytes, None] = None, - device: str = "CPU", - num_requests: int = 1, - ): - model_adapter = OpenvinoAdapter( - create_core(), - model_file, - weight_file, - device=device, - max_num_requests=num_requests) - - configuration = { - **attr.asdict( - hparams.postprocessing, - filter=lambda attr, value: attr.name not in [ - 'header', 'description', 'type', 'visible_in_ui'])} - - model = Model.create_model( - 'maskrcnn', - model_adapter, - configuration, - preload=True) - - converter = MaskToAnnotationConverter(label_schema) - - super().__init__(configuration, model, converter) - - -class OpenVINORotatedRectInferencer(BaseInferencerWithConverter): - def __init__( - self, - hparams: OTEDetectionConfig, - label_schema: LabelSchemaEntity, - model_file: Union[str, bytes], - weight_file: Union[str, bytes, None] = None, - device: str = "CPU", - num_requests: int = 1, - ): - model_adapter = OpenvinoAdapter( - create_core(), - model_file, - weight_file, - device=device, - max_num_requests=num_requests) - - configuration = { - **attr.asdict( - hparams.postprocessing, - filter=lambda attr, value: attr.name not in [ - 'header', 'description', 'type', 'visible_in_ui'])} - - model = Model.create_model( - 'maskrcnn', - model_adapter, - configuration, - preload=True) - - converter = RotatedRectToAnnotationConverter(label_schema) - - super().__init__(configuration, model, converter) - - -class OTEOpenVinoDataLoader(DataLoader): - def __init__(self, dataset: DatasetEntity, inferencer: BaseInferencer): - self.dataset = dataset - self.inferencer = inferencer - - def __getitem__(self, index): - image = self.dataset[index].numpy - annotation = self.dataset[index].annotation_scene - inputs, metadata = self.inferencer.pre_process(image) - - return (index, annotation), inputs, metadata - - def __len__(self): - return len(self.dataset) - - -class OpenVINODetectionTask(IDeploymentTask, IInferenceTask, IEvaluationTask, IOptimizationTask): - def __init__(self, task_environment: TaskEnvironment): - logger.info('Loading OpenVINO OTEDetectionTask') - self.task_environment = task_environment - self.model = self.task_environment.model - self.task_type = self.task_environment.model_template.task_type - self.confidence_threshold: float = 0.0 - self.inferencer = self.load_inferencer() - logger.info('OpenVINO task initialization completed') - - @property - def hparams(self): - return self.task_environment.get_hyper_parameters(OTEDetectionConfig) - - def load_inferencer(self) -> Union[OpenVINODetectionInferencer, OpenVINOMaskInferencer] : - _hparams = copy.deepcopy(self.hparams) - self.confidence_threshold = float(np.frombuffer(self.model.get_data("confidence_threshold"), dtype=np.float32)[0]) - _hparams.postprocessing.confidence_threshold = self.confidence_threshold - args = [ - _hparams, - self.task_environment.label_schema, - self.model.get_data("openvino.xml"), - self.model.get_data("openvino.bin"), - ] - if self.task_type == TaskType.DETECTION: - return OpenVINODetectionInferencer(*args) - if self.task_type == TaskType.INSTANCE_SEGMENTATION: - return OpenVINOMaskInferencer(*args) - if self.task_type == TaskType.ROTATED_DETECTION: - return OpenVINORotatedRectInferencer(*args) - raise RuntimeError(f"Unknown OpenVINO Inferencer TaskType: {self.task_type}") - - def infer(self, dataset: DatasetEntity, inference_parameters: Optional[InferenceParameters] = None) -> DatasetEntity: - logger.info('Start OpenVINO inference') - update_progress_callback = default_progress_callback - if inference_parameters is not None: - update_progress_callback = inference_parameters.update_progress - dataset_size = len(dataset) - for i, dataset_item in enumerate(dataset, 1): - predicted_scene = self.inferencer.predict(dataset_item.numpy) - dataset_item.append_annotations(predicted_scene.annotations) - update_progress_callback(int(i / dataset_size * 100)) - logger.info('OpenVINO inference completed') - return dataset - - def evaluate(self, - output_result_set: ResultSetEntity, - evaluation_metric: Optional[str] = None): - logger.info('Start OpenVINO metric evaluation') - if evaluation_metric is not None: - logger.warning(f'Requested to use {evaluation_metric} metric, but parameter is ignored. Use F-measure instead.') - output_result_set.performance = MetricsHelper.compute_f_measure(output_result_set).get_performance() - logger.info('OpenVINO metric evaluation completed') - - def deploy(self, - output_model: ModelEntity) -> None: - logger.info('Deploying the model') - - work_dir = os.path.dirname(demo.__file__) - parameters = {} - parameters['type_of_model'] = self.inferencer.model.__model__ - parameters['converter_type'] = str(self.task_type) - parameters['model_parameters'] = self.inferencer.configuration - parameters['model_parameters']['labels'] = LabelSchemaMapper.forward(self.task_environment.label_schema) - - zip_buffer = io.BytesIO() - with ZipFile(zip_buffer, 'w') as arch: - # model files - arch.writestr(os.path.join("model", "model.xml"), self.model.get_data("openvino.xml")) - arch.writestr(os.path.join("model", "model.bin"), self.model.get_data("openvino.bin")) - arch.writestr( - os.path.join("model", "config.json"), json.dumps(parameters, ensure_ascii=False, indent=4) - ) - # python files - arch.write(os.path.join(work_dir, "requirements.txt"), os.path.join("python", "requirements.txt")) - arch.write(os.path.join(work_dir, "LICENSE"), os.path.join("python", "LICENSE")) - arch.write(os.path.join(work_dir, "README.md"), os.path.join("python", "README.md")) - arch.write(os.path.join(work_dir, "demo.py"), os.path.join("python", "demo.py")) - output_model.exportable_code = zip_buffer.getvalue() - logger.info('Deploying completed') - - def optimize(self, - optimization_type: OptimizationType, - dataset: DatasetEntity, - output_model: ModelEntity, - optimization_parameters: Optional[OptimizationParameters]): - logger.info('Start POT optimization') - - if optimization_type is not OptimizationType.POT: - raise ValueError('POT is the only supported optimization type for OpenVino models') - - data_loader = OTEOpenVinoDataLoader(dataset, self.inferencer) - - with tempfile.TemporaryDirectory() as tempdir: - xml_path = os.path.join(tempdir, "model.xml") - bin_path = os.path.join(tempdir, "model.bin") - with open(xml_path, "wb") as f: - f.write(self.model.get_data("openvino.xml")) - with open(bin_path, "wb") as f: - f.write(self.model.get_data("openvino.bin")) - - model_config = ADDict({ - 'model_name': 'openvino_model', - 'model': xml_path, - 'weights': bin_path - }) - - model = load_model(model_config) - - if get_nodes_by_type(model, ['FakeQuantize']): - raise RuntimeError("Model is already optimized by POT") - - if optimization_parameters is not None: - optimization_parameters.update_progress(10) - - engine_config = ADDict({ - 'device': 'CPU' - }) - - stat_subset_size = self.hparams.pot_parameters.stat_subset_size - preset = self.hparams.pot_parameters.preset.name.lower() - - algorithms = [ - { - 'name': 'DefaultQuantization', - 'params': { - 'target_device': 'ANY', - 'preset': preset, - 'stat_subset_size': min(stat_subset_size, len(data_loader)), - 'shuffle_data': True - } - } - ] - - engine = IEEngine(config=engine_config, data_loader=data_loader, metric=None) - - pipeline = create_pipeline(algorithms, engine) - - compressed_model = pipeline.run(model) - - compress_model_weights(compressed_model) - - if optimization_parameters is not None: - optimization_parameters.update_progress(90) - - with tempfile.TemporaryDirectory() as tempdir: - save_model(compressed_model, tempdir, model_name="model") - with open(os.path.join(tempdir, "model.xml"), "rb") as f: - output_model.set_data("openvino.xml", f.read()) - with open(os.path.join(tempdir, "model.bin"), "rb") as f: - output_model.set_data("openvino.bin", f.read()) - output_model.set_data("confidence_threshold", np.array([self.confidence_threshold], dtype=np.float32).tobytes()) - - output_model.set_data("label_schema.json", label_schema_to_bytes(self.task_environment.label_schema)) - - # set model attributes for quantized model - output_model.model_format = ModelFormat.OPENVINO - output_model.optimization_type = ModelOptimizationType.POT - output_model.optimization_methods = [OptimizationMethod.QUANTIZATION] - output_model.precision = [ModelPrecision.INT8] - - self.model = output_model - self.inferencer = self.load_inferencer() - logger.info('POT optimization completed') - - if optimization_parameters is not None: - optimization_parameters.update_progress(100) diff --git a/external/mmdetection/build/lib/detection_tasks/apis/detection/ote_utils.py b/external/mmdetection/build/lib/detection_tasks/apis/detection/ote_utils.py deleted file mode 100644 index 991efaaa52b..00000000000 --- a/external/mmdetection/build/lib/detection_tasks/apis/detection/ote_utils.py +++ /dev/null @@ -1,167 +0,0 @@ -# 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 time -import colorsys -import importlib -import random -from typing import Callable, Union - -import numpy as np -import yaml -from ote_sdk.entities.color import Color -from ote_sdk.entities.id import ID -from ote_sdk.entities.label import Domain, LabelEntity -from ote_sdk.entities.label_schema import LabelGroup, LabelGroupType, LabelSchemaEntity -from ote_sdk.entities.train_parameters import UpdateProgressCallback -from ote_sdk.usecases.reporting.time_monitor_callback import TimeMonitorCallback - - -class ColorPalette: - def __init__(self, n, rng=None): - assert n > 0 - - if rng is None: - rng = random.Random(0xACE) - - candidates_num = 100 - hsv_colors = [(1.0, 1.0, 1.0)] - for _ in range(1, n): - colors_candidates = [(rng.random(), rng.uniform(0.8, 1.0), rng.uniform(0.5, 1.0)) - for _ in range(candidates_num)] - min_distances = [self.min_distance(hsv_colors, c) for c in colors_candidates] - arg_max = np.argmax(min_distances) - hsv_colors.append(colors_candidates[arg_max]) - - self.palette = [Color(*self.hsv2rgb(*hsv)) for hsv in hsv_colors] - - @staticmethod - def dist(c1, c2): - dh = min(abs(c1[0] - c2[0]), 1 - abs(c1[0] - c2[0])) * 2 - ds = abs(c1[1] - c2[1]) - dv = abs(c1[2] - c2[2]) - return dh * dh + ds * ds + dv * dv - - @classmethod - def min_distance(cls, colors_set, color_candidate): - distances = [cls.dist(o, color_candidate) for o in colors_set] - return np.min(distances) - - @staticmethod - def hsv2rgb(h, s, v): - return tuple(round(c * 255) for c in colorsys.hsv_to_rgb(h, s, v)) - - def __getitem__(self, n): - return self.palette[n % len(self.palette)] - - def __len__(self): - return len(self.palette) - - -def generate_label_schema(label_names, label_domain=Domain.DETECTION): - colors = ColorPalette(len(label_names)) if len(label_names) > 0 else [] - not_empty_labels = [LabelEntity(name=name, color=colors[i], domain=label_domain, id=ID(f"{i:08}")) for i, name in - enumerate(label_names)] - emptylabel = LabelEntity(name=f"Empty label", color=Color(42, 43, 46), - is_empty=True, domain=label_domain, id=ID(f"{len(not_empty_labels):08}")) - - label_schema = LabelSchemaEntity() - exclusive_group = LabelGroup(name="labels", labels=not_empty_labels, group_type=LabelGroupType.EXCLUSIVE) - empty_group = LabelGroup(name="empty", labels=[emptylabel], group_type=LabelGroupType.EMPTY_LABEL) - label_schema.add_group(exclusive_group) - label_schema.add_group(empty_group) - return label_schema - - -def load_template(path): - with open(path) as f: - template = yaml.safe_load(f) - return template - - -def get_task_class(path): - module_name, class_name = path.rsplit('.', 1) - module = importlib.import_module(module_name) - return getattr(module, class_name) - - -class TrainingProgressCallback(TimeMonitorCallback): - def __init__(self, update_progress_callback: UpdateProgressCallback): - super().__init__(0, 0, 0, 0, update_progress_callback=update_progress_callback) - - def on_train_batch_end(self, batch, logs=None): - super().on_train_batch_end(batch, logs) - self.update_progress_callback(self.get_progress()) - - def on_epoch_end(self, epoch, logs=None): - self.past_epoch_duration.append(time.time() - self.start_epoch_time) - self._calculate_average_epoch() - score = None - if hasattr(self.update_progress_callback, 'metric') and isinstance(logs, dict): - score = logs.get(self.update_progress_callback.metric, None) - score = float(score) if score is not None else None - self.update_progress_callback(self.get_progress(), score=score) - - -class InferenceProgressCallback(TimeMonitorCallback): - def __init__(self, num_test_steps, update_progress_callback: Callable[[int], None]): - super().__init__( - num_epoch=0, - num_train_steps=0, - num_val_steps=0, - num_test_steps=num_test_steps, - update_progress_callback=update_progress_callback) - - def on_test_batch_end(self, batch=None, logs=None): - super().on_test_batch_end(batch, logs) - self.update_progress_callback(int(self.get_progress())) - - -class OptimizationProgressCallback(TrainingProgressCallback): - """ Progress callback used for optimization using NNCF - There are three stages to the progress bar: - - 5 % model is loaded - - 10 % compressed model is initialized - - 10-100 % compressed model is being fine-tuned - """ - def __init__(self, update_progress_callback: UpdateProgressCallback, loading_stage_progress_percentage: int = 5, - initialization_stage_progress_percentage: int = 5): - super().__init__(update_progress_callback=update_progress_callback) - if loading_stage_progress_percentage + initialization_stage_progress_percentage >= 100: - raise RuntimeError('Total optimization progress percentage is more than 100%') - - self.loading_stage_progress_percentage = loading_stage_progress_percentage - self.initialization_stage_progress_percentage = initialization_stage_progress_percentage - - # set loading_stage_progress_percentage from the start as the model is already loaded at this point - self.update_progress_callback(loading_stage_progress_percentage) - - def on_train_begin(self, logs=None): - super().on_train_begin(logs) - # Callback initialization takes place here after OTEProgressHook.before_run() is called - train_percentage = 100 - self.loading_stage_progress_percentage - self.initialization_stage_progress_percentage - loading_stage_steps = self.total_steps * self.loading_stage_progress_percentage / train_percentage - initialization_stage_steps = self.total_steps * self.initialization_stage_progress_percentage / train_percentage - self.total_steps += loading_stage_steps + initialization_stage_steps - - self.current_step = loading_stage_steps + initialization_stage_steps - self.update_progress_callback(self.get_progress()) - - def on_train_end(self, logs=None): - super().on_train_end(logs) - self.update_progress_callback(self.get_progress(), score=logs) - - def on_initialization_end(self): - self.update_progress_callback(self.loading_stage_progress_percentage + - self.initialization_stage_progress_percentage) diff --git a/external/mmdetection/build/lib/detection_tasks/apis/detection/train_task.py b/external/mmdetection/build/lib/detection_tasks/apis/detection/train_task.py deleted file mode 100644 index 9d837cd2f82..00000000000 --- a/external/mmdetection/build/lib/detection_tasks/apis/detection/train_task.py +++ /dev/null @@ -1,224 +0,0 @@ -# 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 io -import os -from collections import defaultdict -from glob import glob -from typing import List, Optional - -import numpy as np -import torch -from ote_sdk.configuration import cfg_helper -from ote_sdk.configuration.helper.utils import ids_to_strings -from ote_sdk.entities.datasets import DatasetEntity -from ote_sdk.entities.metrics import (BarChartInfo, BarMetricsGroup, CurveMetric, LineChartInfo, LineMetricsGroup, MetricsGroup, - ScoreMetric, VisualizationType) -from ote_sdk.entities.model import ModelEntity, ModelPrecision -from ote_sdk.entities.resultset import ResultSetEntity -from ote_sdk.entities.subset import Subset -from ote_sdk.entities.train_parameters import TrainParameters, default_progress_callback -from ote_sdk.serialization.label_mapper import label_schema_to_bytes -from ote_sdk.usecases.evaluation.metrics_helper import MetricsHelper -from ote_sdk.usecases.tasks.interfaces.training_interface import ITrainingTask - -from mmdet.apis import train_detector -from detection_tasks.apis.detection.config_utils import cluster_anchors, prepare_for_training, set_hyperparams -from detection_tasks.apis.detection.inference_task import OTEDetectionInferenceTask -from detection_tasks.apis.detection.ote_utils import TrainingProgressCallback -from detection_tasks.extension.utils.hooks import OTELoggerHook -from mmdet.datasets import build_dataset -from mmdet.utils.logger import get_root_logger - -logger = get_root_logger() - - -class OTEDetectionTrainingTask(OTEDetectionInferenceTask, ITrainingTask): - - def _generate_training_metrics(self, learning_curves, map) -> Optional[List[MetricsGroup]]: - """ - Parses the mmdetection logs to get metrics from the latest training run - - :return output List[MetricsGroup] - """ - output: List[MetricsGroup] = [] - - # Learning curves. - for key, curve in learning_curves.items(): - n, m = len(curve.x), len(curve.y) - if n != m: - logger.warning(f"Learning curve {key} has inconsistent number of coordinates ({n} vs {m}.") - n = min(n, m) - curve.x = curve.x[:n] - curve.y = curve.y[:n] - metric_curve = CurveMetric( - xs=np.nan_to_num(curve.x).tolist(), - ys=np.nan_to_num(curve.y).tolist(), - name=key) - visualization_info = LineChartInfo(name=key, x_axis_label="Epoch", y_axis_label=key) - output.append(LineMetricsGroup(metrics=[metric_curve], visualization_info=visualization_info)) - - # Final mAP value on the validation set. - output.append( - BarMetricsGroup( - metrics=[ScoreMetric(value=map, name="mAP")], - visualization_info=BarChartInfo("Validation score", visualization_type=VisualizationType.RADIAL_BAR) - ) - ) - - return output - - - def train(self, dataset: DatasetEntity, output_model: ModelEntity, train_parameters: Optional[TrainParameters] = None): - """ Trains a model on a dataset """ - - logger.info('Training the model') - set_hyperparams(self._config, self._hyperparams) - - train_dataset = dataset.get_subset(Subset.TRAINING) - val_dataset = dataset.get_subset(Subset.VALIDATION) - - # Do clustering for SSD model - if hasattr(self._config.model, 'bbox_head') and hasattr(self._config.model.bbox_head, 'anchor_generator'): - if getattr(self._config.model.bbox_head.anchor_generator, 'reclustering_anchors', False): - self._config, self._model = cluster_anchors(self._config, train_dataset, self._model) - - config = self._config - - # Create a copy of the network. - old_model = copy.deepcopy(self._model) - - # Check for stop signal between pre-eval and training. If training is cancelled at this point, - # old_model should be restored. - if self._should_stop: - logger.info('Training cancelled.') - self._model = old_model - self._should_stop = False - self._is_training = False - self._training_work_dir = None - return - - # Run training. - update_progress_callback = default_progress_callback - if train_parameters is not None: - update_progress_callback = train_parameters.update_progress - time_monitor = TrainingProgressCallback(update_progress_callback) - learning_curves = defaultdict(OTELoggerHook.Curve) - training_config = prepare_for_training(config, train_dataset, val_dataset, time_monitor, learning_curves) - self._training_work_dir = training_config.work_dir - mm_train_dataset = build_dataset(training_config.data.train) - self._is_training = True - self._model.train() - logger.info('Start training') - train_detector(model=self._model, dataset=mm_train_dataset, cfg=training_config, validate=True) - logger.info('Training completed') - - # Check for stop signal when training has stopped. If should_stop is true, training was cancelled and no new - # model should be returned. Old train model is restored. - if self._should_stop: - logger.info('Training cancelled.') - self._model = old_model - self._should_stop = False - self._is_training = False - return - - # Load best weights. - checkpoint_file_path = glob(os.path.join(training_config.work_dir, 'best*pth')) - if len(checkpoint_file_path) == 0: - checkpoint_file_path = os.path.join(training_config.work_dir, 'latest.pth') - elif len(checkpoint_file_path) > 1: - logger.warning(f'Multiple candidates for the best checkpoint found: {checkpoint_file_path}') - checkpoint_file_path = checkpoint_file_path[0] - else: - checkpoint_file_path = checkpoint_file_path[0] - logger.info(f'Use {checkpoint_file_path} for final model weights.') - checkpoint = torch.load(checkpoint_file_path) - self._model.load_state_dict(checkpoint['state_dict']) - - # Get predictions on the validation set. - val_preds, val_map = self._infer_detector( - self._model, - config, - val_dataset, - metric_name=config.evaluation.metric, - dump_features=False, - eval=True - ) - preds_val_dataset = val_dataset.with_empty_annotations() - self._add_predictions_to_dataset(val_preds, preds_val_dataset, 0.0) - resultset = ResultSetEntity( - model=output_model, - ground_truth_dataset=val_dataset, - prediction_dataset=preds_val_dataset, - ) - - # Adjust confidence threshold. - adaptive_threshold = self._hyperparams.postprocessing.result_based_confidence_threshold - if adaptive_threshold: - logger.info('Adjusting the confidence threshold') - metric = MetricsHelper.compute_f_measure(resultset, vary_confidence_threshold=True) - best_confidence_threshold = metric.best_confidence_threshold.value - if best_confidence_threshold is None: - raise ValueError(f"Cannot compute metrics: Invalid confidence threshold!") - logger.info(f"Setting confidence threshold to {best_confidence_threshold} based on results") - self.confidence_threshold = best_confidence_threshold - else: - metric = MetricsHelper.compute_f_measure(resultset, vary_confidence_threshold=False) - - # Compose performance statistics. - # TODO[EUGENE]: ADD MAE CURVE FOR TaskType.COUNTING - performance = metric.get_performance() - performance.dashboard_metrics.extend(self._generate_training_metrics(learning_curves, val_map)) - logger.info(f'Final model performance: {str(performance)}') - - # Save resulting model. - self.save_model(output_model) - output_model.performance = performance - - self._is_training = False - logger.info('Training the model [done]') - - - def save_model(self, output_model: ModelEntity): - buffer = io.BytesIO() - hyperparams_str = ids_to_strings(cfg_helper.convert(self._hyperparams, dict, enum_to_str=True)) - - modelinfo = {'model': self._model.state_dict(), - 'config': hyperparams_str, - 'confidence_threshold': self.confidence_threshold, - 'VERSION': 1} - - if hasattr(self._config.model, 'bbox_head') and hasattr(self._config.model.bbox_head, 'anchor_generator'): - if getattr(self._config.model.bbox_head.anchor_generator, 'reclustering_anchors', False): - generator = self._model.bbox_head.anchor_generator - modelinfo['anchors'] = {'heights': generator.heights, 'widths': generator.widths} - - torch.save(modelinfo, buffer) - output_model.set_data("weights.pth", buffer.getvalue()) - output_model.set_data("label_schema.json", label_schema_to_bytes(self._task_environment.label_schema)) - output_model.precision = [ModelPrecision.FP32] - - - def cancel_training(self): - """ - Sends a cancel training signal to gracefully stop the optimizer. The signal consists of creating a - '.stop_training' file in the current work_dir. The runner checks for this file periodically. - The stopping mechanism allows stopping after each iteration, but validation will still be carried out. Stopping - will therefore take some time. - """ - logger.info("Cancel training requested.") - self._should_stop = True - stop_training_filepath = os.path.join(self._training_work_dir, '.stop_training') - open(stop_training_filepath, 'a').close() diff --git a/external/mmdetection/build/lib/detection_tasks/extension/__init__.py b/external/mmdetection/build/lib/detection_tasks/extension/__init__.py deleted file mode 100644 index 90eb8894330..00000000000 --- a/external/mmdetection/build/lib/detection_tasks/extension/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -# 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 .datasets import OTEDataset, get_annotation_mmdet_format -from .utils import (CancelTrainingHook, FixedMomentumUpdaterHook, LoadImageFromOTEDataset, EpochRunnerWithCancel, - LoadAnnotationFromOTEDataset, OTELoggerHook, OTEProgressHook, EarlyStoppingHook, ReduceLROnPlateauLrUpdaterHook) diff --git a/external/mmdetection/build/lib/detection_tasks/extension/datasets/__init__.py b/external/mmdetection/build/lib/detection_tasks/extension/datasets/__init__.py deleted file mode 100644 index e9bc5ab37d1..00000000000 --- a/external/mmdetection/build/lib/detection_tasks/extension/datasets/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -# 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 .data_utils import get_anchor_boxes, get_sizes_from_dataset_entity, format_list_to_str -from .mmdataset import OTEDataset, get_annotation_mmdet_format - -__all__ = [OTEDataset, get_annotation_mmdet_format, get_anchor_boxes, get_sizes_from_dataset_entity, format_list_to_str] diff --git a/external/mmdetection/build/lib/detection_tasks/extension/datasets/data_utils.py b/external/mmdetection/build/lib/detection_tasks/extension/datasets/data_utils.py deleted file mode 100644 index ff38a3243af..00000000000 --- a/external/mmdetection/build/lib/detection_tasks/extension/datasets/data_utils.py +++ /dev/null @@ -1,390 +0,0 @@ -# Copyright (C) 2020-2021 Intel Corporation -# SPDX-License-Identifier: Apache-2.0 -# -import json -import os.path as osp -from typing import List, Optional - -import numpy as np -from ote_sdk.entities.annotation import Annotation, AnnotationSceneEntity, AnnotationSceneKind -from ote_sdk.entities.dataset_item import DatasetItemEntity -from ote_sdk.entities.datasets import DatasetEntity -from ote_sdk.entities.id import ID -from ote_sdk.entities.image import Image -from ote_sdk.entities.label import Domain, LabelEntity -from ote_sdk.entities.scored_label import ScoredLabel -from ote_sdk.entities.shapes.polygon import Polygon, Point -from ote_sdk.entities.shapes.rectangle import Rectangle -from ote_sdk.entities.subset import Subset -from ote_sdk.utils.shape_factory import ShapeFactory -from pycocotools.coco import COCO - -from mmdet.core import BitmapMasks, PolygonMasks - -def get_classes_from_annotation(path): - with open(path) as read_file: - content = json.load(read_file) - categories = [ - v["name"] for v in sorted(content["categories"], key=lambda x: x["id"]) - ] - return categories - - -class LoadAnnotations: - def __init__(self, with_bbox=True, with_label=True, with_mask=False): - self.with_bbox = with_bbox - self.with_label = with_label - self.with_mask = with_mask - - def _load_bboxes(self, results): - ann_info = results["ann_info"] - results["gt_bboxes"] = ann_info["bboxes"].copy() - - gt_bboxes_ignore = ann_info.get("bboxes_ignore", None) - if gt_bboxes_ignore is not None: - results["gt_bboxes_ignore"] = gt_bboxes_ignore.copy() - results["bbox_fields"].append("gt_bboxes_ignore") - results["bbox_fields"].append("gt_bboxes") - return results - - def _load_labels(self, results): - results["gt_labels"] = results["ann_info"]["labels"].copy() - return results - - def _load_masks(self, results): - gt_masks = results['ann_info']['masks'] - results['gt_masks'] = gt_masks - results['mask_fields'].append('gt_masks') - return results - - def __call__(self, results): - if self.with_bbox: - results = self._load_bboxes(results) - if results is None: - return None - if self.with_label: - results = self._load_labels(results) - if self.with_mask: - results = self._load_masks(results) - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f"(with_bbox={self.with_bbox}, " - repr_str += f"with_label={self.with_label})" - return repr_str - - -class CocoDataset: - def __init__( - self, - ann_file, - classes=None, - data_root=None, - img_prefix="", - test_mode=False, - filter_empty_gt=True, - min_size=None, - with_mask=False, - ): - self.ann_file = ann_file - self.data_root = data_root - self.img_prefix = img_prefix - self.test_mode = test_mode - self.filter_empty_gt = filter_empty_gt - self.classes = self.get_classes(classes) - self.min_size = min_size - self.with_mask = with_mask - - if self.data_root is not None: - # if not osp.isabs(self.ann_file): - # self.ann_file = osp.join(self.data_root, self.ann_file) - if not (self.img_prefix is None or osp.isabs(self.img_prefix)): - self.img_prefix = osp.join(self.data_root, self.img_prefix) - - self.data_infos = self.load_annotations(self.ann_file) - - if not test_mode: - valid_inds = self._filter_imgs() - self.data_infos = [self.data_infos[i] for i in valid_inds] - - def __len__(self): - return len(self.data_infos) - - def pre_pipeline(self, results): - results["img_prefix"] = self.img_prefix - results["bbox_fields"] = [] - results["mask_fields"] = [] - results["seg_fields"] = [] - - def _rand_another(self, idx): - pool = np.where(self.flag == self.flag[idx])[0] - return np.random.choice(pool) - - def __getitem__(self, idx): - return self.prepare_img(idx) - - def __iter__(self): - for i in range(len(self)): - yield self[i] - - def prepare_img(self, idx): - img_info = self.data_infos[idx] - ann_info = self.get_ann_info(idx) - results = dict(img_info=img_info, ann_info=ann_info) - self.pre_pipeline(results) - return LoadAnnotations(with_mask=self.with_mask)(results) - - def get_classes(self, classes=None): - if classes is None: - return get_classes_from_annotation(self.ann_file) - - if isinstance(classes, (tuple, list)): - return classes - - raise ValueError(f"Unsupported type {type(classes)} of classes.") - - def load_annotations(self, ann_file): - self.coco = COCO(ann_file) - self.cat_ids = self.coco.get_cat_ids(cat_names=self.classes) - self.cat2label = {cat_id: i for i, cat_id in enumerate(self.cat_ids)} - self.img_ids = self.coco.get_img_ids() - data_infos = [] - for i in self.img_ids: - info = self.coco.load_imgs([i])[0] - info["filename"] = info["file_name"] - data_infos.append(info) - return data_infos - - def get_ann_info(self, idx): - img_id = self.data_infos[idx]["id"] - ann_ids = self.coco.get_ann_ids(img_ids=[img_id]) - ann_info = self.coco.load_anns(ann_ids) - return self._parse_ann_info(self.data_infos[idx], ann_info) - - def get_cat_ids(self, idx): - img_id = self.data_infos[idx]["id"] - ann_ids = self.coco.get_ann_ids(img_ids=[img_id]) - ann_info = self.coco.load_anns(ann_ids) - return [ann["category_id"] for ann in ann_info] - - def _filter_imgs(self, min_size=32): - """Filter images too small or without ground truths.""" - valid_inds = [] - # obtain images that contain annotation - ids_with_ann = set(_["image_id"] for _ in self.coco.anns.values()) - # obtain images that contain annotations of the required categories - ids_in_cat = set() - for i, class_id in enumerate(self.cat_ids): - ids_in_cat |= set(self.coco.cat_img_map[class_id]) - # merge the image id sets of the two conditions and use the merged set - # to filter out images if self.filter_empty_gt=True - ids_in_cat &= ids_with_ann - - valid_img_ids = [] - for i, img_info in enumerate(self.data_infos): - img_id = self.img_ids[i] - if self.filter_empty_gt and img_id not in ids_in_cat: - continue - if min(img_info["width"], img_info["height"]) >= min_size: - valid_inds.append(i) - valid_img_ids.append(img_id) - self.img_ids = valid_img_ids - return valid_inds - - def _parse_ann_info(self, img_info, ann_info): - gt_bboxes = [] - gt_labels = [] - gt_bboxes_ignore = [] - gt_masks_ann = [] - for ann in ann_info: - if ann.get("ignore", False): - continue - x1, y1, w, h = ann["bbox"] - inter_w = max(0, min(x1 + w, img_info["width"]) - max(x1, 0)) - inter_h = max(0, min(y1 + h, img_info["height"]) - max(y1, 0)) - if inter_w * inter_h == 0: - continue - if ann["area"] <= 0 or w < 1 or h < 1: - continue - if self.min_size is not None: - if w < self.min_size or h < self.min_size: - continue - if ann["category_id"] not in self.cat_ids: - continue - bbox = [x1, y1, x1 + w, y1 + h] - if ann.get("iscrowd", False): - gt_bboxes_ignore.append(bbox) - else: - gt_bboxes.append(bbox) - gt_labels.append(self.cat2label[ann["category_id"]]) - gt_masks_ann.append(ann.get("segmentation", None)) - - if gt_bboxes: - gt_bboxes = np.array(gt_bboxes, dtype=np.float32) - gt_labels = np.array(gt_labels, dtype=np.int64) - else: - gt_bboxes = np.zeros((0, 4), dtype=np.float32) - gt_labels = np.array([], dtype=np.int64) - - if gt_bboxes_ignore: - gt_bboxes_ignore = np.array(gt_bboxes_ignore, dtype=np.float32) - else: - gt_bboxes_ignore = np.zeros((0, 4), dtype=np.float32) - - seg_map = img_info["filename"].replace("jpg", "png") - - ann = dict( - bboxes=gt_bboxes, - labels=gt_labels, - bboxes_ignore=gt_bboxes_ignore, - masks=gt_masks_ann, - seg_map=seg_map, - ) - - return ann - - -def find_label_by_name(labels, name, domain): - matching_labels = [label for label in labels if label.name == name] - if len(matching_labels) == 1: - return matching_labels[0] - elif len(matching_labels) == 0: - label = LabelEntity(name=name, domain=domain, id=ID(len(labels))) - labels.append(label) - return label - else: - raise ValueError("Found multiple matching labels") - - -def load_dataset_items_coco_format( - ann_file_path: str, - data_root_dir: str, - domain: Domain, - subset: Subset = Subset.NONE, - labels_list: Optional[List[LabelEntity]] = None, - with_mask: bool = False, -): - test_mode = subset in {Subset.VALIDATION, Subset.TESTING} - - coco_dataset = CocoDataset( - ann_file=ann_file_path, - data_root=data_root_dir, - classes=None, - test_mode=test_mode, - with_mask=with_mask, - ) - coco_dataset.test_mode = False - for label_name in coco_dataset.classes: - find_label_by_name(labels_list, label_name, domain) - - dataset_items = [] - for item in coco_dataset: - - def create_gt_box(x1, y1, x2, y2, label_name): - return Annotation( - Rectangle(x1=x1, y1=y1, x2=x2, y2=y2), - labels=[ScoredLabel(label=find_label_by_name(labels_list, label_name, domain))], - ) - - def create_gt_polygon(polygon_group, label_name): - if len(polygon_group) != 1: - raise RuntimeError("Complex instance segmentation masks consisting of several polygons are not supported.") - - return Annotation( - Polygon(points=polygon_group[0]), - labels=[ScoredLabel(label=find_label_by_name(labels_list, label_name, domain))], - ) - - img_height = item["img_info"].get("height") - img_width = item["img_info"].get("width") - divisor = np.array( - [img_width, img_height, img_width, img_height], - dtype=item["gt_bboxes"].dtype, - ) - bboxes = item["gt_bboxes"] / divisor - labels = item["gt_labels"] - - assert len(bboxes) == len(labels) - if with_mask: - polygons = item["gt_masks"] - assert len(bboxes) == len(polygons) - normalized_polygons = [] - for polygon_group in polygons: - normalized_polygons.append([]) - for polygon in polygon_group: - normalized_polygon = [p / divisor[i % 2] for i, p in enumerate(polygon)] - points = [Point(normalized_polygon[i], normalized_polygon[i + 1]) for i in range(0, len(polygon), 2)] - normalized_polygons[-1].append(points) - - if item["img_prefix"] is not None: - filename = osp.join(item["img_prefix"], item["img_info"]["filename"]) - else: - filename = item["img_info"]["filename"] - - if with_mask: - shapes = [ - create_gt_polygon(polygon_group, coco_dataset.classes[label_id]) - for polygon_group, label_id in zip(normalized_polygons, labels) - ] - else: - shapes = [ - create_gt_box(x1, y1, x2, y2, coco_dataset.classes[label_id]) - for (x1, y1, x2, y2), label_id in zip(bboxes, labels) - ] - - dataset_item = DatasetItemEntity( - media=Image(file_path=filename), - annotation_scene=AnnotationSceneEntity( - annotations=shapes, kind=AnnotationSceneKind.ANNOTATION - ), - subset=subset, - ) - dataset_items.append(dataset_item) - - return dataset_items - - -def get_sizes_from_dataset_entity(dataset: DatasetEntity, target_wh: list): - """ - Function to get sizes of instances in DatasetEntity and to resize it to the target size. - - :param dataset: DatasetEntity in which to get statistics - :param target_wh: target width and height of the dataset - :return list: tuples with width and height of each instance - """ - wh_stats = [] - for item in dataset: - for ann in item.get_annotations(include_empty=False): - has_detection_labels = any(label.domain == Domain.DETECTION for label in ann.get_labels(include_empty=False)) - if has_detection_labels: - box = ShapeFactory.shape_as_rectangle(ann.shape) - w = box.width * target_wh[0] - h = box.height * target_wh[1] - wh_stats.append((w, h)) - return wh_stats - - -def get_anchor_boxes(wh_stats, group_as): - from sklearn.cluster import KMeans - kmeans = KMeans(init='k-means++', n_clusters=sum(group_as), random_state=0).fit(wh_stats) - centers = kmeans.cluster_centers_ - - areas = np.sqrt(np.prod(centers, axis=1)) - idx = np.argsort(areas) - - widths = centers[idx, 0] - heights = centers[idx, 1] - - group_as = np.cumsum(group_as[:-1]) - widths, heights = np.split(widths, group_as), np.split(heights, group_as) - return widths, heights - - -def format_list_to_str(value_lists): - """ Decrease floating point digits in logs """ - str_value = '' - for value_list in value_lists: - str_value += '[' + ', '.join(f'{value:.2f}' for value in value_list) + '], ' - return f'[{str_value[:-2]}]' diff --git a/external/mmdetection/build/lib/detection_tasks/extension/datasets/mmdataset.py b/external/mmdetection/build/lib/detection_tasks/extension/datasets/mmdataset.py deleted file mode 100644 index ba7ef08c136..00000000000 --- a/external/mmdetection/build/lib/detection_tasks/extension/datasets/mmdataset.py +++ /dev/null @@ -1,214 +0,0 @@ -# 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 copy import deepcopy -from typing import List - -import numpy as np -from ote_sdk.entities.dataset_item import DatasetItemEntity -from ote_sdk.entities.datasets import DatasetEntity -from ote_sdk.entities.label import Domain, LabelEntity -from ote_sdk.utils.shape_factory import ShapeFactory - -from mmdet.core import PolygonMasks -from mmdet.datasets.builder import DATASETS -from mmdet.datasets.custom import CustomDataset -from mmdet.datasets.pipelines import Compose - - -def get_annotation_mmdet_format( - dataset_item: DatasetItemEntity, - labels: List[LabelEntity], - domain: Domain, - min_size: int = -1, -) -> dict: - """ - Function to convert a OTE annotation to mmdetection format. This is used both in the OTEDataset class defined in - this file as in the custom pipeline element 'LoadAnnotationFromOTEDataset' - - :param dataset_item: DatasetItem for which to get annotations - :param labels: List of labels that are used in the task - :return dict: annotation information dict in mmdet format - """ - width, height = dataset_item.width, dataset_item.height - - # load annotations for item - gt_bboxes = [] - gt_labels = [] - gt_polygons = [] - - label_idx = {label.id: i for i, label in enumerate(labels)} - - for annotation in dataset_item.get_annotations(labels=labels, include_empty=False): - - box = ShapeFactory.shape_as_rectangle(annotation.shape) - - if min(box.width * width, box.height * height) < min_size: - continue - - class_indices = [ - label_idx[label.id] - for label in annotation.get_labels(include_empty=False) - if label.domain == domain - ] - - n = len(class_indices) - gt_bboxes.extend([[box.x1 * width, box.y1 * height, box.x2 * width, box.y2 * height] for _ in range(n)]) - if domain != Domain.DETECTION: - polygon = ShapeFactory.shape_as_polygon(annotation.shape) - polygon = np.array([p for point in polygon.points for p in [point.x * width, point.y * height]]) - gt_polygons.extend([[polygon] for _ in range(n)]) - gt_labels.extend(class_indices) - - if len(gt_bboxes) > 0: - ann_info = dict( - bboxes=np.array(gt_bboxes, dtype=np.float32).reshape(-1, 4), - labels=np.array(gt_labels, dtype=int), - masks=PolygonMasks( - gt_polygons, height=height, width=width) if gt_polygons else []) - else: - ann_info = dict( - bboxes=np.zeros((0, 4), dtype=np.float32), - labels=np.array([], dtype=int), - masks=[]) - return ann_info - - -@DATASETS.register_module() -class OTEDataset(CustomDataset): - """ - Wrapper that allows using a OTE dataset to train mmdetection models. This wrapper is not based on the filesystem, - but instead loads the items here directly from the OTE DatasetEntity object. - - The wrapper overwrites some methods of the CustomDataset class: prepare_train_img, prepare_test_img and prepipeline - Naming of certain attributes might seem a bit peculiar but this is due to the conventions set in CustomDataset. For - instance, CustomDatasets expects the dataset items to be stored in the attribute data_infos, which is why it is - named like that and not dataset_items. - - """ - - class _DataInfoProxy: - """ - This class is intended to be a wrapper to use it in CustomDataset-derived class as `self.data_infos`. - Instead of using list `data_infos` as in CustomDataset, our implementation of dataset OTEDataset - uses this proxy class with overriden __len__ and __getitem__; this proxy class - forwards data access operations to ote_dataset and converts the dataset items to the view - convenient for mmdetection. - """ - def __init__(self, ote_dataset, labels): - self.ote_dataset = ote_dataset - self.labels = labels - - def __len__(self): - return len(self.ote_dataset) - - def __getitem__(self, index): - """ - Prepare a dict 'data_info' that is expected by the mmdet pipeline to handle images and annotations - :return data_info: dictionary that contains the image and image metadata, as well as the labels of the objects - in the image - """ - - dataset = self.ote_dataset - item = dataset[index] - - height, width = item.height, item.width - - data_info = dict(dataset_item=item, width=width, height=height, index=index, - ann_info=dict(label_list=self.labels)) - - return data_info - - def __init__(self, ote_dataset: DatasetEntity, labels: List[LabelEntity], pipeline, domain, test_mode: bool = False): - self.ote_dataset = ote_dataset - self.labels = labels - self.CLASSES = list(label.name for label in labels) - self.domain = domain - self.test_mode = test_mode - - # Instead of using list data_infos as in CustomDataset, this implementation of dataset - # uses a proxy class with overriden __len__ and __getitem__; this proxy class - # forwards data access operations to ote_dataset. - # Note that list `data_infos` cannot be used here, since OTE dataset class does not have interface to - # get only annotation of a data item, so we would load the whole data item (including image) - # even if we need only checking aspect ratio of the image; due to it - # this implementation of dataset does not uses such tricks as skipping images with wrong aspect ratios or - # small image size, since otherwise reading the whole dataset during initialization will be required. - self.data_infos = OTEDataset._DataInfoProxy(ote_dataset, labels) - - self.proposals = None # Attribute expected by mmdet but not used for OTE datasets - - if not test_mode: - self._set_group_flag() - - self.pipeline = Compose(pipeline) - - def _set_group_flag(self): - """Set flag for grouping images. - - Originally, in Custom dataset, images with aspect ratio greater than 1 will be set as group 1, - otherwise group 0. - This implementation will set group 0 for every image. - """ - self.flag = np.zeros(len(self), dtype=np.uint8) - - def _rand_another(self, idx): - return np.random.choice(len(self)) - - # In contrast with CustomDataset this implementation of dataset - # does not filter images w.r.t. the min size - def _filter_imgs(self, min_size=32): - raise NotImplementedError - - def prepare_train_img(self, idx: int) -> dict: - """Get training data and annotations after pipeline. - - :param idx: int, Index of data. - :return dict: Training data and annotation after pipeline with new keys introduced by pipeline. - """ - item = deepcopy(self.data_infos[idx]) - self.pre_pipeline(item) - return self.pipeline(item) - - def prepare_test_img(self, idx: int) -> dict: - """Get testing data after pipeline. - - :param idx: int, Index of data. - :return dict: Testing data after pipeline with new keys introduced by pipeline. - """ - # FIXME. - # item = deepcopy(self.data_infos[idx]) - item = self.data_infos[idx] - self.pre_pipeline(item) - return self.pipeline(item) - - @staticmethod - def pre_pipeline(results: dict): - """Prepare results dict for pipeline. Add expected keys to the dict. """ - results['bbox_fields'] = [] - results['mask_fields'] = [] - results['seg_fields'] = [] - - def get_ann_info(self, idx): - """ - This method is used for evaluation of predictions. The CustomDataset class implements a method - CustomDataset.evaluate, which uses the class method get_ann_info to retrieve annotations. - - :param idx: index of the dataset item for which to get the annotations - :return ann_info: dict that contains the coordinates of the bboxes and their corresponding labels - """ - dataset_item = self.ote_dataset[idx] - labels = self.labels - return get_annotation_mmdet_format(dataset_item, labels, self.domain) - diff --git a/external/mmdetection/build/lib/detection_tasks/extension/utils/__init__.py b/external/mmdetection/build/lib/detection_tasks/extension/utils/__init__.py deleted file mode 100644 index 144196cf57b..00000000000 --- a/external/mmdetection/build/lib/detection_tasks/extension/utils/__init__.py +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright (C) 2021-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 .hooks import CancelTrainingHook, FixedMomentumUpdaterHook, OTELoggerHook, OTEProgressHook -from .hooks import EarlyStoppingHook, ReduceLROnPlateauLrUpdaterHook, StopLossNanTrainingHook -from .pipelines import LoadImageFromOTEDataset, LoadAnnotationFromOTEDataset -from .runner import EpochRunnerWithCancel - -__all__ = [CancelTrainingHook, FixedMomentumUpdaterHook, LoadImageFromOTEDataset, EpochRunnerWithCancel, - LoadAnnotationFromOTEDataset, OTELoggerHook, OTEProgressHook, EarlyStoppingHook, - ReduceLROnPlateauLrUpdaterHook, StopLossNanTrainingHook] diff --git a/external/mmdetection/build/lib/detection_tasks/extension/utils/hooks.py b/external/mmdetection/build/lib/detection_tasks/extension/utils/hooks.py deleted file mode 100644 index dbde2753aa0..00000000000 --- a/external/mmdetection/build/lib/detection_tasks/extension/utils/hooks.py +++ /dev/null @@ -1,503 +0,0 @@ -# Copyright (C) 2021-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 logging -import math -import os -from math import inf, isnan -from collections import defaultdict - -from mmcv.runner.hooks import HOOKS, Hook, LoggerHook, LrUpdaterHook -from mmcv.runner import BaseRunner, EpochBasedRunner -from mmcv.runner.dist_utils import master_only -from mmcv.utils import print_log - -from mmdet.utils.logger import get_root_logger - - -logger = get_root_logger() - - -@HOOKS.register_module() -class CancelTrainingHook(Hook): - def __init__(self, interval: int = 5): - """ - Periodically check whether whether a stop signal is sent to the runner during model training. - Every 'check_interval' iterations, the work_dir for the runner is checked to see if a file '.stop_training' - is present. If it is, training is stopped. - - :param interval: Period for checking for stop signal, given in iterations. - - """ - self.interval = interval - - @staticmethod - def _check_for_stop_signal(runner: BaseRunner): - work_dir = runner.work_dir - stop_filepath = os.path.join(work_dir, '.stop_training') - if os.path.exists(stop_filepath): - if isinstance(runner, EpochBasedRunner): - epoch = runner.epoch - runner._max_epochs = epoch # Force runner to stop by pretending it has reached it's max_epoch - runner.should_stop = True # Set this flag to true to stop the current training epoch - os.remove(stop_filepath) - - def after_train_iter(self, runner: BaseRunner): - if not self.every_n_iters(runner, self.interval): - return - self._check_for_stop_signal(runner) - - -@HOOKS.register_module() -class FixedMomentumUpdaterHook(Hook): - def __init__(self): - """ - This hook does nothing, as the momentum is fixed by default. The hook is here to streamline switching between - different LR schedules. - """ - pass - - def before_run(self, runner): - pass - - -@HOOKS.register_module() -class EnsureCorrectBestCheckpointHook(Hook): - def __init__(self): - """ - This hook makes sure that the 'best_mAP' checkpoint points properly to the best model, even if the best model is - created in the last epoch. - """ - pass - - def after_run(self, runner): - runner.call_hook('after_train_epoch') - - -@HOOKS.register_module() -class OTELoggerHook(LoggerHook): - - class Curve: - def __init__(self): - self.x = [] - self.y = [] - - def __repr__(self): - points = [] - for x, y in zip(self.x, self.y): - points.append(f'({x},{y})') - return 'curve[' + ','.join(points) + ']' - - def __init__(self, - curves=None, - interval=10, - ignore_last=True, - reset_flag=True, - by_epoch=True): - super().__init__(interval, ignore_last, reset_flag, by_epoch) - self.curves = curves if curves is not None else defaultdict(self.Curve) - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner, allow_text=False) - if runner.max_epochs is not None: - normalized_iter = self.get_iter(runner) / runner.max_iters * runner.max_epochs - else: - normalized_iter = self.get_iter(runner) - for tag, value in tags.items(): - curve = self.curves[tag] - # Remove duplicates. - if len(curve.x) > 0 and curve.x[-1] == normalized_iter: - curve.x.pop() - curve.y.pop() - curve.x.append(normalized_iter) - curve.y.append(value) - - def after_train_epoch(self, runner): - # Iteration counter is increased right after the last iteration in the epoch, - # temporarily decrease it back. - runner._iter -= 1 - super().after_train_epoch(runner) - runner._iter += 1 - - -@HOOKS.register_module() -class OTEProgressHook(Hook): - def __init__(self, time_monitor, verbose=False): - super().__init__() - self.time_monitor = time_monitor - self.verbose = verbose - self.print_threshold = 1 - - def before_run(self, runner): - total_epochs = runner.max_epochs if runner.max_epochs is not None else 1 - self.time_monitor.total_epochs = total_epochs - self.time_monitor.train_steps = runner.max_iters // total_epochs if total_epochs else 1 - self.time_monitor.steps_per_epoch = self.time_monitor.train_steps + self.time_monitor.val_steps - self.time_monitor.total_steps = max(math.ceil(self.time_monitor.steps_per_epoch * total_epochs), 1) - self.time_monitor.current_step = 0 - self.time_monitor.current_epoch = 0 - self.time_monitor.on_train_begin() - - def before_epoch(self, runner): - self.time_monitor.on_epoch_begin(runner.epoch) - - def after_epoch(self, runner): - self.time_monitor.on_epoch_end(runner.epoch, runner.log_buffer.output) - - def before_iter(self, runner): - self.time_monitor.on_train_batch_begin(1) - - def after_iter(self, runner): - self.time_monitor.on_train_batch_end(1) - if self.verbose: - progress = self.progress - if progress >= self.print_threshold: - logger.warning(f'training progress {progress:.0f}%') - self.print_threshold = (progress + 10) // 10 * 10 - - def before_val_iter(self, runner): - self.time_monitor.on_test_batch_begin(1) - - def after_val_iter(self, runner): - self.time_monitor.on_test_batch_end(1) - - def after_run(self, runner): - self.time_monitor.on_train_end(1) - self.time_monitor.update_progress_callback(int(self.time_monitor.get_progress())) - - @property - def progress(self): - return self.time_monitor.get_progress() - - -@HOOKS.register_module() -class EarlyStoppingHook(Hook): - """ - Cancel training when a metric has stopped improving. - - Early Stopping hook monitors a metric quantity and if no improvement is seen for a ‘patience’ - number of epochs, the training is cancelled. - - :param interval: the number of intervals for checking early stop. The interval number should be - the same as the evaluation interval - the `interval` variable set in - `evaluation` config. - :param metric: the metric name to be monitored - :param rule: greater or less. In `less` mode, training will stop when the metric has stopped - decreasing and in `greater` mode it will stop when the metric has stopped - increasing. - :param patience: Number of epochs with no improvement after which the training will be reduced. - For example, if patience = 2, then we will ignore the first 2 epochs with no - improvement, and will only cancel the training after the 3rd epoch if the - metric still hasn’t improved then - :param iteration_patience: Number of iterations must be trained after the last improvement - before training stops. The same as patience but the training - continues if the number of iteration is lower than iteration_patience - This variable makes sure a model is trained enough for some - iterations after the last improvement before stopping. - :param min_delta: Minimal decay applied to lr. If the difference between new and old lr is - smaller than eps, the update is ignored - """ - rule_map = {'greater': lambda x, y: x > y, 'less': lambda x, y: x < y} - init_value_map = {'greater': -inf, 'less': inf} - greater_keys = [ - 'acc', 'top', 'AR@', 'auc', 'precision', 'mAP', 'mDice', 'mIoU', - 'mAcc', 'aAcc' - ] - less_keys = ['loss'] - - def __init__(self, - interval: int, - metric: str = 'bbox_mAP', - rule: str = None, - patience: int = 5, - iteration_patience: int = 500, - min_delta: float = 0.0): - super().__init__() - self.patience = patience - self.iteration_patience = iteration_patience - self.interval = interval - self.min_delta = min_delta - self._init_rule(rule, metric) - - self.min_delta *= 1 if self.rule == 'greater' else -1 - self.last_iter = 0 - self.wait_count = 0 - self.best_score = self.init_value_map[self.rule] - - def _init_rule(self, rule, key_indicator): - """Initialize rule, key_indicator, comparison_func, and best score. - - Here is the rule to determine which rule is used for key indicator - when the rule is not specific: - 1. If the key indicator is in ``self.greater_keys``, the rule will be - specified as 'greater'. - 2. Or if the key indicator is in ``self.less_keys``, the rule will be - specified as 'less'. - 3. Or if the key indicator is equal to the substring in any one item - in ``self.greater_keys``, the rule will be specified as 'greater'. - 4. Or if the key indicator is equal to the substring in any one item - in ``self.less_keys``, the rule will be specified as 'less'. - - Args: - rule (str | None): Comparison rule for best score. - key_indicator (str | None): Key indicator to determine the - comparison rule. - """ - if rule not in self.rule_map and rule is not None: - raise KeyError(f'rule must be greater, less or None, ' - f'but got {rule}.') - - if rule is None: - if key_indicator in self.greater_keys or any( - key in key_indicator for key in self.greater_keys): - rule = 'greater' - elif key_indicator in self.less_keys or any( - key in key_indicator for key in self.less_keys): - rule = 'less' - else: - raise ValueError(f'Cannot infer the rule for key ' - f'{key_indicator}, thus a specific rule ' - f'must be specified.') - self.rule = rule - self.key_indicator = key_indicator - self.compare_func = self.rule_map[self.rule] - - def before_run(self, runner): - self.by_epoch = False if runner.max_epochs is None else True - for hook in runner.hooks: - if isinstance(hook, LrUpdaterHook): - self.warmup_iters = hook.warmup_iters - break - - def after_train_iter(self, runner): - """Called after every training iter to evaluate the results.""" - if not self.by_epoch: - self._do_check_stopping(runner) - - def after_train_epoch(self, runner): - """Called after every training epoch to evaluate the results.""" - if self.by_epoch: - self._do_check_stopping(runner) - - def _do_check_stopping(self, runner): - if not self._should_check_stopping( - runner) or self.warmup_iters > runner.iter: - return - - if runner.rank == 0: - if self.key_indicator not in runner.log_buffer.output: - raise KeyError( - f'metric {self.key_indicator} does not exist in buffer. Please check ' - f'{self.key_indicator} is cached in evaluation output buffer' - ) - - key_score = runner.log_buffer.output[self.key_indicator] - if self.compare_func(key_score - self.min_delta, self.best_score): - self.best_score = key_score - self.wait_count = 0 - self.last_iter = runner.iter - else: - self.wait_count += 1 - if self.wait_count >= self.patience: - if runner.iter - self.last_iter < self.iteration_patience: - print_log( - f"\nSkip early stopping. Accumulated iteration " - f"{runner.iter - self.last_iter} from the last " - f"improvement must be larger than {self.iteration_patience} to trigger " - f"Early Stopping.", - logger=runner.logger) - return - stop_point = runner.epoch if self.by_epoch else runner.iter - print_log( - f"\nEarly Stopping at :{stop_point} with " - f"best {self.key_indicator}: {self.best_score}", - logger=runner.logger) - runner.should_stop = True - - def _should_check_stopping(self, runner): - check_time = self.every_n_epochs if self.by_epoch else self.every_n_iters - if not check_time(runner, self.interval): - # No evaluation during the interval. - return False - return True - - -@HOOKS.register_module() -class ReduceLROnPlateauLrUpdaterHook(LrUpdaterHook): - """ - Reduce learning rate when a metric has stopped improving. - - Models often benefit from reducing the learning rate by a factor of 2-10 once learning stagnates. - This scheduler reads a metrics quantity and if no improvement is seen for a ‘patience’ - number of epochs, the learning rate is reduced. - - :param min_lr: minimum learning rate. The lower bound of the desired learning rate. - :param interval: the number of intervals for checking the hook. The interval number should be - the same as the evaluation interval - the `interval` variable set in - `evaluation` config. - :param metric: the metric name to be monitored - :param rule: greater or less. In `less` mode, learning rate will be dropped if the metric has - stopped decreasing and in `greater` mode it will be dropped when the metric has - stopped increasing. - :param patience: Number of epochs with no improvement after which learning rate will be reduced. - For example, if patience = 2, then we will ignore the first 2 epochs with no - improvement, and will only drop LR after the 3rd epoch if the metric still - hasn’t improved then - :param iteration_patience: Number of iterations must be trained after the last improvement - before LR drops. The same as patience but the LR remains the same if - the number of iteration is lower than iteration_patience. This - variable makes sure a model is trained enough for some iterations - after the last improvement before dropping the LR. - :param factor: Factor to be multiply with the learning rate. - For example, new_lr = current_lr * factor - """ - rule_map = {'greater': lambda x, y: x > y, 'less': lambda x, y: x < y} - init_value_map = {'greater': -inf, 'less': inf} - greater_keys = [ - 'acc', 'top', 'AR@', 'auc', 'precision', 'mAP', 'mDice', 'mIoU', - 'mAcc', 'aAcc' - ] - less_keys = ['loss'] - - def __init__(self, - min_lr, - interval, - metric='bbox_mAP', - rule=None, - factor=0.1, - patience=3, - iteration_patience=300, - **kwargs): - super().__init__(**kwargs) - self.interval = interval - self.min_lr = min_lr - self.factor = factor - self.patience = patience - self.iteration_patience = iteration_patience - self.metric = metric - self.bad_count = 0 - self.last_iter = 0 - self.current_lr = None - self._init_rule(rule, metric) - self.best_score = self.init_value_map[self.rule] - - def _init_rule(self, rule, key_indicator): - """Initialize rule, key_indicator, comparison_func, and best score. - - Here is the rule to determine which rule is used for key indicator - when the rule is not specific: - 1. If the key indicator is in ``self.greater_keys``, the rule will be - specified as 'greater'. - 2. Or if the key indicator is in ``self.less_keys``, the rule will be - specified as 'less'. - 3. Or if the key indicator is equal to the substring in any one item - in ``self.greater_keys``, the rule will be specified as 'greater'. - 4. Or if the key indicator is equal to the substring in any one item - in ``self.less_keys``, the rule will be specified as 'less'. - - Args: - rule (str | None): Comparison rule for best score. - key_indicator (str | None): Key indicator to determine the - comparison rule. - """ - if rule not in self.rule_map and rule is not None: - raise KeyError(f'rule must be greater, less or None, ' - f'but got {rule}.') - - if rule is None: - if key_indicator in self.greater_keys or any( - key in key_indicator for key in self.greater_keys): - rule = 'greater' - elif key_indicator in self.less_keys or any( - key in key_indicator for key in self.less_keys): - rule = 'less' - else: - raise ValueError(f'Cannot infer the rule for key ' - f'{key_indicator}, thus a specific rule ' - f'must be specified.') - self.rule = rule - self.key_indicator = key_indicator - self.compare_func = self.rule_map[self.rule] - - def _should_check_stopping(self, runner): - check_time = self.every_n_epochs if self.by_epoch else self.every_n_iters - if not check_time(runner, self.interval): - # No evaluation during the interval. - return False - return True - - def get_lr(self, runner, base_lr): - if not self._should_check_stopping( - runner) or self.warmup_iters > runner.iter: - return base_lr - - if self.current_lr is None: - self.current_lr = base_lr - - if hasattr(runner, self.metric): - score = getattr(runner, self.metric, 0.0) - else: - return self.current_lr - - print_log( - f"\nBest Score: {self.best_score}, Current Score: {score}, Patience: {self.patience} " - f"Count: {self.bad_count}", - logger=runner.logger) - if self.compare_func(score, self.best_score): - self.best_score = score - self.bad_count = 0 - self.last_iter = runner.iter - else: - self.bad_count += 1 - - if self.bad_count >= self.patience: - if runner.iter - self.last_iter < self.iteration_patience: - print_log( - f"\nSkip LR dropping. Accumulated iteration " - f"{runner.iter - self.last_iter} from the last " - f"improvement must be larger than {self.iteration_patience} to trigger " - f"LR dropping.", - logger=runner.logger) - return self.current_lr - self.last_iter = runner.iter - self.bad_count = 0 - print_log( - f"\nDrop LR from: {self.current_lr}, to: " - f"{max(self.current_lr * self.factor, self.min_lr)}", - logger=runner.logger) - self.current_lr = max(self.current_lr * self.factor, self.min_lr) - return self.current_lr - - def before_run(self, runner): - # TODO: remove overloaded method after fixing the issue - # https://github.com/open-mmlab/mmdetection/issues/6572 - for group in runner.optimizer.param_groups: - group.setdefault('initial_lr', group['lr']) - self.base_lr = [ - group['lr'] for group in runner.optimizer.param_groups - ] - self.bad_count = 0 - self.last_iter = 0 - self.current_lr = None - self.best_score = self.init_value_map[self.rule] - - -@HOOKS.register_module() -class StopLossNanTrainingHook(Hook): - - def after_train_iter(self, runner): - if isnan(runner.outputs['loss'].item()): - logger.warning(f"Early Stopping since loss is NaN") - runner.should_stop = True diff --git a/external/mmdetection/build/lib/detection_tasks/extension/utils/pipelines.py b/external/mmdetection/build/lib/detection_tasks/extension/utils/pipelines.py deleted file mode 100644 index 82629e49973..00000000000 --- a/external/mmdetection/build/lib/detection_tasks/extension/utils/pipelines.py +++ /dev/null @@ -1,120 +0,0 @@ -# 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 numpy as np - -from mmdet.datasets.builder import PIPELINES - -from ..datasets import get_annotation_mmdet_format - - -@PIPELINES.register_module() -class LoadImageFromOTEDataset: - """ - Pipeline element that loads an image from a OTE Dataset on the fly. Can do conversion to float 32 if needed. - - Expected entries in the 'results' dict that should be passed to this pipeline element are: - results['dataset_item']: dataset_item from which to load the image - results['dataset_id']: id of the dataset to which the item belongs - results['index']: index of the item in the dataset - - :param to_float32: optional bool, True to convert images to fp32. defaults to False - """ - - def __init__(self, to_float32: bool = False): - self.to_float32 = to_float32 - - def __call__(self, results): - dataset_item = results['dataset_item'] - img = dataset_item.numpy - shape = img.shape - - assert img.shape[0] == results['height'], f"{img.shape[0]} != {results['height']}" - assert img.shape[1] == results['width'], f"{img.shape[1]} != {results['width']}" - - filename = f"Dataset item index {results['index']}" - results['filename'] = filename - results['ori_filename'] = filename - results['img'] = img - results['img_shape'] = shape - results['ori_shape'] = shape - # Set initial values for default meta_keys - results['pad_shape'] = shape - num_channels = 1 if len(shape) < 3 else shape[2] - results['img_norm_cfg'] = dict( - mean=np.zeros(num_channels, dtype=np.float32), - std=np.ones(num_channels, dtype=np.float32), - to_rgb=False) - results['img_fields'] = ['img'] - - if self.to_float32: - results['img'] = results['img'].astype(np.float32) - - return results - - -@PIPELINES.register_module() -class LoadAnnotationFromOTEDataset: - """ - Pipeline element that loads an annotation from a OTE Dataset on the fly. - - Expected entries in the 'results' dict that should be passed to this pipeline element are: - results['dataset_item']: dataset_item from which to load the annotation - results['ann_info']['label_list']: list of all labels in the project - - """ - - def __init__(self, min_size : int, with_bbox: bool = True, with_label: bool = True, with_mask: bool = False, with_seg: bool = False, - poly2mask: bool = True, with_text: bool = False, domain=None): - self.with_bbox = with_bbox - self.with_label = with_label - self.with_mask = with_mask - self.with_seg = with_seg - self.poly2mask = poly2mask - self.with_text = with_text - self.domain = domain - self.min_size = min_size - - @staticmethod - def _load_bboxes(results, ann_info): - results['bbox_fields'].append('gt_bboxes') - results['gt_bboxes'] = copy.deepcopy(ann_info['bboxes']) - return results - - @staticmethod - def _load_labels(results, ann_info): - results['gt_labels'] = copy.deepcopy(ann_info['labels']) - return results - - @staticmethod - def _load_masks(results, ann_info): - results['mask_fields'].append('gt_masks') - results['gt_masks'] = copy.deepcopy(ann_info['masks']) - return results - - def __call__(self, results): - dataset_item = results['dataset_item'] - label_list = results['ann_info']['label_list'] - ann_info = get_annotation_mmdet_format(dataset_item, label_list, self.domain, self.min_size) - if self.with_bbox: - results = self._load_bboxes(results, ann_info) - if results is None or len(results['gt_bboxes']) == 0: - return None - if self.with_label: - results = self._load_labels(results, ann_info) - if self.with_mask: - results = self._load_masks(results, ann_info) - return results diff --git a/external/mmdetection/build/lib/detection_tasks/extension/utils/runner.py b/external/mmdetection/build/lib/detection_tasks/extension/utils/runner.py deleted file mode 100644 index 94889b2d1d2..00000000000 --- a/external/mmdetection/build/lib/detection_tasks/extension/utils/runner.py +++ /dev/null @@ -1,127 +0,0 @@ -# Copyright (c) 2018-2021 OpenMMLab -# SPDX-License-Identifier: Apache-2.0 -# -# Copyright (C) 2020-2022 Intel Corporation -# SPDX-License-Identifier: Apache-2.0 -# - -# Is based on -# * https://github.com/open-mmlab/mmcv/blob/master/mmcv/runner/epoch_based_runner.py -# * https://github.com/open-mmlab/mmcv/blob/master/mmcv/runner/iter_based_runner.py - -import time -import warnings - -import mmcv -import torch.distributed as dist -from mmcv.runner.utils import get_host_info -from mmcv.runner import RUNNERS, EpochBasedRunner, IterBasedRunner, IterLoader, get_dist_info - - -@RUNNERS.register_module() -class EpochRunnerWithCancel(EpochBasedRunner): - """ - Simple modification to EpochBasedRunner to allow cancelling the training during an epoch. - A stopping hook should set the runner.should_stop flag to True if stopping is required. - """ - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.should_stop = False - _, world_size = get_dist_info() - self.distributed = True if world_size > 1 else False - - def stop(self) -> bool: - """ Returning a boolean to break the training loop - This method supports distributed training by broadcasting should_stop to other ranks - :return: a cancellation bool - """ - broadcast_obj = [False] - if self.rank == 0 and self.should_stop: - broadcast_obj = [True] - - if self.distributed: - dist.broadcast_object_list(broadcast_obj, src=0) - if broadcast_obj[0]: - self._max_epochs = self.epoch - return broadcast_obj[0] - - def train(self, data_loader, **kwargs): - self.model.train() - self.mode = 'train' - self.data_loader = data_loader - self._max_iters = self._max_epochs * len(self.data_loader) - self.call_hook('before_train_epoch') - time.sleep(2) # Prevent possible deadlock during epoch transition - for i, data_batch in enumerate(self.data_loader): - self._inner_iter = i - self.call_hook('before_train_iter') - self.run_iter(data_batch, train_mode=True, **kwargs) - self.call_hook('after_train_iter') - if self.stop(): - break - self._iter += 1 - self.call_hook('after_train_epoch') - self.stop() - self._epoch += 1 - - -@RUNNERS.register_module() -class IterBasedRunnerWithCancel(IterBasedRunner): - """ - Simple modification to IterBasedRunner to allow cancelling the training. The cancel training hook - should set the runner.should_stop flag to True if stopping is required. - - # TODO: Implement cancelling of training via keyboard interrupt signal, instead of should_stop - """ - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.should_stop = False - - def main_loop(self, workflow, iter_loaders, **kwargs): - while self.iter < self._max_iters: - for i, flow in enumerate(workflow): - self._inner_iter = 0 - mode, iters = flow - if not isinstance(mode, str) or not hasattr(self, mode): - raise ValueError( - 'runner has no method named "{}" to run a workflow'. - format(mode)) - iter_runner = getattr(self, mode) - for _ in range(iters): - if mode == 'train' and self.iter >= self._max_iters: - break - iter_runner(iter_loaders[i], **kwargs) - if self.should_stop: - return - - def run(self, data_loaders, workflow, max_iters=None, **kwargs): - assert isinstance(data_loaders, list) - assert mmcv.is_list_of(workflow, tuple) - assert len(data_loaders) == len(workflow) - if max_iters is not None: - warnings.warn( - 'setting max_iters in run is deprecated, ' - 'please set max_iters in runner_config', DeprecationWarning) - self._max_iters = max_iters - assert self._max_iters is not None, ( - 'max_iters must be specified during instantiation') - - work_dir = self.work_dir if self.work_dir is not None else 'NONE' - self.logger.info('Start running, host: %s, work_dir: %s', - get_host_info(), work_dir) - self.logger.info('workflow: %s, max: %d iters', workflow, - self._max_iters) - self.call_hook('before_run') - - iter_loaders = [IterLoader(x) for x in data_loaders] - - self.call_hook('before_epoch') - - self.should_stop = False - self.main_loop(workflow, iter_loaders, **kwargs) - self.should_stop = False - - time.sleep(1) # wait for some hooks like loggers to finish - self.call_hook('after_epoch') - self.call_hook('after_run') diff --git a/external/mmdetection/dist/detection_tasks-0.0.0-py3.8.egg b/external/mmdetection/dist/detection_tasks-0.0.0-py3.8.egg deleted file mode 100644 index fb1e0ec450d..00000000000 Binary files a/external/mmdetection/dist/detection_tasks-0.0.0-py3.8.egg and /dev/null differ