Skip to content

Commit

Permalink
added unit tests, integration and e2e
Browse files Browse the repository at this point in the history
  • Loading branch information
kprokofi committed May 22, 2023
1 parent 445a1e4 commit 7baae35
Show file tree
Hide file tree
Showing 11 changed files with 158 additions and 40 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class MeanTeacherSegmentor(BaseSegmentor):
def __init__(
self,
orig_type,
num_iters_per_epoch,
num_iters_per_epoch=None,
unsup_weight=0.1,
proto_weight=0.7,
drop_unrel_pixels_percent=20,
Expand All @@ -47,13 +47,16 @@ def __init__(
super().__init__()
self.test_cfg = kwargs["test_cfg"]
self.count_iter = 0
# filter unreliable pixels during first 100 epochs
self.filter_pixels_iters = num_iters_per_epoch * 100
self.semisl_start_iter = num_iters_per_epoch * semisl_start_epoch
# num_iters_per_epoch will be None during validation
# Overwise it should be overwritten in train_task
if num_iters_per_epoch is not None:
# filter unreliable pixels during first 100 epochs
self.filter_pixels_iters = num_iters_per_epoch * 100
self.semisl_start_iter = num_iters_per_epoch * semisl_start_epoch
self.drop_unrel_pixels_percent = drop_unrel_pixels_percent
cfg = kwargs.copy()
cfg["type"] = orig_type
self.align_corners = cfg["decode_head"].align_corners
self.align_corners = cfg["decode_head"].get("align_corners", False)
self.model_s = build_segmentor(cfg)
self.model_t = build_segmentor(cfg)
self.use_prototype_head = False
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,7 @@
"num_bn_adaptation_samples": 500
}
},
"ignored_scopes": [
"{re}.*cross_resolution_weighting.*__mul__.*",
"{re}.*spatial_weighting.*__mul__.*"
]
"ignored_scopes": []
}
],
"accuracy_aware_training": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ name: SegNext-B
task_type: SEGMENTATION
task_family: VISION
instantiation: "CLASS"
summary: Class-Incremental Semantic Segmentation with large architecture which based on the MSCAN backbone for the better accuracy.
summary: Class-Incremental Semantic Segmentation with larger architecture which based on the MSCAN backbone for the better accuracy.
application: ~

# Algo backend.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,7 @@
"num_bn_adaptation_samples": 500
}
},
"ignored_scopes": [
"{re}.*cross_resolution_weighting.*__mul__.*",
"{re}.*spatial_weighting.*__mul__.*"
]
"ignored_scopes": []
}
],
"accuracy_aware_training": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,7 @@
"num_bn_adaptation_samples": 500
}
},
"ignored_scopes": [
"{re}.*cross_resolution_weighting.*__mul__.*",
"{re}.*spatial_weighting.*__mul__.*"
]
"ignored_scopes": []
}
],
"accuracy_aware_training": {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Description.
model_template_id: Custom_Semantic_Segmentation_SegNext_s
name: SegNext-s
model_template_id: Custom_Semantic_Segmentation_SegNext_t
name: SegNext-t
task_type: SEGMENTATION
task_family: VISION
instantiation: "CLASS"
Expand Down
46 changes: 28 additions & 18 deletions tests/e2e/cli/semantic_segmentation/test_segmentation.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,21 @@
else:
templates = Registry("otx/algorithms/segmentation").filter(task_type="SEGMENTATION").templates
templates_ids = [template.model_template_id for template in templates]
# add one experimental template for new segmentation model. In the future we will update them as main templates
# but we need to start to test them now. For time saving - one new model will be validated
# NNCF is not validated since the work in progress with optimization task
template_experimental = parse_model_template(
os.path.join("otx/algorithms/segmentation/configs", "ham_segnext_s", "template_experimental.yaml")
)
templates_inc_segnext = copy.deepcopy(templates)
templates_ids_inc_segnext = copy.deepcopy(templates_ids)
templates_inc_segnext.extend([template_experimental])
templates_ids_inc_segnext.extend([template_experimental.model_template_id])


class TestToolsMPASegmentation:
@e2e_pytest_component
@pytest.mark.parametrize("template", templates, ids=templates_ids)
@pytest.mark.parametrize("template", templates_inc_segnext, ids=templates_ids_inc_segnext)
def test_otx_train(self, template, tmp_dir_path):
tmp_dir_path = tmp_dir_path / "segmentation"
otx_train_testing(template, tmp_dir_path, otx_dir, args)
Expand All @@ -91,7 +101,7 @@ def test_otx_train(self, template, tmp_dir_path):

@e2e_pytest_component
@pytest.mark.skipif(TT_STABILITY_TESTS, reason="This is TT_STABILITY_TESTS")
@pytest.mark.parametrize("template", templates, ids=templates_ids)
@pytest.mark.parametrize("template", templates_inc_segnext, ids=templates_ids_inc_segnext)
def test_otx_resume(self, template, tmp_dir_path):
tmp_dir_path = tmp_dir_path / "segmentation/test_resume"
otx_resume_testing(template, tmp_dir_path, otx_dir, args)
Expand All @@ -105,72 +115,72 @@ def test_otx_resume(self, template, tmp_dir_path):

@e2e_pytest_component
@pytest.mark.skipif(TT_STABILITY_TESTS, reason="This is TT_STABILITY_TESTS")
@pytest.mark.parametrize("template", templates, ids=templates_ids)
@pytest.mark.parametrize("template", templates_inc_segnext, ids=templates_ids_inc_segnext)
@pytest.mark.parametrize("dump_features", [True, False])
def test_otx_export(self, template, tmp_dir_path, dump_features):
tmp_dir_path = tmp_dir_path / "segmentation"
otx_export_testing(template, tmp_dir_path, dump_features)

@e2e_pytest_component
@pytest.mark.skipif(TT_STABILITY_TESTS, reason="This is TT_STABILITY_TESTS")
@pytest.mark.parametrize("template", templates, ids=templates_ids)
@pytest.mark.parametrize("template", templates_inc_segnext, ids=templates_ids_inc_segnext)
def test_otx_export_fp16(self, template, tmp_dir_path):
tmp_dir_path = tmp_dir_path / "segmentation"
otx_export_testing(template, tmp_dir_path, half_precision=True)

@e2e_pytest_component
@pytest.mark.skipif(TT_STABILITY_TESTS, reason="This is TT_STABILITY_TESTS")
@pytest.mark.parametrize("template", templates, ids=templates_ids)
@pytest.mark.parametrize("template", templates_inc_segnext, ids=templates_ids_inc_segnext)
def test_otx_eval(self, template, tmp_dir_path):
tmp_dir_path = tmp_dir_path / "segmentation"
otx_eval_testing(template, tmp_dir_path, otx_dir, args)

@e2e_pytest_component
@pytest.mark.skipif(TT_STABILITY_TESTS, reason="This is TT_STABILITY_TESTS")
@pytest.mark.parametrize("template", templates, ids=templates_ids)
@pytest.mark.parametrize("template", templates_inc_segnext, ids=templates_ids_inc_segnext)
@pytest.mark.parametrize("half_precision", [True, False])
def test_otx_eval_openvino(self, template, tmp_dir_path, half_precision):
tmp_dir_path = tmp_dir_path / "segmentation"
otx_eval_openvino_testing(template, tmp_dir_path, otx_dir, args, threshold=0.05, half_precision=half_precision)

@e2e_pytest_component
@pytest.mark.skipif(TT_STABILITY_TESTS, reason="This is TT_STABILITY_TESTS")
@pytest.mark.parametrize("template", templates, ids=templates_ids)
@pytest.mark.parametrize("template", templates_inc_segnext, ids=templates_ids_inc_segnext)
def test_otx_demo(self, template, tmp_dir_path):
tmp_dir_path = tmp_dir_path / "segmentation"
otx_demo_testing(template, tmp_dir_path, otx_dir, args)

@e2e_pytest_component
@pytest.mark.skipif(TT_STABILITY_TESTS, reason="This is TT_STABILITY_TESTS")
@pytest.mark.parametrize("template", templates, ids=templates_ids)
@pytest.mark.parametrize("template", templates_inc_segnext, ids=templates_ids_inc_segnext)
def test_otx_demo_openvino(self, template, tmp_dir_path):
tmp_dir_path = tmp_dir_path / "segmentation"
otx_demo_openvino_testing(template, tmp_dir_path, otx_dir, args)

@e2e_pytest_component
@pytest.mark.skipif(TT_STABILITY_TESTS, reason="This is TT_STABILITY_TESTS")
@pytest.mark.parametrize("template", templates, ids=templates_ids)
@pytest.mark.parametrize("template", templates_inc_segnext, ids=templates_ids_inc_segnext)
def test_otx_deploy_openvino(self, template, tmp_dir_path):
tmp_dir_path = tmp_dir_path / "segmentation"
otx_deploy_openvino_testing(template, tmp_dir_path, otx_dir, args)

@e2e_pytest_component
@pytest.mark.skipif(TT_STABILITY_TESTS, reason="This is TT_STABILITY_TESTS")
@pytest.mark.parametrize("template", templates, ids=templates_ids)
@pytest.mark.parametrize("template", templates_inc_segnext, ids=templates_ids_inc_segnext)
def test_otx_eval_deployment(self, template, tmp_dir_path):
tmp_dir_path = tmp_dir_path / "segmentation"
otx_eval_deployment_testing(template, tmp_dir_path, otx_dir, args, threshold=0.0)

@e2e_pytest_component
@pytest.mark.skipif(TT_STABILITY_TESTS, reason="This is TT_STABILITY_TESTS")
@pytest.mark.parametrize("template", templates, ids=templates_ids)
@pytest.mark.parametrize("template", templates_inc_segnext, ids=templates_ids_inc_segnext)
def test_otx_demo_deployment(self, template, tmp_dir_path):
tmp_dir_path = tmp_dir_path / "segmentation"
otx_demo_deployment_testing(template, tmp_dir_path, otx_dir, args)

@e2e_pytest_component
@pytest.mark.skipif(TT_STABILITY_TESTS, reason="This is TT_STABILITY_TESTS")
@pytest.mark.parametrize("template", templates, ids=templates_ids)
@pytest.mark.parametrize("template", templates_inc_segnext, ids=templates_ids_inc_segnext)
def test_otx_hpo(self, template, tmp_dir_path):
tmp_dir_path = tmp_dir_path / "segmentation/test_hpo"
otx_hpo_testing(template, tmp_dir_path, otx_dir, args)
Expand Down Expand Up @@ -227,7 +237,7 @@ def test_nncf_eval_openvino(self, template, tmp_dir_path):

@e2e_pytest_component
@pytest.mark.skipif(TT_STABILITY_TESTS, reason="This is TT_STABILITY_TESTS")
@pytest.mark.parametrize("template", templates, ids=templates_ids)
@pytest.mark.parametrize("template", templates_inc_segnext, ids=templates_ids_inc_segnext)
def test_pot_optimize(self, template, tmp_dir_path):
tmp_dir_path = tmp_dir_path / "segmentation"
pot_optimize_testing(template, tmp_dir_path, otx_dir, args)
Expand All @@ -241,15 +251,15 @@ def test_pot_validate_fq(self, template, tmp_dir_path):

@e2e_pytest_component
@pytest.mark.skipif(TT_STABILITY_TESTS, reason="This is TT_STABILITY_TESTS")
@pytest.mark.parametrize("template", templates, ids=templates_ids)
@pytest.mark.parametrize("template", templates_inc_segnext, ids=templates_ids_inc_segnext)
def test_pot_eval(self, template, tmp_dir_path):
tmp_dir_path = tmp_dir_path / "segmentation"
pot_eval_testing(template, tmp_dir_path, otx_dir, args)

@e2e_pytest_component
@pytest.mark.skipif(TT_STABILITY_TESTS, reason="This is TT_STABILITY_TESTS")
@pytest.mark.skipif(MULTI_GPU_UNAVAILABLE, reason="The number of gpu is insufficient")
@pytest.mark.parametrize("template", templates, ids=templates_ids)
@pytest.mark.parametrize("template", templates_inc_segnext, ids=templates_ids_inc_segnext)
def test_otx_multi_gpu_train(self, template, tmp_dir_path):
tmp_dir_path = tmp_dir_path / "segmentation/test_multi_gpu"
args1 = copy.deepcopy(args)
Expand All @@ -276,22 +286,22 @@ def test_otx_multi_gpu_train(self, template, tmp_dir_path):

class TestToolsMPASemiSLSegmentation:
@e2e_pytest_component
@pytest.mark.parametrize("template", templates, ids=templates_ids)
@pytest.mark.parametrize("template", templates_inc_segnext, ids=templates_ids_inc_segnext)
def test_otx_train(self, template, tmp_dir_path):
tmp_dir_path = tmp_dir_path / "segmentation/test_semisl"
otx_train_testing(template, tmp_dir_path, otx_dir, args_semisl)

@e2e_pytest_component
@pytest.mark.skipif(TT_STABILITY_TESTS, reason="This is TT_STABILITY_TESTS")
@pytest.mark.parametrize("template", templates, ids=templates_ids)
@pytest.mark.parametrize("template", templates_inc_segnext, ids=templates_ids_inc_segnext)
def test_otx_eval(self, template, tmp_dir_path):
tmp_dir_path = tmp_dir_path / "segmentation/test_semisl"
otx_eval_testing(template, tmp_dir_path, otx_dir, args_semisl)

@e2e_pytest_component
@pytest.mark.skipif(TT_STABILITY_TESTS, reason="This is TT_STABILITY_TESTS")
@pytest.mark.skipif(MULTI_GPU_UNAVAILABLE, reason="The number of gpu is insufficient")
@pytest.mark.parametrize("template", templates, ids=templates_ids)
@pytest.mark.parametrize("template", templates_inc_segnext, ids=templates_ids_inc_segnext)
def test_otx_multi_gpu_train_semisl(self, template, tmp_dir_path):
tmp_dir_path = tmp_dir_path / "segmentation/test_multi_gpu_semisl"
args_semisl_multigpu = copy.deepcopy(args_semisl)
Expand Down
12 changes: 10 additions & 2 deletions tests/integration/cli/semantic_segmentation/test_segmentation.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,14 @@
templates = [default_template]
templates_ids = [default_template.model_template_id]

# add integration test for semi-sl with new SegNext model and prototype based approach
# other tests will be updated accordingly after fully transfer to segnext templates
segnext_experimental_template = parse_model_template(
os.path.join("otx/algorithms/segmentation/configs", "ham_segnext_s", "template_experimental.yaml")
)
templates_inc_segnext = [segnext_experimental_template, default_template]
templates_ids_inc_segnext = [segnext_experimental_template.model_template_id, default_template.model_template_id]


class TestSegmentationCLI:
@e2e_pytest_component
Expand Down Expand Up @@ -186,14 +194,14 @@ def test_otx_multi_gpu_train(self, template, tmp_dir_path):
otx_train_testing(template, tmp_dir_path, otx_dir, args1)

@e2e_pytest_component
@pytest.mark.parametrize("template", templates, ids=templates_ids)
@pytest.mark.parametrize("template", templates_inc_segnext, ids=templates_ids_inc_segnext)
def test_otx_train_semisl(self, template, tmp_dir_path):
tmp_dir_path = tmp_dir_path / "segmentation/test_semisl"
otx_train_testing(template, tmp_dir_path, otx_dir, args_semisl)

@e2e_pytest_component
@pytest.mark.skipif(MULTI_GPU_UNAVAILABLE, reason="The number of gpu is insufficient")
@pytest.mark.parametrize("template", templates, ids=templates_ids)
@pytest.mark.parametrize("template", templates_inc_segnext, ids=templates_ids_inc_segnext)
def test_otx_multi_gpu_train_semisl(self, template, tmp_dir_path):
tmp_dir_path = tmp_dir_path / "segmentation/test_multi_gpu_semisl"
args_semisl_multigpu = copy.deepcopy(args_semisl)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import pytest
import torch

from otx.algorithms.segmentation.adapters.mmseg.models.heads.proto_head import ProtoNet
from tests.test_suite.e2e_test_system import e2e_pytest_unit


class TestProtoNet:
@pytest.fixture(autouse=True)
def setup(self):
self.proto_net = ProtoNet(
gamma=0.99, num_prototype=4, in_proto_channels=512, in_channels=512, channels=512, num_classes=4
)

def test_prototype_learning(self):
dummy_input = torch.rand(32768, 512)
dummy_out_seg = torch.rand(8, 4, 64, 64)
dummy_masks = torch.rand(32768, 4, 4)
dummy_gt_seg = torch.randint(low=0, high=5, size=(32768,))
proto_logits, proto_target = self.proto_net.prototype_learning(
dummy_input, dummy_out_seg, dummy_gt_seg, dummy_masks
)
assert proto_logits is not None
assert proto_target is not None

def test_forward(self):
dummy_input = torch.rand(8, 512, 64, 64)
dummy_gt_seg = torch.randint(low=0, high=5, size=(8, 1, 512, 512))
proto_out = self.proto_net(inputs=dummy_input, gt_semantic_seg=dummy_gt_seg)
assert isinstance(proto_out, dict)
assert proto_out["out_seg"] is not None
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import pytest
import torch

from otx.algorithms.segmentation.adapters.mmseg.models.losses import (
PixelPrototypeCELoss,
)

from tests.test_suite.e2e_test_system import e2e_pytest_unit


class TestPixelPrototypeCELoss:
@pytest.fixture(autouse=True)
def setup(self):
self.loss_proto_ce = PixelPrototypeCELoss()

@e2e_pytest_unit
def test_forward(self):
dummy_out = torch.rand(4, 5, 512, 512)
proto_logits = torch.rand(1280, 16)
proto_target = torch.rand(1280)
target = torch.randint(low=0, high=5, size=(4, 1, 512, 512))
loss = self.loss_proto_ce(dummy_out, proto_logits, proto_target, target)
assert loss is not None
assert loss >= 0
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import pytest
import torch

from otx.algorithms.segmentation.adapters.mmseg import MeanTeacherSegmentor
from tests.test_suite.e2e_test_system import e2e_pytest_unit


class TestMeanTeacherSegmentor:
@pytest.fixture(autouse=True)
def setup(self, mocker) -> None:
mocker.patch(
"otx.algorithms.segmentation.adapters.mmseg.models.segmentors.mean_teacher_segmentor.build_segmentor"
)
self.mean_teacher = MeanTeacherSegmentor(None, 100, test_cfg=dict(), decode_head={"align_corners": False})
self.mean_teacher.proto_net = mocker.MagicMock()
self.mean_teacher.use_prototype_head = True
self.input = torch.rand(4, 3, 512, 512)
self.gt_seg = torch.randint(low=0, high=5, size=(4, 1, 512, 512))

@e2e_pytest_unit
def test_decode_proto_network(self, mocker):
mocker_update_loss = mocker.patch.object(self.mean_teacher, "_update_summary_loss")
self.mean_teacher.decode_proto_network(self.input, self.gt_seg)
mocker_update_loss.assert_called_once()
# dummy input
self.mean_teacher.decode_proto_network(self.input, self.gt_seg, self.input, self.gt_seg)

@e2e_pytest_unit
def test_generate_pseudo_labels(self, mocker):
mocker.patch(
"otx.algorithms.segmentation.adapters.mmseg.models.segmentors.mean_teacher_segmentor.resize",
return_value=self.input,
)
pl_from_teacher, reweight_unsup = self.mean_teacher.generate_pseudo_labels(
ul_w_img=self.input, ul_img_metas=dict()
)
assert isinstance(pl_from_teacher, torch.Tensor)
assert pl_from_teacher.shape == (4, 1, 512, 512)
assert round(reweight_unsup.item(), 2) == 1.25

@e2e_pytest_unit
def test_forward_train(self, mocker):
loss = self.mean_teacher(self.input, img_metas=dict(), gt_semantic_seg=self.gt_seg)
assert loss is not None
self.mean_teacher.semisl_start_iter = -1
mocker.patch.object(self.mean_teacher, "decode_proto_network")
mocker.patch.object(self.mean_teacher, "generate_pseudo_labels", return_value=(self.gt_seg, 1.0))
ul_kwargs = dict(extra_0=dict(img=self.input, ul_w_img=self.input, img_metas=dict()))
loss = self.mean_teacher(self.input, img_metas=dict(), gt_semantic_seg=self.gt_seg, **ul_kwargs)
assert loss is not None
assert loss["sum_loss"] == 0.0

0 comments on commit 7baae35

Please sign in to comment.