Skip to content

Commit cb94427

Browse files
author
Songki Choi
authored
Fix auto input size mismatch in eval & export (#2530)
* Fix auto input size mismatch in eval & export * Re-enable E2E tests for Issue#2518 * Add input size check in export testing * Format float numbers in log * Fix NNCF export shape mismatch * Fix saliency map issue * Disable auto input size if tiling enabled --------- Signed-off-by: Songki Choi <[email protected]>
1 parent aec9f55 commit cb94427

File tree

25 files changed

+176
-259
lines changed

25 files changed

+176
-259
lines changed

src/otx/algorithms/classification/adapters/mmcls/configurer.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
"""Base configurer for mmdet config."""
2+
23
# Copyright (C) 2023 Intel Corporation
34
# SPDX-License-Identifier: Apache-2.0
4-
#
55

6-
from typing import Optional
6+
from typing import Optional, Tuple
77

88
import torch
99
from mmcv import build_from_cfg
@@ -22,7 +22,6 @@
2222
recursively_update_cfg,
2323
update_or_add_custom_hook,
2424
)
25-
from otx.algorithms.common.configs.configuration_enums import InputSizePreset
2625
from otx.algorithms.common.utils.logger import get_logger
2726

2827
logger = get_logger()
@@ -162,16 +161,19 @@ def configure_topk(cfg):
162161

163162
@staticmethod
164163
def configure_input_size(
165-
cfg, input_size_config: InputSizePreset = InputSizePreset.DEFAULT, model_ckpt_path: Optional[str] = None
164+
cfg, input_size=Optional[Tuple[int, int]], model_ckpt_path: Optional[str] = None, training=True
166165
):
167166
"""Change input size if necessary."""
168-
manager = InputSizeManager(cfg)
169-
input_size = manager.get_configured_input_size(input_size_config, model_ckpt_path)
170167
if input_size is None: # InputSizePreset.DEFAULT
171168
return
172169

170+
manager = InputSizeManager(cfg)
171+
173172
if input_size == (0, 0): # InputSizePreset.AUTO
174-
input_size = BaseConfigurer.adapt_input_size_to_dataset(cfg, manager)
173+
if training:
174+
input_size = BaseConfigurer.adapt_input_size_to_dataset(cfg, manager, use_annotations=False)
175+
else:
176+
input_size = manager.get_trained_input_size(model_ckpt_path)
175177
if input_size is None:
176178
return
177179

src/otx/algorithms/classification/adapters/mmcls/nncf/task.py

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,7 @@
11
"""NNCF Task for OTX Classification."""
22

3-
# Copyright (C) 2022 Intel Corporation
4-
#
5-
# Licensed under the Apache License, Version 2.0 (the 'License');
6-
# you may not use this file except in compliance with the License.
7-
# You may obtain a copy of the License at
8-
#
9-
# http://www.apache.org/licenses/LICENSE-2.0
10-
#
11-
# Unless required by applicable law or agreed to in writing,
12-
# software distributed under the License is distributed on an 'AS IS' BASIS,
13-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14-
# See the License for the specific language governing permissions
15-
# and limitations under the License.
3+
# Copyright (C) 2022-2023 Intel Corporation
4+
# SPDX-License-Identifier: Apache-2.0
165

176
from functools import partial
187
from typing import List, Optional
@@ -121,3 +110,6 @@ def _generate_training_metrics_group(self, learning_curves):
121110
output.append(LineMetricsGroup(metrics=[metric_curve], visualization_info=visualization_info))
122111

123112
return output, best_acc
113+
114+
def _save_model_post_hook(self, modelinfo):
115+
modelinfo["input_size"] = self._input_size

src/otx/algorithms/classification/adapters/mmcls/task.py

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,7 @@
11
"""Task of OTX Classification using mmclassification training backend."""
22

33
# Copyright (C) 2023 Intel Corporation
4-
#
5-
# Licensed under the Apache License, Version 2.0 (the "License");
6-
# you may not use this file except in compliance with the License.
7-
# You may obtain a copy of the License at
8-
#
9-
# http://www.apache.org/licenses/LICENSE-2.0
10-
#
11-
# Unless required by applicable law or agreed to in writing,
12-
# software distributed under the License is distributed on an "AS IS" BASIS,
13-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14-
# See the License for the specific language governing permissions
15-
# and limitations under the License.
4+
# SPDX-License-Identifier: Apache-2.0
165

176
import glob
187
import os
@@ -194,11 +183,12 @@ def configure(
194183
ir_options,
195184
data_classes,
196185
model_classes,
197-
self._hyperparams.learning_parameters.input_size,
186+
self._input_size,
198187
options_for_patch_datasets=options_for_patch_datasets,
199188
options_for_patch_evaluation=options_for_patch_evaluation,
200189
)
201190
self._config = cfg
191+
self._input_size = cfg.model.pop("input_size", None)
202192
return cfg
203193

204194
def build_model(

src/otx/algorithms/classification/task.py

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,7 @@
11
"""Task of OTX Classification."""
22

33
# Copyright (C) 2023 Intel Corporation
4-
#
5-
# Licensed under the Apache License, Version 2.0 (the "License");
6-
# you may not use this file except in compliance with the License.
7-
# You may obtain a copy of the License at
8-
#
9-
# http://www.apache.org/licenses/LICENSE-2.0
10-
#
11-
# Unless required by applicable law or agreed to in writing,
12-
# software distributed under the License is distributed on an "AS IS" BASIS,
13-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14-
# See the License for the specific language governing permissions
15-
# and limitations under the License.
4+
# SPDX-License-Identifier: Apache-2.0
165

176
import io
187
import json
@@ -34,6 +23,7 @@
3423
get_multihead_class_info as get_hierarchical_info,
3524
)
3625
from otx.algorithms.common.configs import TrainType
26+
from otx.algorithms.common.configs.configuration_enums import InputSizePreset
3727
from otx.algorithms.common.tasks.base_task import TRAIN_TYPE_DIR_PATH, OTXTask
3828
from otx.algorithms.common.utils import embed_ir_model_data
3929
from otx.algorithms.common.utils.callback import TrainingProgressCallback
@@ -130,6 +120,12 @@ def __init__(self, task_environment: TaskEnvironment, output_path: Optional[str]
130120
if self._task_environment.model is not None:
131121
self._load_model()
132122

123+
if hasattr(self._hyperparams.learning_parameters, "input_size"):
124+
input_size_cfg = InputSizePreset(self._hyperparams.learning_parameters.input_size.value)
125+
else:
126+
input_size_cfg = InputSizePreset.DEFAULT
127+
self._input_size = input_size_cfg.tuple
128+
133129
def _is_multi_label(self, label_groups: List[LabelGroup], all_labels: List[LabelEntity]):
134130
"""Check whether the current training mode is multi-label or not."""
135131
# NOTE: In the current Geti, multi-label should have `___` symbol for all group names.
@@ -479,6 +475,7 @@ def save_model(self, output_model: ModelEntity):
479475
"model": model_ckpt,
480476
"config": hyperparams_str,
481477
"labels": labels,
478+
"input_size": self._input_size,
482479
"VERSION": 1,
483480
}
484481

src/otx/algorithms/common/adapters/mmcv/configurer.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
recursively_update_cfg,
2727
update_or_add_custom_hook,
2828
)
29-
from otx.algorithms.common.configs.configuration_enums import InputSizePreset
3029
from otx.algorithms.common.tasks.base_task import OnHookInitialized
3130
from otx.algorithms.common.utils import UncopiableDefaultDict, append_dist_rank_suffix
3231
from otx.algorithms.common.utils.data import compute_robust_dataset_statistics
@@ -74,7 +73,7 @@ def configure(
7473
ir_options: Optional[Config] = None,
7574
data_classes: Optional[List[str]] = None,
7675
model_classes: Optional[List[str]] = None,
77-
input_size: InputSizePreset = InputSizePreset.DEFAULT,
76+
input_size: Optional[Tuple[int, int]] = None,
7877
**kwargs: Dict[Any, Any],
7978
) -> Config:
8079
"""Create MMCV-consumable config from given inputs."""
@@ -228,7 +227,7 @@ def configure_data_pipeline(self, cfg, input_size, model_ckpt_path, **kwargs):
228227
"""Configuration data pipeline settings."""
229228

230229
patch_color_conversion(cfg)
231-
self.configure_input_size(cfg, input_size, model_ckpt_path)
230+
self.configure_input_size(cfg, input_size, model_ckpt_path, self.training)
232231

233232
def configure_recipe(self, cfg, **kwargs):
234233
"""Configuration training recipe settings."""
@@ -533,7 +532,15 @@ def adapt_input_size_to_dataset(
533532
stat = compute_robust_dataset_statistics(dataset, use_annotations)
534533
if not stat:
535534
return None
536-
logger.info(f"Dataset stat: {json.dumps(stat, indent=4)}")
535+
536+
def format_float(obj):
537+
if isinstance(obj, float):
538+
return f"{obj:.2f}"
539+
if isinstance(obj, dict):
540+
return {k: format_float(v) for k, v in obj.items()}
541+
return obj
542+
543+
logger.info(f"Dataset stat: {json.dumps(format_float(stat), indent=4)}")
537544

538545
# Fit to typical large image size (conservative)
539546
# -> "avg" size might be preferrable for efficiency

src/otx/algorithms/common/adapters/mmcv/utils/config_utils.py

Lines changed: 15 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -682,12 +682,12 @@ def set_input_size(self, input_size: Union[int, List[int], Tuple[int, int]]):
682682
self._set_pipeline_size_value(pipelines, resize_ratio)
683683

684684
# Set model size
685-
# - needed only for YOLOX
686685
model_cfg = self._config.get("model", {})
686+
model_cfg["input_size"] = input_size
687687
if model_cfg.get("type", "") == "CustomYOLOX":
688+
# - needed only for YOLOX
688689
if input_size[0] % 32 != 0 or input_size[1] % 32 != 0:
689690
raise ValueError("YOLOX should have input size being multiple of 32.")
690-
model_cfg["input_size"] = input_size
691691

692692
@property
693693
def base_input_size(self) -> Union[Tuple[int, int], Dict[str, Tuple[int, int]]]:
@@ -862,38 +862,28 @@ def _set_size_value(pipeline: Dict, attr: str, scale: Tuple[Union[int, float], U
862862
pipeline[attr] = (round(pipeline[attr][0] * scale[0]), round(pipeline[attr][1] * scale[1]))
863863

864864
@staticmethod
865-
def get_configured_input_size(
866-
input_size_config: InputSizePreset = InputSizePreset.DEFAULT, model_ckpt: Optional[str] = None
867-
) -> Optional[Tuple[int, int]]:
868-
"""Get configurable input size configuration. If it doesn't exist, return None.
865+
def get_trained_input_size(model_ckpt: Optional[str] = None) -> Optional[Tuple[int, int]]:
866+
"""Get trained input size from checkpoint. If it doesn't exist, return None.
869867
870868
Args:
871-
input_size_config (InputSizePreset, optional): Input size setting. Defaults to InputSizePreset.DEFAULT.
872869
model_ckpt (Optional[str], optional): Model weight to load. Defaults to None.
873870
874871
Returns:
875872
Optional[Tuple[int, int]]: Pair of width and height. If there is no input size configuration, return None.
876873
"""
877-
input_size = None
878-
if input_size_config == InputSizePreset.DEFAULT:
879-
if model_ckpt is None:
880-
return None
881-
882-
model_info = torch.load(model_ckpt, map_location="cpu")
883-
for key in ["config", "learning_parameters", "input_size", "value"]:
884-
if key not in model_info:
885-
return None
886-
model_info = model_info[key]
887-
input_size = model_info
888-
889-
if input_size == InputSizePreset.DEFAULT.value:
890-
return None
891-
logger.info("Given model weight was trained with {} input size.".format(input_size))
874+
if model_ckpt is None:
875+
return None
892876

893-
else:
894-
input_size = input_size_config.value
877+
model_info = torch.load(model_ckpt, map_location="cpu")
878+
if model_info is None:
879+
return None
895880

896-
return InputSizePreset.parse(input_size)
881+
input_size = model_info.get("input_size", None)
882+
if not input_size:
883+
return None
884+
885+
logger.info("Given model weight was trained with {} input size.".format(input_size))
886+
return input_size
897887

898888
@staticmethod
899889
def select_closest_size(input_size: Tuple[int, int], preset_sizes: List[Tuple[int, int]]):

src/otx/algorithms/common/utils/utils.py

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,7 @@
11
"""Collections of Utils for common OTX algorithms."""
22

3-
# Copyright (C) 2022 Intel Corporation
4-
#
5-
# Licensed under the Apache License, Version 2.0 (the "License");
6-
# you may not use this file except in compliance with the License.
7-
# You may obtain a copy of the License at
8-
#
9-
# http://www.apache.org/licenses/LICENSE-2.0
10-
#
11-
# Unless required by applicable law or agreed to in writing,
12-
# software distributed under the License is distributed on an "AS IS" BASIS,
13-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14-
# See the License for the specific language governing permissions
15-
# and limitations under the License.
3+
# Copyright (C) 2022-2023 Intel Corporation
4+
# SPDX-License-Identifier: Apache-2.0
165

176
import importlib
187
import inspect
@@ -98,7 +87,7 @@ def get_arg_spec( # noqa: C901 # pylint: disable=too-many-branches
9887
return tuple(args)
9988

10089

101-
def set_random_seed(seed, logger, deterministic=False):
90+
def set_random_seed(seed, logger=None, deterministic=False):
10291
"""Set random seed.
10392
10493
Args:
@@ -116,7 +105,8 @@ def set_random_seed(seed, logger, deterministic=False):
116105
torch.manual_seed(seed)
117106
torch.cuda.manual_seed_all(seed)
118107
os.environ["PYTHONHASHSEED"] = str(seed)
119-
logger.info(f"Training seed was set to {seed} w/ deterministic={deterministic}.")
108+
if logger:
109+
logger.info(f"Training seed was set to {seed} w/ deterministic={deterministic}.")
120110
if deterministic:
121111
torch.backends.cudnn.deterministic = True
122112
torch.backends.cudnn.benchmark = False

src/otx/algorithms/detection/adapters/mmdet/configurer.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
"""Base configurer for mmdet config."""
2+
23
# Copyright (C) 2023 Intel Corporation
34
# SPDX-License-Identifier: Apache-2.0
4-
#
55

6-
from typing import Optional
6+
from typing import Optional, Tuple
77

88
from mmcv.utils import ConfigDict
99

@@ -13,7 +13,6 @@
1313
from otx.algorithms.common.adapters.mmcv.utils.config_utils import (
1414
InputSizeManager,
1515
)
16-
from otx.algorithms.common.configs.configuration_enums import InputSizePreset
1716
from otx.algorithms.common.utils.logger import get_logger
1817
from otx.algorithms.detection.adapters.mmdet.utils import (
1918
cluster_anchors,
@@ -154,9 +153,12 @@ def configure_bbox_head(self, cfg):
154153

155154
@staticmethod
156155
def configure_input_size(
157-
cfg, input_size_config: InputSizePreset = InputSizePreset.DEFAULT, model_ckpt_path: Optional[str] = None
156+
cfg, input_size=Optional[Tuple[int, int]], model_ckpt_path: Optional[str] = None, training=True
158157
):
159158
"""Change input size if necessary."""
159+
if input_size is None: # InputSizePreset.DEFAULT
160+
return
161+
160162
# YOLOX tiny has a different input size in train and val data pipeline
161163
base_input_size = None
162164
model_cfg = cfg.get("model")
@@ -168,15 +170,13 @@ def configure_input_size(
168170
"test": (416, 416),
169171
"unlabeled": (992, 736),
170172
}
171-
172173
manager = InputSizeManager(cfg, base_input_size)
173174

174-
input_size = manager.get_configured_input_size(input_size_config, model_ckpt_path)
175-
if input_size is None: # InputSizePreset.DEFAULT
176-
return
177-
178175
if input_size == (0, 0): # InputSizePreset.AUTO
179-
input_size = BaseConfigurer.adapt_input_size_to_dataset(cfg, manager, use_annotations=True)
176+
if training:
177+
input_size = BaseConfigurer.adapt_input_size_to_dataset(cfg, manager, use_annotations=True)
178+
else:
179+
input_size = manager.get_trained_input_size(model_ckpt_path)
180180
if input_size is None:
181181
return
182182

src/otx/algorithms/detection/adapters/mmdet/nncf/task.py

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,7 @@
11
"""NNCF Task of OTX Detection."""
22

3-
# Copyright (C) 2022 Intel Corporation
4-
#
5-
# Licensed under the Apache License, Version 2.0 (the "License");
6-
# you may not use this file except in compliance with the License.
7-
# You may obtain a copy of the License at
8-
#
9-
# http://www.apache.org/licenses/LICENSE-2.0
10-
#
11-
# Unless required by applicable law or agreed to in writing,
12-
# software distributed under the License is distributed on an "AS IS" BASIS,
13-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14-
# See the License for the specific language governing permissions
15-
# and limitations under the License.
16-
3+
# Copyright (C) 2022-2023 Intel Corporation
4+
# SPDX-License-Identifier: Apache-2.0
175

186
from functools import partial
197
from typing import Optional
@@ -124,3 +112,4 @@ def _save_model_post_hook(self, modelinfo):
124112
self._update_anchors(modelinfo["anchors"], self.config.model.bbox_head.anchor_generator)
125113

126114
modelinfo["confidence_threshold"] = self.confidence_threshold
115+
modelinfo["input_size"] = self._input_size

0 commit comments

Comments
 (0)