From 7bccb4aee251aa249e4a1f254a08a1bda019727c Mon Sep 17 00:00:00 2001 From: mjq2020 Date: Mon, 16 Oct 2023 02:16:23 +0000 Subject: [PATCH 01/33] modify: yolov8 inference result foramt --- sscma/models/heads/yolov8_head.py | 42 ++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/sscma/models/heads/yolov8_head.py b/sscma/models/heads/yolov8_head.py index 437967c2..8acdca63 100644 --- a/sscma/models/heads/yolov8_head.py +++ b/sscma/models/heads/yolov8_head.py @@ -1,4 +1,5 @@ import math +from functools import partial from typing import List, Sequence, Tuple, Union from mmdet.structures import SampleList @@ -142,25 +143,33 @@ def _init_layers(self): proj = torch.arange(self.reg_max, dtype=torch.float) self.register_buffer('proj', proj, persistent=False) - def forward(self, x: Tuple[Tensor]) -> Tuple[List]: + def forward(self, x: Tuple[Tensor], export: bool = False) -> Tuple[List]: """Forward features from the upstream network. Args: x (Tuple[Tensor]): Features from the upstream network, each is a 4D-tensor. + export (bool): Whether to inference in export mode Returns: Tuple[List]: A tuple of multi-level classification scores, bbox predictions """ assert len(x) == self.num_levels - return multi_apply(self.forward_single, x, self.cls_preds, self.reg_preds) - - def forward_single(self, x: torch.Tensor, cls_pred: nn.ModuleList, reg_pred: nn.ModuleList) -> Tuple: + return multi_apply( + partial(self.forward_single, export=export), + x, + self.cls_preds, + self.reg_preds, + ) + + def forward_single( + self, x: torch.Tensor, cls_pred: nn.ModuleList, reg_pred: nn.ModuleList, export: bool = False + ) -> Tuple: """Forward feature of a single scale level.""" b, _, h, w = x.shape cls_logit = cls_pred(x) bbox_dist_preds = reg_pred(x) - if self.reg_max > 1: + if self.reg_max > 1 and not export: bbox_dist_preds = bbox_dist_preds.reshape([-1, 4, self.reg_max, h * w]).permute(0, 3, 1, 2) # TODO: The get_flops script cannot handle the situation of # matmul, and needs to be fixed later @@ -198,22 +207,25 @@ def predict(self, x: Tuple[Tensor], batch_data_samples: SampleList, rescale: boo def forward(self, x: Tuple[Tensor]) -> Tensor: B = x[0].shape[0] - out = super().forward(x=x) + out = self.head_module(x, export=True) return self.export(*out, batch_num=B) def export(self, cls_scores: List[Tensor], bbox_preds: List[Tensor], batch_num: int = 1) -> Tensor: assert len(cls_scores) == len(bbox_preds) featmap_len = len(cls_scores) tmp = [torch.cat((bbox_preds[idx], cls_scores[idx]), 1) for idx in range(featmap_len)] - anchors, stides = (i.transpose(0, 1) for i in make_anchors(tmp, self.head_module.featmap_strides, 0.5)) - x_cat = torch.cat([i.reshape(batch_num, 4 + self.num_classes, -1) for i in tmp], 2) - bbox, cls = x_cat.split((4, self.num_classes), 1) - lt, rb = bbox.chunk(2, 1) - x1y1 = anchors.unsqueeze(0) - lt - x2y2 = anchors.unsqueeze(0) + rb - dbox = torch.cat((x1y1, x2y2), 1) * stides - preds = torch.cat((dbox.permute(0, 2, 1), cls.sigmoid().permute(0, 2, 1) * 100), 2) - return preds + x_cat = torch.cat( + [i.reshape(batch_num, 4 * self.head_module.reg_max + self.num_classes, -1).permute(0, 2, 1) for i in tmp], 1 + ) + return x_cat + + # bbox, cls = x_cat.split((4, self.num_classes), 1) + # lt, rb = bbox.chunk(2, 1) + # x1y1 = anchors.unsqueeze(0) - lt + # x2y2 = anchors.unsqueeze(0) + rb + # dbox = torch.cat((x1y1, x2y2), 1) * stides + # preds = torch.cat((dbox.permute(0, 2, 1), cls.sigmoid().permute(0, 2, 1) * 100), 2) + # return preds def make_anchors(feats, strides, grid_cell_offset=0.5): From 0ee15130cc2c1ac911cffc4f92ba213ab6ee9ea9 Mon Sep 17 00:00:00 2001 From: mjq2020 Date: Mon, 16 Oct 2023 02:28:59 +0000 Subject: [PATCH 02/33] delete: vscode config --- .vscode/settings.json | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 3f23075d..00000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "[python]": { - "editor.defaultFormatter": "ms-python.black-formatter" - }, - "python.formatting.provider": "black" -} From 0dd455fe0b7a157e092384271bb25737c8a2acd0 Mon Sep 17 00:00:00 2001 From: mjq2020 Date: Fri, 20 Oct 2023 10:34:40 +0000 Subject: [PATCH 03/33] add: semidataset --- sscma/datasets/dataset_wrappers.py | 31 ++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 sscma/datasets/dataset_wrappers.py diff --git a/sscma/datasets/dataset_wrappers.py b/sscma/datasets/dataset_wrappers.py new file mode 100644 index 00000000..6949b18e --- /dev/null +++ b/sscma/datasets/dataset_wrappers.py @@ -0,0 +1,31 @@ +from torch.utils.data.dataset import ConcatDataset as _ConcatDataset +from sscma.registry import DATASETS + + +@DATASETS.register_module(_ConcatDataset) +class SemiDataset(_ConcatDataset): + """ + For merging real labeled and pseudo-labeled datasets in semi-supervised. + + Params: + sup_dataset (dict): Real labeled dataset configuration + unsup_dataset (dict): Pseudo-labeled dataset configuration + """ + + def __init__(self, sup_dataset: dict, unsup_dataset: dict, **kwargs) -> None: + self._sup_dataset = DATASETS.build(sup_dataset) + self._unsup_dataset = DATASETS.build(unsup_dataset) + + super(SemiDataset, self).__init__((self._sup_dataset, self._unsup_dataset)) + + self.CLASSES = self.sup_dataset.CLASSES + + @property + def sup_dataset(self): + # get real labeled dataset + return self._sup_dataset + + @property + def unsup_dataset(self): + # get pseudo labeled dataset + return self._unsup_dataset From 1c9440e0a6345c8120dc1565314f5267d2064af7 Mon Sep 17 00:00:00 2001 From: mjq2020 Date: Fri, 20 Oct 2023 10:36:13 +0000 Subject: [PATCH 04/33] add: dataset sampler for semi --- sscma/datasets/sampler.py | 111 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 sscma/datasets/sampler.py diff --git a/sscma/datasets/sampler.py b/sscma/datasets/sampler.py new file mode 100644 index 00000000..19d33a98 --- /dev/null +++ b/sscma/datasets/sampler.py @@ -0,0 +1,111 @@ +from typing import Iterator, List, Optional, Any +from mmengine.dataset import DefaultSampler +from mmengine.dataset.sampler import DefaultSampler +from sscma.registry import DATA_SANPLERS + +from torch.utils.data import ConcatDataset, DataLoader, Dataset + +import numpy as np +import torch +import random + + +@DATA_SANPLERS.register_module() +class SemiSampler(DefaultSampler): + """ + Sampler for scaled sampling of semi-supervised data + + Params: + dataset (torch::ConcatDataset): Multiple merged datasets + batch_size (int): Training is set batch_size + sample_ratio (List[int,float]): Sampling rate for each dataset + with length equal to the number of datasets in dataset + shuffle: (bool): Whether to disrupt the sampling order of the data + seed (int): Random seed used + round_up (bool): If the ratio of the length of each dataset is not + equal to the ratio of the sampling rate, whether to resample + the under-sampled data. + """ + + def __init__( + self, + dataset: ConcatDataset, + batch_size: int, + sample_ratio: List[int, float], + shuffle: bool = True, + seed: Optional[int] = None, + round_up: bool = True, + ) -> None: + assert len(sample_ratio) == len( + dataset.cumulative_sizes + ), "Sampling rate length must be equal to the number of datasets." + + super(SemiSampler, self).__init__(dataset, shuffle=shuffle, seed=seed, round_up=round_up) + if seed is not None: + self.set_seed(seed) + + self.dataset = dataset + self.batch_size = batch_size + sample_ratio = [scale / sum(sample_ratio) for scale in sample_ratio] + self.sample_ratio = sample_ratio + self.sample_size = [int(sample_ratio[0] * batch_size), batch_size - int(sample_ratio[0] * batch_size)] + + data_index = np.arange(dataset.cumulative_sizes[1]) + self.data1 = data_index[: dataset.cumulative_sizes[0]] + self.data2 = data_index[dataset.cumulative_sizes[0] :] + if shuffle: + np.random.shuffle(self.data1) + np.random.shuffle(self.data2) + + if round_up: + self.epoch = max(len(self.data1) // self.sample_size[0], len(self.data2) // self.sample_size[1]) + else: + self.epoch = min(len(self.data1) // self.sample_size[0], len(self.data2) // self.sample_size[1]) + + self.datasets_len = dataset.cumulative_sizes[-1] + + def __iter__(self) -> Iterator[int]: + indexs = [] + num1 = 0 + num2 = 0 + for i in range(self.epoch): + for _ in range(self.sample_size[0]): + indexs.append(self.data1[num1 % len(self.data1)]) + num1 += 1 + for _ in range(self.sample_size[1]): + indexs.append(self.data2[num2 % len(self.data2)]) + num2 += 1 + + return iter(indexs) + + def __len__(self) -> int: + return self.epoch * self.batch_size + + def set_seed(self, seed: int) -> None: + # set seed + np.random.seed(seed) + torch.manual_seed(seed) + random.seed(seed) + + +if __name__ == "__main__": + + class TestSet(Dataset): + def __init__(self, start=0, end=100) -> None: + super().__init__() + self.start = start + self.end = end + self.data = [i for i in range(start, end)] + + def __getitem__(self, index) -> Any: + return self.data[index] + + def __len__(self): + return len(self.data) + + datasets = ConcatDataset([TestSet(), TestSet(start=100, end=200)]) + batch = 8 + sampler = SemiSampler(datasets, batch, [2, 6]) + data_loader = DataLoader(dataset=datasets, batch_size=batch, sampler=sampler, num_workers=1) + for idx, data in enumerate(data_loader): + print(idx, data) From bfd427773c0aefded8332656d42a49f66c5ca57b Mon Sep 17 00:00:00 2001 From: mjq2020 Date: Wed, 8 Nov 2023 10:46:10 +0000 Subject: [PATCH 05/33] add: semisup train eval loop and hook --- sscma/engine/hooks/__init__.py | 2 ++ sscma/engine/hooks/semihook.py | 30 +++++++++++++++++++ sscma/engine/runner/__init__.py | 4 +-- sscma/engine/runner/loops.py | 51 +++++++++++++++++++++++++++++++-- 4 files changed, 82 insertions(+), 5 deletions(-) create mode 100644 sscma/engine/hooks/semihook.py diff --git a/sscma/engine/hooks/__init__.py b/sscma/engine/hooks/__init__.py index b6097986..a8d15505 100644 --- a/sscma/engine/hooks/__init__.py +++ b/sscma/engine/hooks/__init__.py @@ -5,6 +5,7 @@ WandbLoggerHook, ) from .visualization_hook import DetFomoVisualizationHook, Posevisualization +from .semihook import SemidHook __all__ = [ 'TextLoggerHook', @@ -14,4 +15,5 @@ 'ClearMLLoggerHook', 'Posevisualization', 'DetFomoVisualizationHook', + "SemiHook", ] diff --git a/sscma/engine/hooks/semihook.py b/sscma/engine/hooks/semihook.py new file mode 100644 index 00000000..735335d8 --- /dev/null +++ b/sscma/engine/hooks/semihook.py @@ -0,0 +1,30 @@ +from typing import Union +from mmengine.hooks import Hook +from mmengine.runner import Runner + +from sscma.registry import HOOKS + + +@HOOKS.register_module() +class SemidHook(Hook): + """ """ + + def __init__(self, bure_epoch: Union[float, int] = 1) -> None: + super().__init__() + if isinstance(bure_epoch, float): + assert ( + bure_epoch <= 1.0 + ), "The number of supervised training rounds must be less than the maximum number of rounds" + + self.bure_epoch = bure_epoch + + def before_run(self, runner: Runner) -> None: + if isinstance(self.bure_epoch, float): + self.bure_epoch = int(runner.max_epochs * self.bure_epoch) + + assert self.bure_epoch <= runner.max_epochs + + def before_train_epoch(self, runner: Runner) -> None: + if self.bure_epoch == runner.epoch: + # dataloader starts loading unlabeled dataset for semi-supervised training + runner.train_dataloader.sampler.with_unlabel = True diff --git a/sscma/engine/runner/__init__.py b/sscma/engine/runner/__init__.py index 9acd586b..f202eede 100644 --- a/sscma/engine/runner/__init__.py +++ b/sscma/engine/runner/__init__.py @@ -1,3 +1,3 @@ -from .loops import GetEpochBasedTrainLoop +from .loops import GetEpochBasedTrainLoop, SemiValLoop -__all__ = ['GetEpochBasedTrainLoop'] +__all__ = ['GetEpochBasedTrainLoop', 'SemiValLoop'] diff --git a/sscma/engine/runner/loops.py b/sscma/engine/runner/loops.py index 88023635..f192f8c1 100644 --- a/sscma/engine/runner/loops.py +++ b/sscma/engine/runner/loops.py @@ -1,15 +1,60 @@ -from typing import Dict, List, Sequence, Union +from typing import Dict, List, Optional, Sequence, Tuple, Union +from mmengine.evaluator import Evaluator import onnx import torch from mmengine.evaluator.evaluator import Evaluator -from mmengine.runner import Runner +from mmengine.runner import Runner, ValLoop from mmengine.runner.loops import BaseLoop, EpochBasedTrainLoop +from mmengine.model import is_model_wrapper from torch.utils.data import DataLoader from sscma.registry import LOOPS +@LOOPS.register_module() +class SemiValLoop(ValLoop): + """Loop for validation of model teacher and student.""" + + runner: Runner + + def __init__(self, bure_epoch: int, *args, **kwargs) -> None: + super().__init__(*args, **kwargs) + self.bure_epoch = bure_epoch + + def run(self) -> None: + """Launch validation for model teacher and student.""" + self.runner.call_hook('before_val') + self.runner.call_hook('before_val_epoch') + self.runner.model.eval() + + model = self.runner.model + if is_model_wrapper(model): + model = model.module + assert hasattr(model, 'teacher') + assert hasattr(model, 'student') + + predict_on = model.semi_test_cfg.get('predict_on', None) + multi_metrics = dict() + + if self.runner.epoch < self.bure_epoch: + eval_model = ['student'] + else: + eval_model = ['student', 'teacher'] + + for _predict_on in eval_model: + model.semi_test_cfg['predict_on'] = _predict_on + for idx, data_batch in enumerate(self.dataloader): + self.run_iter(idx, data_batch) + # compute metrics + metrics = self.evaluator.evaluate(len(self.dataloader.dataset)) + multi_metrics.update({'/'.join((_predict_on, k)): v for k, v in metrics.items()}) + model.semi_test_cfg['predict_on'] = predict_on + + self.runner.call_hook('after_val_epoch', metrics=multi_metrics) + self.runner.call_hook('after_val') + + @LOOPS.register_module() class GetEpochBasedTrainLoop(EpochBasedTrainLoop): def run_iter(self, idx, data_batch: Sequence[dict]) -> None: @@ -37,7 +82,7 @@ def __init__( dataloader: Union[DataLoader, Dict], evaluator: Union[Evaluator, Dict, List], fp16: bool = False, - ): + ) -> None: super().__init__(runner, dataloader) From f1f70984c99086d19053f29e1c8b9e5126413e42 Mon Sep 17 00:00:00 2001 From: mjq2020 Date: Wed, 8 Nov 2023 10:47:54 +0000 Subject: [PATCH 06/33] add: semisup sampler --- sscma/datasets/sampler.py | 79 +++++++++++++++++++++++++++++---------- 1 file changed, 59 insertions(+), 20 deletions(-) diff --git a/sscma/datasets/sampler.py b/sscma/datasets/sampler.py index 19d33a98..546da5be 100644 --- a/sscma/datasets/sampler.py +++ b/sscma/datasets/sampler.py @@ -1,4 +1,4 @@ -from typing import Iterator, List, Optional, Any +from typing import Iterator, List, Optional, Any, Union from mmengine.dataset import DefaultSampler from mmengine.dataset.sampler import DefaultSampler from sscma.registry import DATA_SANPLERS @@ -31,10 +31,10 @@ def __init__( self, dataset: ConcatDataset, batch_size: int, - sample_ratio: List[int, float], + sample_ratio: List[Union[int, float]], shuffle: bool = True, seed: Optional[int] = None, - round_up: bool = True, + round_up: bool = False, ) -> None: assert len(sample_ratio) == len( dataset.cumulative_sizes @@ -45,7 +45,9 @@ def __init__( self.set_seed(seed) self.dataset = dataset - self.batch_size = batch_size + self._batch_size = batch_size + self.round_up = round_up + self.shuffle = shuffle sample_ratio = [scale / sum(sample_ratio) for scale in sample_ratio] self.sample_ratio = sample_ratio self.sample_size = [int(sample_ratio[0] * batch_size), batch_size - int(sample_ratio[0] * batch_size)] @@ -53,33 +55,55 @@ def __init__( data_index = np.arange(dataset.cumulative_sizes[1]) self.data1 = data_index[: dataset.cumulative_sizes[0]] self.data2 = data_index[dataset.cumulative_sizes[0] :] - if shuffle: - np.random.shuffle(self.data1) - np.random.shuffle(self.data2) - - if round_up: - self.epoch = max(len(self.data1) // self.sample_size[0], len(self.data2) // self.sample_size[1]) - else: - self.epoch = min(len(self.data1) // self.sample_size[0], len(self.data2) // self.sample_size[1]) + # 是否加载无标签数据 + self._with_unlabel = False self.datasets_len = dataset.cumulative_sizes[-1] + self.computer_epoch() def __iter__(self) -> Iterator[int]: indexs = [] num1 = 0 num2 = 0 - for i in range(self.epoch): - for _ in range(self.sample_size[0]): - indexs.append(self.data1[num1 % len(self.data1)]) - num1 += 1 - for _ in range(self.sample_size[1]): - indexs.append(self.data2[num2 % len(self.data2)]) - num2 += 1 + if self.shuffle: + np.random.shuffle(self.data1) + np.random.shuffle(self.data2) + + for i in range(self.total_epoch): + if self.with_unlabel: + for _ in range(self.sample_size[0]): + indexs.append(self.data1[num1 % len(self.data1)]) + num1 += 1 + for _ in range(self.sample_size[1]): + indexs.append(self.data2[num2 % len(self.data2)]) + num2 += 1 + else: + for _ in range(self.sample_size[0] + self.sample_size[1]): + indexs.append(self.data1[num1 % len(self.data1)]) + num1 += 1 return iter(indexs) def __len__(self) -> int: - return self.epoch * self.batch_size + return self.total_epoch * self._batch_size + + def computer_epoch(self): + if self.with_unlabel: + if self.round_up: + self.total_epoch = max(len(self.data1) // self.sample_size[0], len(self.data2) // self.sample_size[1]) + else: + self.total_epoch = min(len(self.data1) // self.sample_size[0], len(self.data2) // self.sample_size[1]) + else: + self.total_epoch = len(self.data1) // sum(self.sample_size) + + @property + def batch_size(self) -> int: + return self._batch_size + + @batch_size.setter + def batch_size(self, batch: int) -> None: + self._batch_size = batch + self.sample_size = [int(self.sample_ratio[0] * batch), batch - int(self.sample_ratio[0] * batch)] def set_seed(self, seed: int) -> None: # set seed @@ -87,6 +111,18 @@ def set_seed(self, seed: int) -> None: torch.manual_seed(seed) random.seed(seed) + @property + def with_unlabel(self) -> bool: + return self._with_unlabel + + @with_unlabel.setter + def with_unlabel(self, unlabel: bool) -> None: + self._with_unlabel = unlabel + self.computer_epoch() + + def set_epoch(self, epoch: int) -> None: + self.epoch = epoch + if __name__ == "__main__": @@ -109,3 +145,6 @@ def __len__(self): data_loader = DataLoader(dataset=datasets, batch_size=batch, sampler=sampler, num_workers=1) for idx, data in enumerate(data_loader): print(idx, data) + data_loader.sampler.with_unlabel = True + for idx, data in enumerate(data_loader): + print(idx, data) From c223e5f94d39151a261423996389338cb0cac7dc Mon Sep 17 00:00:00 2001 From: mjq2020 Date: Wed, 8 Nov 2023 10:57:28 +0000 Subject: [PATCH 07/33] add: semisup loss --- sscma/models/losses/__init__.py | 11 ++- sscma/models/losses/domain_focal_loss.py | 118 +++++++++++++++++++++++ 2 files changed, 128 insertions(+), 1 deletion(-) create mode 100644 sscma/models/losses/domain_focal_loss.py diff --git a/sscma/models/losses/__init__.py b/sscma/models/losses/__init__.py index 3bac913b..a56145da 100644 --- a/sscma/models/losses/__init__.py +++ b/sscma/models/losses/__init__.py @@ -2,5 +2,14 @@ from .classfication_loss import LabelSmoothCrossEntropyLoss from .nll_loss import NLLLoss from .pfld_loss import PFLDLoss +from .domain_focal_loss import DomainFocalLoss, TargetLoss, DomainLoss -__all__ = ['LabelSmoothCrossEntropyLoss', 'PFLDLoss', 'NLLLoss', 'BCEWithLogitsLoss'] +__all__ = [ + 'LabelSmoothCrossEntropyLoss', + 'PFLDLoss', + 'NLLLoss', + 'BCEWithLogitsLoss', + 'DomainFocalLoss', + 'TargetLoss', + 'DomainLoss', +] diff --git a/sscma/models/losses/domain_focal_loss.py b/sscma/models/losses/domain_focal_loss.py new file mode 100644 index 00000000..50c793ea --- /dev/null +++ b/sscma/models/losses/domain_focal_loss.py @@ -0,0 +1,118 @@ +from typing import Optional, Union, Tuple + +import torch +import torch.nn as nn +from torch.autograd import Variable +from torch.nn import functional as F + +from sscma.registry import LOSSES + + +@LOSSES.register_module() +class DomainFocalLoss(nn.Module): + def __init__( + self, + class_num: int, + alpha: Optional[Union[float, Variable]] = None, + gamma: int = 2, + size_average: bool = True, + sigmoid: bool = False, + reduce: bool = True, + ) -> None: + super(DomainFocalLoss, self).__init__() + if alpha is None: + self.alpha = Variable(torch.ones(class_num, 1) * 1.0) + else: + if isinstance(alpha, Variable): + self.alpha = alpha + else: + self.alpha = Variable(alpha) + self.gamma = gamma + self.class_num = class_num + self.size_average = size_average + self.sigmoid = sigmoid + self.reduce = reduce + + def forward(self, inputs: torch.Tensor, targets: torch.Tensor) -> torch.Tensor: + N = inputs.size(0) + + C = inputs.size(1) + if self.sigmoid: + P = F.sigmoid(inputs) + # F.softmax(inputs) + if targets == 0: + probs = 1 - P # (P * class_mask).sum(1).view(-1, 1) + log_p = probs.log() + batch_loss = -(torch.pow((1 - probs), self.gamma)) * log_p + if targets == 1: + probs = P # (P * class_mask).sum(1).view(-1, 1) + log_p = probs.log() + batch_loss = -(torch.pow((1 - probs), self.gamma)) * log_p + else: + P = F.softmax(inputs, dim=1) + + class_mask = inputs.data.new(N, C).fill_(0) + class_mask = Variable(class_mask) + ids = targets.view(-1, 1) + class_mask.scatter_(1, ids.data, 1.0) + + if inputs.is_cuda and not self.alpha.is_cuda: + self.alpha = self.alpha.cuda() + alpha = self.alpha[ids.data.view(-1)] + + probs = (P * class_mask).sum(1).view(-1, 1) + + log_p = probs.log() + batch_loss = -alpha * (torch.pow((1 - probs), self.gamma)) * log_p + + if not self.reduce: + return batch_loss + if self.size_average: + loss = batch_loss.mean() + else: + loss = batch_loss.sum() + return loss + + +@LOSSES.register_module() +class TargetLoss: + def __init__(self) -> None: + self.fl = DomainFocalLoss(class_num=2) + + def __call__(self, feature: torch.Tensor) -> torch.Tensor: + out_8 = feature[0] + out_16 = feature[1] + out_32 = feature[2] + + out_d_t_8 = out_8.permute(0, 2, 3, 1).reshape(-1, 2) + out_d_t_16 = out_16.permute(0, 2, 3, 1).reshape(-1, 2) + out_d_t_32 = out_32.permute(0, 2, 3, 1).reshape(-1, 2) + out_d_t = torch.cat((out_d_t_8, out_d_t_16, out_d_t_32), 0) + + # domain label + domain_t = Variable(torch.ones(out_d_t.size(0)).long().cuda()) + dloss_t = 0.5 * self.fl(out_d_t, domain_t) + return dloss_t + + +@LOSSES.register_module() +class DomainLoss: + def __init__(self): + self.fl = DomainFocalLoss(class_num=2) + + def __call__(self, feature: Tuple[torch.Tensor]) -> torch.Tensor: + out_8 = feature[0] + out_16 = feature[1] + out_32 = feature[2] + + out_d_s_8 = out_8.permute(0, 2, 3, 1).reshape(-1, 2) + out_d_s_16 = out_16.permute(0, 2, 3, 1).reshape(-1, 2) + out_d_s_32 = out_32.permute(0, 2, 3, 1).reshape(-1, 2) + + out_d_s = torch.cat((out_d_s_8, out_d_s_16, out_d_s_32), 0) + + # domain label + domain_s = Variable(torch.zeros(out_d_s.size(0)).long().cuda()) + # global alignment loss + dloss_s = 0.5 * self.fl(out_d_s, domain_s) + return dloss_s From bd80826d042fa054e8ad4c602d4754d20194ed04 Mon Sep 17 00:00:00 2001 From: mjq2020 Date: Wed, 8 Nov 2023 11:43:22 +0000 Subject: [PATCH 08/33] optimizer: fomo inference --- sscma/utils/inference.py | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/sscma/utils/inference.py b/sscma/utils/inference.py index 22d84bff..ad9f62ee 100644 --- a/sscma/utils/inference.py +++ b/sscma/utils/inference.py @@ -1,7 +1,6 @@ import os import os.path as osp import time -import math from typing import AnyStr, List, Optional, Sequence, Tuple, Union import cv2 @@ -71,15 +70,8 @@ def __init__(self, model: List or AnyStr or Tuple): net.load_param(param) net.load_model(bin) net.opt.use_vulkan_compute = False - # computer intput shape,non-strict calculation - extra = net.create_extractor() - input_name = net.input_names()[0] - output_name = net.output_names()[0] - extra.input(input_name, ncnn.Mat(np.random.randn(1, 3, 192, 192))) # noqa - H = extra.extract(output_name)[1].h - w = h = int(math.sqrt(H / 21) * 32) self.engine = 'ncnn' - self._input_shape = [3, h, w] + self._input_shape = [3, 640, 640] elif model.endswith('onnx'): try: import onnxruntime @@ -395,7 +387,8 @@ def test(self) -> None: if not self.source: ori_shape = data['data_samples'][0].ori_shape bboxes = data['data_samples'][0].gt_instances - target = build_target(preds.shape[1:], (96, 96), bboxes) + # target = build_target(preds.shape[1:], (H*8, W*8), bboxes) + target = torch.as_tensor(data['data_samples'][0].fomo_mask[0]) data['data_samples'][0].pred_instances = InstanceData( pred=tuple([torch.from_numpy(preds).permute(0, 3, 1, 2)]), labels=tuple([target]) @@ -445,17 +438,16 @@ def test(self) -> None: img = img * 255 img_scale = 1 / _get_adaptive_scale(img.shape[:2]) - - + img = cv2.resize(img, (int(img.shape[1] * img_scale), int(img.shape[0] * img_scale))) - + self.visualizer.set_image(img) label = np.argmax(preds[0], axis=1) data['data_samples'][0].set_pred_score(preds[0][0]).set_pred_label(label) self.evaluator.process(data_samples=data['data_samples'], data_batch=data) - + texts = self.visualizer.dataset_meta["classes"][label[0]] + ':' + str(preds[0][0][label[0]]) - + text_cfg = dict() text_cfg = { 'positions': np.array([[0, 5]]), @@ -465,7 +457,7 @@ def test(self) -> None: 'bboxes': dict(facecolor='black', alpha=0.5, boxstyle='Round'), **text_cfg, } - + self.visualizer.draw_texts(texts, **text_cfg) if self.show: From 1a9c23208602d92c8c7c68f6482dff6ef0e61044 Mon Sep 17 00:00:00 2001 From: mjq2020 Date: Thu, 9 Nov 2023 07:55:48 +0000 Subject: [PATCH 09/33] Add the fu:nction of exporting vela model --- requirements/export.txt | 5 ++++- tools/export.py | 36 +++++++++++++++++++++++++++++++----- 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/requirements/export.txt b/requirements/export.txt index 78b69ac2..68a357f9 100644 --- a/requirements/export.txt +++ b/requirements/export.txt @@ -2,4 +2,7 @@ TinyNeuralNetwork @ https://files.seeedstudio.com/sscma/library/TinyNeuralNetwork-0.1.1-py3-none-any.whl # ncnn -pnnx \ No newline at end of file +pnnx + +# vela +ethos-u-vela \ No newline at end of file diff --git a/tools/export.py b/tools/export.py index 8256d4fa..6facacd4 100644 --- a/tools/export.py +++ b/tools/export.py @@ -11,6 +11,7 @@ import sscma.evaluation # noqa import sscma.models # noqa import sscma.visualization # noqa +from sscma.utils.check import check_lib def parse_args(): @@ -25,7 +26,7 @@ def parse_args(): '--targets', type=str, nargs='+', - default=['tflite', 'onnx', 'pnnx'], + default=['tflite', 'onnx', 'pnnx', 'vela'], help='the target type of model(s) to export e.g. tflite onnx', ) parser.add_argument( @@ -78,6 +79,12 @@ def parse_args(): action=DictAction, help="override some settings in the used config, the key-value pair in 'xxx=yyy' format will be merged into config file", ) + parser.add_argument( + '--vela', + nargs='+', + action=DictAction, + help='Parameters required for exporting vela model, need to correspond to vela command line parameters.', + ) parser.add_argument( '--simplify', type=int, @@ -150,7 +157,7 @@ def verify_args(args): ), "The chackpoint model should be a PyTorch model with '.pth' extension" assert os.path.exists(args.checkpoint), 'The chackpoint model does not exist' assert {str(t).lower() for t in args.targets}.issubset( - {'tflite', 'onnx', 'pnnx'} + {'tflite', 'onnx', 'pnnx', 'vela'} ), 'Supported in target type(s): onnx, tflite' assert {str(p).lower() for p in args.precisions}.issubset( {'int8', 'uint8', 'int16', 'float16', 'float32'} @@ -182,7 +189,6 @@ def build_config(args): args.work_dir = cfg.work_dir = os.path.join('work_dirs', os.path.splitext(os.path.basename(args.config))[0]) else: args.work_dir = cfg.work_dir - if args.device.startswith('cuda'): args.device = args.device if torch.cuda.is_available() else 'cpu' @@ -195,8 +201,8 @@ def build_config(args): if args.input_shape is None: try: - if 'shape' in cfg: - args.input_shape = cfg.shape + if 'imgsz' in cfg: + args.input_shape = [1, 1 if cfg.get('gray', False) else 3, *cfg.imgsz] elif 'width' in cfg and 'height' in cfg: args.input_shape = [ 1, @@ -376,6 +382,9 @@ def export_tflite(args, model, loader): converter.convert() except Exception as exp: raise RuntimeError('TFLite: Failed exporting the model') from exp + else: + if 'vela' in args.targets: + export_vela(args, tflite_file) print('TFLite: Successfully export model: {}'.format(tflite_file)) @@ -424,6 +433,23 @@ def export_onnx(args, model): print('ONNX: Successfully export model: {}'.format(onnx_file)) +def export_vela(args, model): + if check_lib('ethos-u-vela'): + from ethosu.vela.vela import main as vela_main + else: + raise ImportError( + 'An error occurred while importing "ethosu", please check if "ethosu" is already installed,', + 'or run "pip install ethos-u-vela" and try again.', + ) + + vela_args = [model, '--output-dir', os.path.dirname(model)] + if args.vela is not None: + for key, value in args.vela.items(): + vela_args.append('--' + key) + vela_args.append(value) + vela_main(vela_args) + + def main(): args = parse_args() args = verify_args(args) From 9e21986840fe1f5074137f22a2659a4ba6162061 Mon Sep 17 00:00:00 2001 From: mjq2020 Date: Thu, 9 Nov 2023 08:05:21 +0000 Subject: [PATCH 10/33] Add: the function of installing and checking third-party libraries --- sscma/utils/__init__.py | 14 ++++++++- sscma/utils/check.py | 66 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 sscma/utils/check.py diff --git a/sscma/utils/__init__.py b/sscma/utils/__init__.py index 5498663d..eaf3de3d 100644 --- a/sscma/utils/__init__.py +++ b/sscma/utils/__init__.py @@ -2,5 +2,17 @@ from .config import load_config from .inference import Infernce from .iot_camera import IoTCamera +from .check import net_online, install_lib, check_lib -__all__ = ['NMS', 'xywh2xyxy', 'xyxy2cocoxywh', 'load_image', 'load_config', 'Infernce', 'IoTCamera'] +__all__ = [ + 'NMS', + 'xywh2xyxy', + 'xyxy2cocoxywh', + 'load_image', + 'load_config', + 'Infernce', + 'IoTCamera', + 'net_online', + 'install_lib', + 'check_lib', +] diff --git a/sscma/utils/check.py b/sscma/utils/check.py new file mode 100644 index 00000000..c8db9ca9 --- /dev/null +++ b/sscma/utils/check.py @@ -0,0 +1,66 @@ +from typing import Optional, Union, Iterable +import subprocess + + +def net_online() -> bool: + """ + Check whether the current device is connected to the Internet. + """ + import socket + + for host in '1.1.1.1', '8.8.8.8', '223.5.5.5': # Cloudflare, Google, AliDNS: + try: + test_connection = socket.create_connection(address=(host, 53), timeout=2) + except (socket.timeout, socket.gaierror, OSError): + continue + else: + # If the connection was successful, close it to avoid a ResourceWarning + test_connection.close() + return True + return False + + +def install_lib(name: Union[str, Iterable[str]], version: Optional[Union[str, Iterable[str]]] = None) -> bool: + """Install python third-party libraries.""" + if isinstance(name, str): + name = [name] + if version is not None: + if isinstance(version, str): + name = [i + version for i in name] + else: + name = [i + version[idx] for idx, i in enumerate(name)] + + if version is not None: + name = '=='.join(name) + try: + assert net_online(), "Network Connection Failure!" + print(f"Third-party library {name} being installed") + subprocess.check_output(f'pip install --no-cache {name}', shell=True).decode() + return True + except Exception as e: + print(f"Warning ⚠️: Installation of {name} has failed, the installation process has been skipped") + return False + + +def check_lib(name: str, version: Optional[str] = None, install: bool = True) -> bool: + """ + Check if the third-party libraries have been installed. + """ + import pkg_resources as pkg + + flag = True + try: + pkg.require(name) + except pkg.DistributionNotFound: + try: + import importlib + + importlib.import_module(next(pkg.parse_requirements(name)).name) + except ImportError: + flag = False + except pkg.VersionConflict: + pass + + if install and not flag: + return install_lib(name, version=version) + return flag From 95525b6d4f3d17bc2c801db1b8037fa25c9d21f5 Mon Sep 17 00:00:00 2001 From: mjq2020 Date: Thu, 16 Nov 2023 09:07:42 +0000 Subject: [PATCH 11/33] modify: sample size is zero bug --- sscma/datasets/sampler.py | 53 +++++++++++++-------------------------- 1 file changed, 17 insertions(+), 36 deletions(-) diff --git a/sscma/datasets/sampler.py b/sscma/datasets/sampler.py index 546da5be..1fabdfff 100644 --- a/sscma/datasets/sampler.py +++ b/sscma/datasets/sampler.py @@ -1,13 +1,12 @@ from typing import Iterator, List, Optional, Any, Union -from mmengine.dataset import DefaultSampler -from mmengine.dataset.sampler import DefaultSampler -from sscma.registry import DATA_SANPLERS - -from torch.utils.data import ConcatDataset, DataLoader, Dataset -import numpy as np import torch import random +import numpy as np +from torch.utils.data import ConcatDataset +from mmengine.dataset import DefaultSampler + +from sscma.registry import DATA_SANPLERS @DATA_SANPLERS.register_module() @@ -50,13 +49,12 @@ def __init__( self.shuffle = shuffle sample_ratio = [scale / sum(sample_ratio) for scale in sample_ratio] self.sample_ratio = sample_ratio - self.sample_size = [int(sample_ratio[0] * batch_size), batch_size - int(sample_ratio[0] * batch_size)] data_index = np.arange(dataset.cumulative_sizes[1]) self.data1 = data_index[: dataset.cumulative_sizes[0]] self.data2 = data_index[dataset.cumulative_sizes[0] :] - - # 是否加载无标签数据 + self.computer_sampler_size() + # Whether to load unlabeled data self._with_unlabel = False self.datasets_len = dataset.cumulative_sizes[-1] self.computer_epoch() @@ -87,6 +85,15 @@ def __iter__(self) -> Iterator[int]: def __len__(self) -> int: return self.total_epoch * self._batch_size + def computer_sampler_size(self): + frist_size = int(self.sample_ratio[0] * self.batch_size) + if frist_size >= self.batch_size: + frist_size = self.batch_size - 1 + elif frist_size <= 0: + frist_size = 1 + + self.sample_size = [frist_size, self.batch_size - frist_size] + def computer_epoch(self): if self.with_unlabel: if self.round_up: @@ -103,7 +110,7 @@ def batch_size(self) -> int: @batch_size.setter def batch_size(self, batch: int) -> None: self._batch_size = batch - self.sample_size = [int(self.sample_ratio[0] * batch), batch - int(self.sample_ratio[0] * batch)] + self.computer_sampler_size() def set_seed(self, seed: int) -> None: # set seed @@ -122,29 +129,3 @@ def with_unlabel(self, unlabel: bool) -> None: def set_epoch(self, epoch: int) -> None: self.epoch = epoch - - -if __name__ == "__main__": - - class TestSet(Dataset): - def __init__(self, start=0, end=100) -> None: - super().__init__() - self.start = start - self.end = end - self.data = [i for i in range(start, end)] - - def __getitem__(self, index) -> Any: - return self.data[index] - - def __len__(self): - return len(self.data) - - datasets = ConcatDataset([TestSet(), TestSet(start=100, end=200)]) - batch = 8 - sampler = SemiSampler(datasets, batch, [2, 6]) - data_loader = DataLoader(dataset=datasets, batch_size=batch, sampler=sampler, num_workers=1) - for idx, data in enumerate(data_loader): - print(idx, data) - data_loader.sampler.with_unlabel = True - for idx, data in enumerate(data_loader): - print(idx, data) From 6d81510c66884f24b9c5906fef354458e704e8b1 Mon Sep 17 00:00:00 2001 From: mjq2020 Date: Sun, 19 Nov 2023 15:41:23 +0000 Subject: [PATCH 12/33] add: labelmatch hook --- sscma/engine/hooks/semihook.py | 159 ++++++++++++++++++++++++++++++++- 1 file changed, 156 insertions(+), 3 deletions(-) diff --git a/sscma/engine/hooks/semihook.py b/sscma/engine/hooks/semihook.py index 735335d8..d2d5a0bb 100644 --- a/sscma/engine/hooks/semihook.py +++ b/sscma/engine/hooks/semihook.py @@ -1,12 +1,17 @@ -from typing import Union +import json +from typing import Dict, Optional, Sequence, Union from mmengine.hooks import Hook from mmengine.runner import Runner from sscma.registry import HOOKS +import numpy as np +from tqdm.std import tqdm + +DATA_BATCH = Optional[Union[dict, tuple, list]] @HOOKS.register_module() -class SemidHook(Hook): +class SemiHook(Hook): """ """ def __init__(self, bure_epoch: Union[float, int] = 1) -> None: @@ -27,4 +32,152 @@ def before_run(self, runner: Runner) -> None: def before_train_epoch(self, runner: Runner) -> None: if self.bure_epoch == runner.epoch: # dataloader starts loading unlabeled dataset for semi-supervised training - runner.train_dataloader.sampler.with_unlabel = True + runner.train_dataloader.sampler.all_data = True + + +@HOOKS.register_module() +class LabelMatchHook(Hook): + def __init__( + self, + bure_epoch=0, + interval=10, + by_epoch=False, + percent=0.2, + ann_file: Optional[str] = None, + act_eval: bool = False, + ) -> None: + super().__init__() + self.by_epoch = by_epoch + self.interval = interval + self.percent = percent + self.bure_epoch = bure_epoch + self.computer_thr = False + self.posetive = 1000 + self.ann_file = ann_file + self.act_eval = act_eval + + def before_run(self, runner: Runner) -> None: + # computer bure epoch + if isinstance(self.bure_epoch, float): + self.bure_epoch = int(runner.max_epochs * self.bure_epoch) + + assert self.bure_epoch <= runner.max_epochs + # get label dataset classes + self.CLASSES: Union[tuple, list] = runner.train_dataloader.dataset.CLASSES + self.score_list = [[] for _ in range(len(self.CLASSES))] + # set ann file + if self.ann_file is None: + self.ann_file = runner.train_dataloader.dataset.sup_dataset.ann_file + # get label dataset distribution info + self.boxes_per_image_gt, self.cls_taio_gt = self.get_sup_distribution_info(self.ann_file) + # computer perclass postive number for unlabel dataset + self.per_cls_postive = ( + self.boxes_per_image_gt * self.cls_taio_gt * len(runner.train_dataloader.dataset.unsup_dataset) + ) + + def before_train_epoch(self, runner: Runner) -> None: + if self.bure_epoch == runner.epoch: + # dataloader starts loading unlabeled dataset for semi-supervised training + runner.train_dataloader.sampler.all_data = True + + def after_train_epoch(self, runner: Runner) -> None: + if not self.act_eval and self.every_n_epochs(runner, self.interval): + runner.train_dataloader.sampler.only_unlabel = True + self.test_dataset_result(runner.model, runner.train_dataloader) + self.parse_thr(runner) + runner.train_dataloader.sampler.all_data = True + + return super().after_train_epoch(runner) + + def get_sup_distribution_info(self, ann_file): + # computer sup dataset label distribudion info + with open(ann_file, 'r') as f: + data_info = json.load(f) + image_num = len(data_info['images']) + cls_num = [0] * len(self.CLASSES) + catid2index = {} + for value in data_info['categories']: + catid2index[value['id']] = self.CLASSES.index(value['name']) + + for value in data_info['annotations']: + cls_num[catid2index[value['category_id']]] += 1 + total_boxes = sum(cls_num) + per_cls_gt_tatio = np.asarray([num / total_boxes for num in cls_num]) + + per_image_gt_boxes = sum(cls_num) / image_num + + return per_image_gt_boxes, per_cls_gt_tatio + + def test_dataset_result(self, model, dataload): + self.score_list = [[] for _ in range(len(self.CLASSES))] + for data in tqdm(dataload, desc="computer thr", ncols=80): + batch_results = model.val_step( + {"inputs": data['inputs']['unsup_teacher'], 'data_samples': data['data_samples']['unsup_teacher']} + ) + self.parse_val_result(batch_results) + + def parse_val_result(self, results): + for out in results: + for idx, label in enumerate(out.pred_instances.labels): + self.score_list[int(label)].append(float(out.pred_instances.scores[idx])) + + def parse_thr(self, runner: Runner): + self.score_list = [sorted(cl, reverse=True) if len(cl) else [0] for cl in self.score_list] + cls_thr = [0] * len(runner.train_dataloader.dataset.CLASSES) + cls_thr_ig = [0] * len(runner.train_dataloader.dataset.CLASSES) + for idx, scores in enumerate(self.score_list): + cls_thr[idx] = max(0.05, scores[min(len(scores) - 1, int(self.per_cls_postive[idx] * self.percent))]) + cls_thr_ig[idx] = max(0.05, scores[min(len(scores) - 1, int(self.per_cls_postive[idx]))]) + + runner.model.pseudo_label_creator.cls_thr = cls_thr + runner.model.pseudo_label_creator.cls_thr_ig = cls_thr_ig + + self.score_list = [i.clear() for i in self.score_list] + + def before_val_epoch(self, runner: Runner) -> None: + if self.act_eval and self.every_n_epochs(runner, self.interval): + self.computer_thr = True + + def after_val_epoch(self, runner, metrics: Optional[Dict[str, float]] = None) -> None: + if self.computer_thr: + self.computer_thr = False + self.parse_thr(runner) + + def after_val_iter( + self, runner: Runner, batch_idx: int, data_batch: DATA_BATCH = None, outputs: Optional[Sequence] = None + ) -> None: + if self.computer_thr: + self.parse_val_result(outputs) + + return super().after_val_iter(runner, batch_idx, data_batch, outputs) + + def every_n_epochs(self, runner, n: int) -> bool: + """Test whether current epoch can be evenly divided by n. + + Args: + runner (Runner): The runner of the training, validation or testing + process. + n (int): Whether current epoch can be evenly divided by n. + + Returns: + bool: Whether current epoch can be evenly divided by n. + """ + if runner.epoch + 1 <= self.bure_epoch: + return False + return (runner.epoch + 1 - self.bure_epoch) % n == 0 if n > 0 else False + + def every_n_train_iters(self, runner: Runner, n: int) -> bool: + """Test whether current training iteration can be evenly divided by n. + + Args: + runner (Runner): The runner of the training, validation or testing + process. + n (int): Whether current iteration can be evenly divided by n. + + Returns: + bool: Return True if the current iteration can be evenly divided + by n, otherwise False. + """ + if runner.epoch + 1 <= self.bure_epoch: + return False + return (runner.iter + 1 - (len(runner.train_dataloader) * self.bure_epoch)) % n == 0 if n > 0 else False From 65fc5f931681f810381cfac26c24e38f95c3f0fd Mon Sep 17 00:00:00 2001 From: mjq2020 Date: Sun, 19 Nov 2023 15:46:05 +0000 Subject: [PATCH 13/33] optimize: ssod dataset sampler --- sscma/datasets/sampler.py | 67 +++++++++++++++++++++++++++++---------- 1 file changed, 51 insertions(+), 16 deletions(-) diff --git a/sscma/datasets/sampler.py b/sscma/datasets/sampler.py index 1fabdfff..3273beea 100644 --- a/sscma/datasets/sampler.py +++ b/sscma/datasets/sampler.py @@ -1,4 +1,4 @@ -from typing import Iterator, List, Optional, Any, Union +from typing import Iterator, List, Optional, Union import torch import random @@ -55,30 +55,40 @@ def __init__( self.data2 = data_index[dataset.cumulative_sizes[0] :] self.computer_sampler_size() # Whether to load unlabeled data - self._with_unlabel = False + self._only_label = True + self._only_unlabel = False + self._all_data = False + self.labels = ['_only_label', '_only_unlabel', '_all_data'] self.datasets_len = dataset.cumulative_sizes[-1] + self.computer_epoch() def __iter__(self) -> Iterator[int]: indexs = [] num1 = 0 num2 = 0 + data1_len = len(self.data1) + data2_len = len(self.data2) if self.shuffle: np.random.shuffle(self.data1) np.random.shuffle(self.data2) - for i in range(self.total_epoch): - if self.with_unlabel: + for _ in range(self.total_epoch): + if self.all_data: for _ in range(self.sample_size[0]): - indexs.append(self.data1[num1 % len(self.data1)]) + indexs.append(self.data1[num1 % data1_len]) num1 += 1 for _ in range(self.sample_size[1]): - indexs.append(self.data2[num2 % len(self.data2)]) + indexs.append(self.data2[num2 % data2_len]) num2 += 1 - else: + elif self.only_label: for _ in range(self.sample_size[0] + self.sample_size[1]): - indexs.append(self.data1[num1 % len(self.data1)]) + indexs.append(self.data1[num1 % data1_len]) num1 += 1 + else: + for _ in range(self.sample_size[0] + self.sample_size[1]): + indexs.append(self.data2[num2 % data2_len]) + num2 += 1 return iter(indexs) @@ -95,13 +105,15 @@ def computer_sampler_size(self): self.sample_size = [frist_size, self.batch_size - frist_size] def computer_epoch(self): - if self.with_unlabel: + if self.all_data: if self.round_up: self.total_epoch = max(len(self.data1) // self.sample_size[0], len(self.data2) // self.sample_size[1]) else: self.total_epoch = min(len(self.data1) // self.sample_size[0], len(self.data2) // self.sample_size[1]) - else: + elif self.only_label: self.total_epoch = len(self.data1) // sum(self.sample_size) + else: + self.total_epoch = len(self.data2) // sum(self.sample_size) @property def batch_size(self) -> int: @@ -118,14 +130,37 @@ def set_seed(self, seed: int) -> None: torch.manual_seed(seed) random.seed(seed) + def set_label(self, attr: str, flag): + for i in self.labels: + if attr in i: + setattr(self, i, flag) + else: + setattr(self, i, not flag) + self.computer_epoch() + @property - def with_unlabel(self) -> bool: - return self._with_unlabel + def only_label(self): + return self._only_label - @with_unlabel.setter - def with_unlabel(self, unlabel: bool) -> None: - self._with_unlabel = unlabel - self.computer_epoch() + @only_label.setter + def only_label(self, flag: bool): + self.set_label('only_label', flag) + + @property + def only_unlabel(self): + return self._only_unlabel + + @only_label.setter + def only_unlabel(self, flag: bool): + self.set_label('only_unlabel', flag) + + @property + def all_data(self): + return self._all_data + + @all_data.setter + def all_data(self, flag: bool): + self.set_label('all_data', flag) def set_epoch(self, epoch: int) -> None: self.epoch = epoch From 32584740c5cdef7f1b2369e899965c5759bc78f0 Mon Sep 17 00:00:00 2001 From: mjq2020 Date: Sun, 19 Nov 2023 15:48:10 +0000 Subject: [PATCH 14/33] fix: CI error --- sscma/engine/hooks/__init__.py | 2 +- sscma/engine/runner/loops.py | 5 ++--- sscma/utils/check.py | 2 +- sscma/utils/inference.py | 2 +- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/sscma/engine/hooks/__init__.py b/sscma/engine/hooks/__init__.py index a8d15505..d2ec9102 100644 --- a/sscma/engine/hooks/__init__.py +++ b/sscma/engine/hooks/__init__.py @@ -5,7 +5,7 @@ WandbLoggerHook, ) from .visualization_hook import DetFomoVisualizationHook, Posevisualization -from .semihook import SemidHook +from .semihook import SemiHook __all__ = [ 'TextLoggerHook', diff --git a/sscma/engine/runner/loops.py b/sscma/engine/runner/loops.py index f192f8c1..d1f359a6 100644 --- a/sscma/engine/runner/loops.py +++ b/sscma/engine/runner/loops.py @@ -1,9 +1,8 @@ -from typing import Dict, List, Optional, Sequence, Tuple, Union -from mmengine.evaluator import Evaluator +from typing import Dict, List, Sequence, Union import onnx import torch -from mmengine.evaluator.evaluator import Evaluator +from mmengine.evaluator import Evaluator from mmengine.runner import Runner, ValLoop from mmengine.runner.loops import BaseLoop, EpochBasedTrainLoop from mmengine.model import is_model_wrapper diff --git a/sscma/utils/check.py b/sscma/utils/check.py index c8db9ca9..771f418a 100644 --- a/sscma/utils/check.py +++ b/sscma/utils/check.py @@ -37,7 +37,7 @@ def install_lib(name: Union[str, Iterable[str]], version: Optional[Union[str, It print(f"Third-party library {name} being installed") subprocess.check_output(f'pip install --no-cache {name}', shell=True).decode() return True - except Exception as e: + except Exception: print(f"Warning ⚠️: Installation of {name} has failed, the installation process has been skipped") return False diff --git a/sscma/utils/inference.py b/sscma/utils/inference.py index ad9f62ee..17fb1f89 100644 --- a/sscma/utils/inference.py +++ b/sscma/utils/inference.py @@ -386,7 +386,7 @@ def test(self) -> None: show_point(points, img=img, labels=texts, show=self.show, img_file=img_path) if not self.source: ori_shape = data['data_samples'][0].ori_shape - bboxes = data['data_samples'][0].gt_instances + # bboxes = data['data_samples'][0].gt_instances # target = build_target(preds.shape[1:], (H*8, W*8), bboxes) target = torch.as_tensor(data['data_samples'][0].fomo_mask[0]) From 01e7055ebba95603f590f40da2ebbcb112ee5a08 Mon Sep 17 00:00:00 2001 From: mjq2020 Date: Sun, 19 Nov 2023 15:50:19 +0000 Subject: [PATCH 15/33] add: ssod base class --- sscma/models/detectors/base.py | 178 ++++++++++++++++++++++++++++++++ sscma/models/semi/__init__.py | 5 + sscma/models/semi/base.py | 31 ++++++ sscma/models/semi/labelmatch.py | 41 ++++++++ 4 files changed, 255 insertions(+) create mode 100644 sscma/models/detectors/base.py create mode 100644 sscma/models/semi/__init__.py create mode 100644 sscma/models/semi/base.py create mode 100644 sscma/models/semi/labelmatch.py diff --git a/sscma/models/detectors/base.py b/sscma/models/detectors/base.py new file mode 100644 index 00000000..010b4167 --- /dev/null +++ b/sscma/models/detectors/base.py @@ -0,0 +1,178 @@ +from typing import Dict, Optional, Union +import copy + +from mmdet.models.detectors import BaseDetector, SemiBaseDetector +from mmdet.utils import OptConfigType, OptMultiConfig, ConfigType +from mmdet.models.utils import rename_loss_dict, reweight_loss_dict +from mmdet.structures import SampleList, OptSampleList +import torch +from torch import Tensor +from mmengine.optim import OptimWrapper + +from sscma.registry import MODELS +from sscma.models.semi import BasePseudoLabelCreator + + +@MODELS.register_module() +class BaseSsod(SemiBaseDetector): + teacher: BaseDetector + student: BaseDetector + + def __init__( + self, + detector: ConfigType, + pseudo_label_cfg: ConfigType, + teacher_loss_weight: int, + semi_train_cfg: OptConfigType = None, + semi_test_cfg: OptConfigType = None, + data_preprocessor: OptConfigType = None, + init_cfg: OptMultiConfig = None, + ) -> None: + super().__init__(detector, semi_train_cfg, semi_test_cfg, data_preprocessor, init_cfg) + self.pseudo_label_creator: BasePseudoLabelCreator = MODELS.build(pseudo_label_cfg) + + self.teacher_loss_weight = teacher_loss_weight + + def train_step(self, data: Union[dict, tuple, list], optim_wrapper: OptimWrapper) -> Dict[str, Tensor]: + """Implements the default model training process including + preprocessing, model forward propagation, loss calculation, + optimization, and back-propagation. + + During non-distributed training. If subclasses do not override the + :meth:`train_step`, :class:`EpochBasedTrainLoop` or + :class:`IterBasedTrainLoop` will call this method to update model + parameters. The default parameter update process is as follows: + + 1. Calls ``self.data_processor(data, training=False)`` to collect + batch_inputs and corresponding data_samples(labels). + 2. Calls ``self(batch_inputs, data_samples, mode='loss')`` to get raw + loss + 3. Calls ``self.parse_losses`` to get ``parsed_losses`` tensor used to + backward and dict of loss tensor used to log messages. + 4. Calls ``optim_wrapper.update_params(loss)`` to update model. + + Args: + data (dict or tuple or list): Data sampled from dataset. + optim_wrapper (OptimWrapper): OptimWrapper instance + used to update model parameters. + + Returns: + Dict[str, torch.Tensor]: A ``dict`` of tensor for logging. + """ + # Enable automatic mixed precision training context. + with optim_wrapper.optim_context(self): + if 'unsup_student' in data['inputs'] and not any( + [True for i in data['inputs']['unsup_student'] if isinstance(i, torch.Tensor)] + ): + data['inputs'].pop('unsup_student') + data['inputs'].pop('unsup_teacher') + data['data_samples'].pop('unsup_teacher') + data['data_samples'].pop('unsup_student') + elif 'sup' in data['inputs'] and not any([True for i in data['inputs']['sup'] if isinstance(i, Tensor)]): + data['inputs'].pop('sup') + data['inputs'].pop('sup') + data['data_samples'].pop('sup') + data['data_samples'].pop('sup') + + data = self.data_preprocessor(data, True) + + losses = self._run_forward(data, mode='loss') # type: ignore + parsed_losses, log_vars = self.parse_losses(losses) # type: ignore + optim_wrapper.update_params(parsed_losses) + return log_vars + + def val_step(self, data: Union[tuple, dict, list]) -> list: + """Gets the predictions of given data. + + Calls ``self.data_preprocessor(data, False)`` and + ``self(inputs, data_sample, mode='predict')`` in order. Return the + predictions which will be passed to evaluator. + + Args: + data (dict or tuple or list): Data sampled from dataset. + + Returns: + list: The predictions of given data. + """ + # if 'unsup_student' in data['inputs'] and not any( + # [True for i in data['inputs']['unsup_student'] if isinstance(i, torch.Tensor)] + # ): + # data['inputs'].pop('unsup_student') + # data['inputs'].pop('unsup_teacher') + # data['data_samples'].pop('unsup_teacher') + # data['data_samples'].pop('unsup_student') + # elif 'sup' in data['inputs'] and not any([True for i in data['inputs']['sup'] if isinstance(i, Tensor)]): + # data['inputs'].pop('sup') + # data['data_samples'].pop('sup') + + # print(data['inputs'][0].shape) + data = self.data_preprocessor(data, False) + # print(data['inputs'][0].shape) + return self._run_forward(data, mode='predict') # type: ignore + + def loss(self, multi_batch_inputs: Dict[str, Tensor], multi_batch_data_samples: Dict[str, SampleList]) -> dict: + """Calculate losses from multi-branch inputs and data samples. + + Args: + multi_batch_inputs (Dict[str, Tensor]): The dict of multi-branch + input images, each value with shape (N, C, H, W). + Each value should usually be mean centered and std scaled. + multi_batch_data_samples (Dict[str, List[:obj:`DetDataSample`]]): + The dict of multi-branch data samples. + + Returns: + dict: A dictionary of loss components + """ + if multi_batch_inputs.get('unsup_teacher', None) is not None: + # Semi-supervised training process + with torch.no_grad(): + teacher_pred = self.teacher.predict( + multi_batch_inputs['unsup_teacher'], multi_batch_data_samples['unsup_student'] + ) + # Generate pseudo labels + self.pseudo_label_creator.generate_pseudo_labels_online( + teacher_pred, + copy.copy(multi_batch_data_samples['unsup_student']), + ) + + losses = dict() + # Supervision part + losses.update(**self.loss_by_gt_instances(multi_batch_inputs['sup'], multi_batch_data_samples['sup'])) + # Semi-supervised part + losses.update( + **self.loss_by_pseudo_instances( + multi_batch_inputs['unsup_student'], multi_batch_data_samples['unsup_student'] + ) + ) + else: + # Supervise training process + losses = self.loss_by_gt_instances(multi_batch_inputs['sup'], multi_batch_data_samples['sup']) + return losses + + def loss_by_pseudo_instances( + self, batch_inputs: Tensor, batch_data_samples: SampleList, batch_info: Optional[dict] = None + ) -> dict: + """Calculate losses from a batch of inputs and pseudo data samples. + + Args: + batch_inputs (Tensor): Input images of shape (N, C, H, W). + These should usually be mean centered and std scaled. + batch_data_samples (List[:obj:`DetDataSample`]): The batch + data samples. It usually includes information such + as `gt_instance` or `gt_panoptic_seg` or `gt_sem_seg`, + which are `pseudo_instance` or `pseudo_panoptic_seg` + or `pseudo_sem_seg` in fact. + batch_info (dict): Batch information of teacher model + forward propagation process. Defaults to None. + + Returns: + dict: A dictionary of loss components + """ + losses = self.student.loss(batch_inputs, batch_data_samples) + pseudo_instances_num = sum( + [len(data_samples.gt_instances) for data_samples in batch_data_samples] + + [len(data_samples.ignored_instances) for data_samples in batch_data_samples] + ) + unsup_weight = self.semi_train_cfg.get('unsup_weight', 1.0) if pseudo_instances_num > 0 else 0.0 + + return rename_loss_dict('unsup_', reweight_loss_dict(losses, unsup_weight)) diff --git a/sscma/models/semi/__init__.py b/sscma/models/semi/__init__.py new file mode 100644 index 00000000..39b0c619 --- /dev/null +++ b/sscma/models/semi/__init__.py @@ -0,0 +1,5 @@ +from .base import BasePseudoLabelCreator +from .labelmatch import LabelMatch +from .fairpseudolabel import FairPseudoLabel + +__all__ = ['BasePseudoLabelCreator','LabelMatch', 'FairPseudoLabel'] diff --git a/sscma/models/semi/base.py b/sscma/models/semi/base.py new file mode 100644 index 00000000..666e6ac4 --- /dev/null +++ b/sscma/models/semi/base.py @@ -0,0 +1,31 @@ +from abc import abstractmethod + +from mmdet.utils import InstanceList +from mmdet.structures import SampleList, OptSampleList +import torch.nn as nn + +from sscma.registry import MODELS + + +@MODELS.register_module() +class BasePseudoLabelCreator(nn.Module): + def __init__(self, *args, **kwargs) -> None: + super().__init__(*args, **kwargs) + + @abstractmethod + def generate_pseudo_labels_online_( + self, teach_pred: InstanceList, student_samples: SampleList, teacher_samples: OptSampleList = None + ): + """ + Online generation of pseudo-labels through correlation strategies + Args: + teacher_pred (InstanceList): Prediction results obtained after the unlabeled image + passes through the teacher model + student_samples (SampleList): Relevant information that needs to be input to the + student model image sampling,This parameter needs to be passed through shallow + copy. After the pseudo label is generated, the generated pseudo label will be + passed to this parameter. + teacher_samples (OptSampleList): Information related to the sampling entered into + the teacher model image + Returns: None + """ diff --git a/sscma/models/semi/labelmatch.py b/sscma/models/semi/labelmatch.py new file mode 100644 index 00000000..08dbcafb --- /dev/null +++ b/sscma/models/semi/labelmatch.py @@ -0,0 +1,41 @@ +import torch +from mmdet.structures.det_data_sample import SampleList, OptSampleList +from mmengine.structures.instance_data import InstanceData +from sscma.models.detectors.base import BasePseudoLabelCreator +from sscma.registry import MODELS + + +@MODELS.register_module() +class LabelMatch(BasePseudoLabelCreator): + def __init__(self, *args, **kwargs) -> None: + super().__init__(*args, **kwargs) + + self.cls_thr = None + self.cls_thr_ig = None + + def generate_pseudo_labels_online( + self, + teach_pred, + student_sample: SampleList, + ): + if self.cls_thr is not None and self.cls_thr_ig is not None: + for idx_sample, pred in enumerate(teach_pred): + pseudo_bboxs, pseudo_label = [], [] + pseudo_bboxs_ig, pseudo_label_ig = [], [] + gt = InstanceData() + ignore = InstanceData() + for idx, label in enumerate(pred.pred_instances.labels): + if pred.pred_instances.scores[idx] > self.cls_thr[int(label)]: + pseudo_bboxs.append(pred.pred_instances.bboxes[idx]) + pseudo_label.append(label) + elif pred.pred_instances.scores[idx] > self.cls_thr_ig[int(label)]: + pseudo_bboxs_ig.append(pred.pred_instances.bboxes[idx]) + pseudo_label_ig.append(label) + if len(pseudo_bboxs): + gt.bboxes = torch.concat([i.unsqueeze(0) for i in pseudo_bboxs], dim=0) + gt.labels = torch.concat([i.unsqueeze(0) for i in pseudo_label], dim=0) + if len(pseudo_bboxs_ig): + ignore.labels = torch.concat([i.unsqueeze(0) for i in pseudo_label_ig], dim=0) + ignore.bboxes = torch.concat([i.unsqueeze(0) for i in pseudo_bboxs_ig], dim=0) + student_sample[idx_sample].gt_instannces = gt + student_sample[idx_sample].ignored_instances = ignore From fefa8b04e89984d31583673544804109ea208be7 Mon Sep 17 00:00:00 2001 From: mjq2020 Date: Sun, 19 Nov 2023 15:56:23 +0000 Subject: [PATCH 16/33] add: project init file --- sscma/datasets/__init__.py | 3 ++ sscma/datasets/transforms/wrappers.py | 42 +++++++++++++++++++++++++++ sscma/models/__init__.py | 2 ++ sscma/models/detectors/__init__.py | 4 ++- 4 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 sscma/datasets/transforms/wrappers.py diff --git a/sscma/datasets/__init__.py b/sscma/datasets/__init__.py index 9fd3074c..2ceae00d 100644 --- a/sscma/datasets/__init__.py +++ b/sscma/datasets/__init__.py @@ -10,3 +10,6 @@ from .vocdataset import * # noqa from .yolodataset import * # noqa from .vww import * # noqa +from .sampler import * # noqa +from .unsupdataset import * # noqa +from .dataset_wrappers import * # noqa diff --git a/sscma/datasets/transforms/wrappers.py b/sscma/datasets/transforms/wrappers.py new file mode 100644 index 00000000..33845e91 --- /dev/null +++ b/sscma/datasets/transforms/wrappers.py @@ -0,0 +1,42 @@ +from typing import Dict, List, Optional, Tuple, Union +from mmcv.transforms import BaseTransform, Compose +import copy +from sscma.registry import TRANSFORMS + + +@TRANSFORMS.register_module() +class MutiBranchPipe(BaseTransform): + def __init__(self, branch_field, piece_key: str = None, **branch_pipelines) -> None: + self.branch_field = branch_field + self.branch_pipelines = {branch: Compose(pipeline) for branch, pipeline in branch_pipelines.items()} + self.piece_key = piece_key + + def transform(self, results: Dict) -> Optional[Union[Dict, Tuple[List, List]]]: + multi_results = {} + for branch in self.branch_field: + multi_results[branch] = {'inputs': None, 'data_samples': None} + for branch, pipeline in self.branch_pipelines.items(): + if branch == self.piece_key: + results['img'] + branch_results = pipeline(branch_results) + else: + branch_results = pipeline(copy.deepcopy(results)) + # If one branch pipeline returns None, + # it will sample another data from dataset. + if branch_results is None: + return None + multi_results[branch] = branch_results + + format_results = {} + for branch, results in multi_results.items(): + for key in results.keys(): + if format_results.get(key, None) is None: + format_results[key] = {branch: results[key]} + else: + format_results[key][branch] = results[key] + return format_results + + def __repr__(self) -> str: + repr_str = self.__class__.__name__ + repr_str += f'(branch_pipelines={list(self.branch_pipelines.keys())})' + return repr_str diff --git a/sscma/models/__init__.py b/sscma/models/__init__.py index 88256e30..cd7a2dd3 100644 --- a/sscma/models/__init__.py +++ b/sscma/models/__init__.py @@ -4,3 +4,5 @@ from .heads import * # noqa from .losses import * # noqa from .necks import * # noqa +from .utils import * # noqa +from .semi import * # noqa diff --git a/sscma/models/detectors/__init__.py b/sscma/models/detectors/__init__.py index 82f2cdb9..063e8ed0 100644 --- a/sscma/models/detectors/__init__.py +++ b/sscma/models/detectors/__init__.py @@ -1,5 +1,7 @@ from .fastestdet import FastestDet from .fomo import Fomo from .pfld import PFLD +from .semidetect import EfficientTeacher +from .base import BaseSsod -__all__ = ['PFLD', 'FastestDet', 'Fomo'] +__all__ = ['PFLD', 'FastestDet', 'Fomo', 'EfficientTeacher', 'BaseSsod'] From 44d961e88e9dabbd89800154664f4afb5fd29f8e Mon Sep 17 00:00:00 2001 From: mjq2020 Date: Sun, 19 Nov 2023 16:06:04 +0000 Subject: [PATCH 17/33] add: labelmatch model config --- configs/yolov5/swift_yolo_labelmatch_coco.py | 429 +++++++++++++++++++ 1 file changed, 429 insertions(+) create mode 100644 configs/yolov5/swift_yolo_labelmatch_coco.py diff --git a/configs/yolov5/swift_yolo_labelmatch_coco.py b/configs/yolov5/swift_yolo_labelmatch_coco.py new file mode 100644 index 00000000..450bf570 --- /dev/null +++ b/configs/yolov5/swift_yolo_labelmatch_coco.py @@ -0,0 +1,429 @@ +_base_ = ['../_base_/default_runtime_det.py'] +default_scope = 'mmyolo' + +# ========================Suggested optional parameters======================== +# DATA +# Dataset type, this will be used to define the dataset +# dataset_type = 'mmdet.CocoDataset' +supdataset_type = 'sscma.CustomYOLOv5CocoDataset' +unsupdataset_type = 'sscma.UnsupDataset' +# unsupdataset_type='sscma.CustomYOLOv5CocoDataset' +# Path to the dataset's root directory +# dataset link: https://universe.roboflow.com/team-roboflow/coco-128 +data_root = 'https://universe.roboflow.com/ds/z5UOcgxZzD?key=bwx9LQUT0t' + +# Path of train annotation file +# train_ann = 'train/_annotations.coco.json' +train_ann = 'annotations/train_sup1.json' +# Prefix of train image path +train_data = 'train2017/' +# Path of val annotation file +# val_ann = 'valid/_annotations.coco.json' +val_ann = 'annotations/instances_val2017.json' +# Prefix of val image path +val_data = 'val2017/' +# Height of the model input data +height = 640 +# Width of the model input data +width = 640 +# The width and height of the model input data +imgsz = (width, height) # width, height + +# MODEL +# The scaling factor that controls the depth of the network structure +deepen_factor = 0.33 +# The scaling factor that controls the width of the network structure +widen_factor = 0.5 +# Number of classes for classification +num_classes = 80 + +# TRAIN +# Base learning rate for optim_wrapper. Corresponding to 8xb16=128 bs +lr = 0.01 +# Total number of rounds of model training +epochs = 500 +# Number of input data per iteration in the model training phase +batch = 32 +# Number of threads used to load data during training, this value should be adjusted accordingly to the training batch +workers = 1 +# Optimizer weight decay value +weight_decay = 0.0005 +# SGD momentum/Adam beta1 +momentum = 0.937 +# Learning rate scaling factor +lr_factor = 0.01 +# persistent_workers must be False if num_workers is 0 +persistent_workers = True + +# VAL +# Batch size of a single GPU during validation +val_batch = 16 +# Worker to pre-fetch data for each single GPU during validation +val_workers = 2 +# Save model checkpoint and validation intervals +val_interval = 1 +# Model weight saving interval in epochs +save_interval = val_interval +# The maximum checkpoints to keep. +max_keep_ckpts = 3 +# ================================END================================= + +model_test_cfg = dict( + # The config of multi-label for multi-class prediction. + multi_label=True, + # The number of boxes before NMS + nms_pre=30000, + score_thr=0.1, # Threshold to filter out boxes. + nms=dict(type='nms', iou_threshold=0.65), # NMS type and threshold + max_per_img=300, +) # Max number of detections of each image + + +# Strides of multi-scale prior box +strides = [8, 16, 32] +num_det_layers = 3 # The number of model output scales +norm_cfg = dict(type='BN', momentum=0.03, eps=0.001) # Normalization config + +anchors = [ + [(10, 13), (16, 30), (33, 23)], # P3/8 + [(30, 61), (62, 45), (59, 119)], # P4/16 + [(116, 90), (156, 198), (373, 326)], # P5/32 +] + +# -----train val related----- +affine_scale = 0.5 # YOLOv5RandomAffine scaling ratio +loss_cls_weight = 0.5 +loss_bbox_weight = 0.05 +loss_obj_weight = 1.0 +prior_match_thr = 4.0 # Priori box matching threshold +# The obj loss weights of the three output layers +obj_level_weights = [4.0, 1.0, 0.4] + +# Single-scale training is recommended to +# be turned on, which can speed up training. +env_cfg = dict(cudnn_benchmark=True) + +# model arch +detector = dict( + type='mmyolo.YOLODetector', + data_preprocessor=dict( + type='mmdet.DetDataPreprocessor', mean=[0.0, 0.0, 0.0], std=[255.0, 255.0, 255.0], bgr_to_rgb=True + ), + backbone=dict( + type='YOLOv5CSPDarknet', + deepen_factor=deepen_factor, + widen_factor=widen_factor, + norm_cfg=norm_cfg, + act_cfg=dict(type='ReLU', inplace=True), + ), + neck=dict( + type='YOLOv5PAFPN', + deepen_factor=deepen_factor, + widen_factor=widen_factor, + in_channels=[256, 512, 1024], + out_channels=[256, 512, 1024], + num_csp_blocks=3, + norm_cfg=norm_cfg, + act_cfg=dict(type='ReLU', inplace=True), + ), + bbox_head=dict( + type='sscma.YOLOV5Head', + head_module=dict( + type='sscma.DetHead', + num_classes=num_classes, + in_channels=[256, 512, 1024], + widen_factor=widen_factor, + featmap_strides=strides, + num_base_priors=3, + ), + prior_generator=dict(type='mmdet.YOLOAnchorGenerator', base_sizes=anchors, strides=strides), + # scaled based on number of detection layers + loss_cls=dict(type='mmdet.CrossEntropyLoss', use_sigmoid=True, reduction='mean', loss_weight=loss_cls_weight), + loss_bbox=dict( + type='IoULoss', + iou_mode='ciou', + bbox_format='xywh', + eps=1e-7, + reduction='mean', + loss_weight=loss_bbox_weight, + return_iou=True, + ), + loss_obj=dict(type='mmdet.CrossEntropyLoss', use_sigmoid=True, reduction='mean', loss_weight=loss_obj_weight), + prior_match_thr=prior_match_thr, + obj_level_weights=obj_level_weights, + ), + test_cfg=model_test_cfg, + # _delete_=True +) +model = dict( + type="sscma.BaseSsod", + detector=detector, + pseudo_label_cfg=dict( + type='sscma.LabelMatch', + # cfg=dict( + # multi_label=False, + # conf_thres=0.1, + # iou_thres=0.65, + # ignore_thres_high=0.6, + # ignore_thres_low=0.1, + # resample_high_percent=0.25, + # resample_low_percent=0.99, + # data_names=('person',), + # data_np=0, + # ), + # target_data_len=10, + # label_num_per_img=10, + # nc=80, + ), + teacher_loss_weight=0, + # da_loss_weight=0, + data_preprocessor=dict(type="mmdet.MultiBranchDataPreprocessor", data_preprocessor=detector['data_preprocessor']), + # data_preprocessor=dict( + # type='mmdet.DetDataPreprocessor', mean=[0.0, 0.0, 0.0], std=[255.0, 255.0, 255.0], bgr_to_rgb=True + # ), + semi_train_cfg=dict( + freeze_teacher=True, + sup_weight=1.0, + unsup_weight=4.0, + pseudo_label_initial_score_thr=0.5, + rpn_pseudo_thr=0.9, + cls_pseudo_thr=0.9, + reg_pseudo_thr=0.02, + jitter_times=10, + jitter_scale=0.06, + ), + semi_test_cfg=dict(predict_on='teacher'), + # _delete_=True +) + +color_space = [ + [dict(type='mmdet.ColorTransform')], + [dict(type='mmdet.AutoContrast')], + [dict(type='mmdet.Equalize')], + [dict(type='mmdet.Sharpness')], + [dict(type='mmdet.Posterize')], + [dict(type='mmdet.Solarize')], + [dict(type='mmdet.Color')], + [dict(type='mmdet.Contrast')], + [dict(type='mmdet.Brightness')], +] + +# geometric = [ +# [dict(type='mmdet.Rotate')], +# [dict(type='mmdet.ShearX')], +# [dict(type='mmdet.ShearY')], +# [dict(type='mmdet.TranslateX')], +# [dict(type='mmdet.TranslateY')], +# ] +pre_transform = [ + dict(type='LoadImageFromFile', file_client_args=None), + dict(type='LoadAnnotations', with_bbox=True), +] + + +test_pipeline = [ + dict(type='LoadImageFromFile', file_client_args=dict(backend='disk')), + dict(type='YOLOv5KeepRatioResize', scale=imgsz), + dict(type='LetterResize', scale=imgsz, allow_scale_up=False, pad_val=dict(img=114)), + dict(type='LoadAnnotations', with_bbox=True, _scope_='mmdet'), + dict( + type='mmdet.PackDetInputs', + meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape', 'scale_factor', 'pad_param'), + ), +] + +# pipeline used to augment unlabeled data weakly, +# which will be sent to teacher model for predicting pseudo instances. +weak_pipeline = [ + # dict(type='Mosaic', img_scale=imgsz, pad_val=114.0, pre_transform=pre_transform), + dict(type='LetterResize', scale=imgsz, allow_scale_up=False, pad_val=dict(img=114)), + dict(type='mmdet.RandomFlip', prob=0.5), + dict( + type='mmdet.PackDetInputs', + meta_keys=( + 'img_id', + 'img_path', + 'ori_shape', + 'img_shape', + 'scale_factor', + 'flip', + 'flip_direction', + 'homography_matrix', + ), + ), +] +# pipeline used to augment unlabeled data strongly, +# which will be sent to student model for unsupervised training. +strong_pipeline = [ + # dict(type='Mosaic', img_scale=imgsz, pad_val=114.0, pre_transform=pre_transform), + dict(type='LetterResize', scale=imgsz, allow_scale_up=False, pad_val=dict(img=114)), + # dict(type='mmdet.RandomFlip', prob=0.5), + dict( + type='mmdet.RandomOrder', + transforms=[ + dict(type='mmdet.RandAugment', aug_space=color_space, aug_num=1), + # dict(type='mmdet.RandAugment', aug_space=geometric, aug_num=1), + ], + ), + dict(type='mmdet.RandomErasing', n_patches=(1, 5), ratio=(0, 0.2)), + dict(type='mmdet.FilterAnnotations', min_gt_bbox_wh=(1e-2, 1e-2)), + dict( + type='mmdet.PackDetInputs', + meta_keys=( + 'img_id', + 'img_path', + 'ori_shape', + 'img_shape', + 'scale_factor', + # 'flip', + # 'flip_direction', + 'homography_matrix', + ), + ), +] +sup_branch_field = ['sup', 'unsup_teacher', 'unsup_student'] +unsup_branch_field = ['sup', 'unsup_teacher', 'unsup_student'] +# pipeline used to augment labeled data, +# which will be sent to student model for supervised training. +sup_pipeline = [ + *pre_transform, + dict(type='Mosaic', img_scale=imgsz, pad_val=114.0, pre_transform=pre_transform), + dict(type='LetterResize', scale=imgsz, allow_scale_up=False, pad_val=dict(img=114)), + # dict(type='mmdet.RandomResize', scale=imgsz, keep_ratio=True), + dict(type='mmdet.RandomFlip', prob=0.5), + dict(type='mmdet.RandAugment', aug_space=color_space, aug_num=1), + dict(type='mmdet.FilterAnnotations', min_gt_bbox_wh=(1e-2, 1e-2)), + dict(type='mmdet.MultiBranch', branch_field=sup_branch_field, sup=dict(type='mmdet.PackDetInputs')), +] +# pipeline used to augment unlabeled data into different views +unsup_pipeline = [ + *pre_transform, + dict( + type='mmdet.MultiBranch', + branch_field=unsup_branch_field, + unsup_teacher=weak_pipeline, + unsup_student=strong_pipeline, + ), +] +albu_train_transforms = [ + dict(type='Blur', p=0.01), + dict(type='MedianBlur', p=0.01), + dict(type='ToGray', p=0.01), + dict(type='CLAHE', p=0.01), +] +train_pipeline = [ + *pre_transform, + dict(type='Mosaic', img_scale=imgsz, pad_val=114.0, pre_transform=pre_transform), + dict( + type='YOLOv5RandomAffine', + max_rotate_degree=0.0, + max_shear_degree=0.0, + scaling_ratio_range=(1 - affine_scale, 1 + affine_scale), + # imgsz is (width, height) + border=(-imgsz[0] // 2, -imgsz[1] // 2), + border_val=(114, 114, 114), + ), + dict( + type='mmdet.Albu', + transforms=albu_train_transforms, + bbox_params=dict(type='BboxParams', format='pascal_voc', label_fields=['gt_bboxes_labels', 'gt_ignore_flags']), + keymap={'img': 'image', 'gt_bboxes': 'bboxes'}, + ), + dict(type='YOLOv5HSVRandomAug'), + dict(type='mmdet.RandomFlip', prob=0.5), + dict( + type='mmdet.MultiBranch', + branch_field=sup_branch_field, + sup=dict( + type='mmdet.PackDetInputs', + meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape', 'flip', 'flip_direction'), + ), + ), +] + +labeled_dataset = dict( + type=supdataset_type, + data_root=data_root, + ann_file=train_ann, + data_prefix=dict(img=train_data), + filter_cfg=dict(filter_empty_gt=False, min_size=32), + pipeline=train_pipeline, +) + +unlabeled_dataset = dict( + type=unsupdataset_type, + data_root=data_root, + ann_file=val_ann, + data_prefix=dict(img='unlabel_data'), + filter_cfg=dict(filter_empty_gt=False), + pipeline=unsup_pipeline, +) + + +train_dataloader = dict( + batch_size=batch, + num_workers=workers, + persistent_workers=True, + sampler=dict( + # type='mmdet.GroupMultiSourceSampler', + type='sscma.SemiSampler', + batch_size=batch, + sample_ratio=[1, 4], + round_up=True, + ), + dataset=dict(type='sscma.SemiDataset', sup_dataset=labeled_dataset, unsup_dataset=unlabeled_dataset), +) + +val_dataloader = dict( + batch_size=val_batch, + num_workers=val_workers, + persistent_workers=persistent_workers, + pin_memory=True, + drop_last=False, + sampler=dict(type='DefaultSampler', shuffle=True), + dataset=dict( + type=supdataset_type, + data_root=data_root, + test_mode=True, + data_prefix=dict(img=val_data), + ann_file=val_ann, + pipeline=test_pipeline, + # batch_shapes_cfg=None, + ), +) + +test_dataloader = val_dataloader + +param_scheduler = None +optim_wrapper = dict( + type='OptimWrapper', + optimizer=dict( + type='SGD', lr=lr, momentum=momentum, weight_decay=weight_decay, nesterov=True, batch_size_per_gpu=batch + ), + constructor='YOLOv5OptimizerConstructor', +) + +default_hooks = dict( + param_scheduler=dict( + type='YOLOv5ParamSchedulerHook', scheduler_type='linear', lr_factor=lr_factor, max_epochs=epochs + ), + checkpoint=dict(type='CheckpointHook', interval=val_interval, save_best='auto', max_keep_ckpts=max_keep_ckpts), +) + + +custom_hooks = [ + dict( + type='EMAHook', ema_type='ExpMomentumEMA', momentum=0.0001, update_buffers=True, strict_load=False, priority=49 + ), + dict(type='mmdet.MeanTeacherHook'), + dict(type="sscma.SemiHook", bure_epoch=200), + dict(type="sscma.LabelMatchHook",priority=100), +] + + +val_evaluator = dict(type='mmdet.CocoMetric', proposal_nums=(100, 1, 10), ann_file=data_root + val_ann, metric='bbox') +test_evaluator = val_evaluator + +train_cfg = dict(type='EpochBasedTrainLoop', max_epochs=epochs, val_interval=val_interval, _delete_=True) +val_cfg = dict(type='sscma.SemiValLoop', bure_epoch=200) +test_cfg = val_cfg From 12b490ed63feaee527688b6a9d0ff49bf923a461 Mon Sep 17 00:00:00 2001 From: mjq2020 Date: Sun, 19 Nov 2023 16:07:09 +0000 Subject: [PATCH 18/33] add: unsup dataset class --- sscma/datasets/unsupdataset.py | 151 +++++++++++++++++++++++++++++++++ 1 file changed, 151 insertions(+) create mode 100644 sscma/datasets/unsupdataset.py diff --git a/sscma/datasets/unsupdataset.py b/sscma/datasets/unsupdataset.py new file mode 100644 index 00000000..8ce3812f --- /dev/null +++ b/sscma/datasets/unsupdataset.py @@ -0,0 +1,151 @@ +from typing import List, Optional, Sequence, Union +import os +import os.path as osp +from mmengine.dataset import BaseDataset +from sscma.registry import DATASETS + + +@DATASETS.register_module() +class UnsupDataset(BaseDataset): + METAINFO: dict = dict( + classes=(), + # palette is a list of color tuples, which is used for visualization. + palette=[ + (220, 20, 60), + (119, 11, 32), + (0, 0, 142), + (0, 0, 230), + (106, 0, 228), + (0, 60, 100), + (0, 80, 100), + (0, 0, 70), + (0, 0, 192), + (250, 170, 30), + (100, 170, 30), + (220, 220, 0), + (175, 116, 175), + (250, 0, 30), + (165, 42, 42), + (255, 77, 255), + (0, 226, 252), + (182, 182, 255), + (0, 82, 0), + (120, 166, 157), + (110, 76, 0), + (174, 57, 255), + (199, 100, 0), + (72, 0, 118), + (255, 179, 240), + (0, 125, 92), + (209, 0, 151), + (188, 208, 182), + (0, 220, 176), + (255, 99, 164), + (92, 0, 73), + (133, 129, 255), + (78, 180, 255), + (0, 228, 0), + (174, 255, 243), + (45, 89, 255), + (134, 134, 103), + (145, 148, 174), + (255, 208, 186), + (197, 226, 255), + (171, 134, 1), + (109, 63, 54), + (207, 138, 255), + (151, 0, 95), + (9, 80, 61), + (84, 105, 51), + (74, 65, 105), + (166, 196, 102), + (208, 195, 210), + (255, 109, 65), + (0, 143, 149), + (179, 0, 194), + (209, 99, 106), + (5, 121, 0), + (227, 255, 205), + (147, 186, 208), + (153, 69, 1), + (3, 95, 161), + (163, 255, 0), + (119, 0, 170), + (0, 182, 199), + (0, 165, 120), + (183, 130, 88), + (95, 32, 0), + (130, 114, 135), + (110, 129, 133), + (166, 74, 118), + (219, 142, 185), + (79, 210, 114), + (178, 90, 62), + (65, 70, 15), + (127, 167, 115), + (59, 105, 106), + (142, 108, 45), + (196, 172, 0), + (95, 54, 80), + (128, 76, 255), + (201, 57, 1), + (246, 0, 122), + (191, 162, 208), + ], + ) + + def __init__( + self, + ann_file: str = '', + metainfo: Optional[dict] = None, + data_root: str = '', + data_prefix: dict = dict(img=''), + filter_cfg: Optional[dict] = None, + indices: Optional[Union[int, Sequence[int]]] = None, + serialize_data: bool = True, + pipeline=None, + test_mode: bool = False, + lazy_init: bool = False, + max_refetch: int = 1000, + ): + super().__init__( + ann_file, + metainfo, + data_root, + data_prefix, + filter_cfg, + indices, + serialize_data, + pipeline, + test_mode, + lazy_init, + max_refetch, + ) + + if data_root and data_prefix and not osp.isabs(data_prefix['img']): + self.data_prefix = osp.join(data_root, data_prefix['img']) + elif osp.isabs(data_prefix['img']): + self.data_prefix = data_prefix['img'] + else: + raise ValueError + print(self.data_prefix) + + def load_data_list(self) -> List[dict]: + print(self.data_prefix) + filels = [osp.join(self.data_prefix['img'], i) for i in os.listdir(self.data_prefix['img'])] + data_list = [] + for idx, file in enumerate(filels): + # img = cv2.imread(file) + data_list.append({'img_path': file, 'img_id': idx}) + return data_list + + def __getitem__(self, idx: int) -> dict: + return super().__getitem__(idx) + + @property + def metainfo(self) -> dict: + return self.METAINFO + + @metainfo.setter + def metainfo(self, metainfo) -> None: + self.METAINFO = metainfo From 437a440ab99e208742427d49ec3e05d7ce4d243a Mon Sep 17 00:00:00 2001 From: mjq2020 Date: Sun, 19 Nov 2023 16:11:30 +0000 Subject: [PATCH 19/33] add: EfficientTeacher class --- sscma/datasets/sampler.py | 2 +- sscma/datasets/transforms/wrappers.py | 12 +-- sscma/models/detectors/base.py | 2 +- sscma/models/detectors/semidetect.py | 109 ++++++++++++++++++++++++++ sscma/models/semi/labelmatch.py | 2 +- 5 files changed, 119 insertions(+), 8 deletions(-) create mode 100644 sscma/models/detectors/semidetect.py diff --git a/sscma/datasets/sampler.py b/sscma/datasets/sampler.py index 3273beea..4318f208 100644 --- a/sscma/datasets/sampler.py +++ b/sscma/datasets/sampler.py @@ -150,7 +150,7 @@ def only_label(self, flag: bool): def only_unlabel(self): return self._only_unlabel - @only_label.setter + @only_unlabel.setter def only_unlabel(self, flag: bool): self.set_label('only_unlabel', flag) diff --git a/sscma/datasets/transforms/wrappers.py b/sscma/datasets/transforms/wrappers.py index 33845e91..9c11f6c2 100644 --- a/sscma/datasets/transforms/wrappers.py +++ b/sscma/datasets/transforms/wrappers.py @@ -16,11 +16,13 @@ def transform(self, results: Dict) -> Optional[Union[Dict, Tuple[List, List]]]: for branch in self.branch_field: multi_results[branch] = {'inputs': None, 'data_samples': None} for branch, pipeline in self.branch_pipelines.items(): - if branch == self.piece_key: - results['img'] - branch_results = pipeline(branch_results) - else: - branch_results = pipeline(copy.deepcopy(results)) + branch_results = pipeline(copy.deepcopy(results)) + if branch == 'unsup_teacher': + teach = branch_results['inputs'].permute(1, 2, 0).cpu().numpy() + results['img'] = branch_results['inputs'].permute(1, 2, 0).cpu().numpy() + + elif branch == 'unsup_student': + stu = branch_results['inputs'].permute(1, 2, 0).cpu().numpy() # If one branch pipeline returns None, # it will sample another data from dataset. if branch_results is None: diff --git a/sscma/models/detectors/base.py b/sscma/models/detectors/base.py index 010b4167..21c63e21 100644 --- a/sscma/models/detectors/base.py +++ b/sscma/models/detectors/base.py @@ -4,7 +4,7 @@ from mmdet.models.detectors import BaseDetector, SemiBaseDetector from mmdet.utils import OptConfigType, OptMultiConfig, ConfigType from mmdet.models.utils import rename_loss_dict, reweight_loss_dict -from mmdet.structures import SampleList, OptSampleList +from mmdet.structures import SampleList import torch from torch import Tensor from mmengine.optim import OptimWrapper diff --git a/sscma/models/detectors/semidetect.py b/sscma/models/detectors/semidetect.py new file mode 100644 index 00000000..1178600f --- /dev/null +++ b/sscma/models/detectors/semidetect.py @@ -0,0 +1,109 @@ +import copy +from typing import Dict, Optional, Tuple, Union + +from mmdet.models.detectors import SemiBaseDetector, BaseDetector +from mmdet.structures import SampleList +from mmdet.utils import ConfigType, OptConfigType, OptMultiConfig +from mmengine.optim import OptimWrapper +import torch +from torch import Tensor + +from sscma.models.semi.labelmatch import LabelMatch +from sscma.registry import MODELS, LOSSES + + +@MODELS.register_module() +class EfficientTeacher(SemiBaseDetector): + teacher: BaseDetector + student: BaseDetector + + def __init__( + self, + detector: ConfigType, + domain_loss_cfg: ConfigType, + target_loss_cfg: ConfigType, + pseudo_label_cfg: ConfigType, + teacher_loss_weight: int, + da_loss_weight: int = 0, + online_pseudo: bool = True, + semi_train_cfg: OptConfigType = None, + semi_test_cfg: OptConfigType = None, + data_preprocessor: OptConfigType = None, + init_cfg: OptMultiConfig = None, + ) -> None: + super().__init__(detector, semi_train_cfg, semi_test_cfg, data_preprocessor, init_cfg) + self.pseudo_label_creator: LabelMatch = MODELS.build(pseudo_label_cfg) + self.domain_loss = LOSSES.build(domain_loss_cfg) + self.target_loss = LOSSES.build(target_loss_cfg) + self.teacher_loss_weight = teacher_loss_weight + self.da_loss_weight = da_loss_weight + self.online_pseudo = online_pseudo + + def train_step(self, data: Union[dict, tuple, list], optim_wrapper: OptimWrapper) -> Dict[str, Tensor]: + """Implements the default model training process including + preprocessing, model forward propagation, loss calculation, + optimization, and back-propagation. + + During non-distributed training. If subclasses do not override the + :meth:`train_step`, :class:`EpochBasedTrainLoop` or + :class:`IterBasedTrainLoop` will call this method to update model + parameters. The default parameter update process is as follows: + + 1. Calls ``self.data_processor(data, training=False)`` to collect + batch_inputs and corresponding data_samples(labels). + 2. Calls ``self(batch_inputs, data_samples, mode='loss')`` to get raw + loss + 3. Calls ``self.parse_losses`` to get ``parsed_losses`` tensor used to + backward and dict of loss tensor used to log messages. + 4. Calls ``optim_wrapper.update_params(loss)`` to update model. + + Args: + data (dict or tuple or list): Data sampled from dataset. + optim_wrapper (OptimWrapper): OptimWrapper instance + used to update model parameters. + + Returns: + Dict[str, torch.Tensor]: A ``dict`` of tensor for logging. + """ + # Enable automatic mixed precision training context. + with optim_wrapper.optim_context(self): + if 'unsup_student' in data['inputs'] and not any( + [True for i in data['inputs']['unsup_student'] if isinstance(i, torch.Tensor)] + ): + data['inputs'].pop('unsup_student') + data['inputs'].pop('unsup_teacher') + data['data_samples'].pop('unsup_teacher') + data['data_samples'].pop('unsup_student') + + data = self.data_preprocessor(data, True) + + losses = self._run_forward(data, mode='loss') # type: ignore + parsed_losses, log_vars = self.parse_losses(losses) # type: ignore + optim_wrapper.update_params(parsed_losses) + return log_vars + + def loss(self, multi_batch_inputs: Dict[str, Tensor], multi_batch_data_samples: Dict[str, SampleList]) -> dict: + """Calculate losses from multi-branch inputs and data samples. + + Args: + multi_batch_inputs (Dict[str, Tensor]): The dict of multi-branch + input images, each value with shape (N, C, H, W). + Each value should usually be mean centered and std scaled. + multi_batch_data_samples (Dict[str, List[:obj:`DetDataSample`]]): + The dict of multi-branch data samples. + + Returns: + dict: A dictionary of loss components + """ + if multi_batch_inputs.get('unsup_teacher', None) is not None: + return super().loss(multi_batch_inputs, multi_batch_data_samples) + else: + # supervised training process + losses = self.loss_by_gt_instances(multi_batch_inputs['sup'], multi_batch_data_samples['sup']) + return losses + + def computer_domain_loss(self, feature: Tensor) -> Tensor: + return self.domain_loss(feature) + + def computer_target_loss(self, feature: Tensor) -> Tensor: + return self.target_loss(feature) diff --git a/sscma/models/semi/labelmatch.py b/sscma/models/semi/labelmatch.py index 08dbcafb..90b5825e 100644 --- a/sscma/models/semi/labelmatch.py +++ b/sscma/models/semi/labelmatch.py @@ -1,5 +1,5 @@ import torch -from mmdet.structures.det_data_sample import SampleList, OptSampleList +from mmdet.structures.det_data_sample import SampleList from mmengine.structures.instance_data import InstanceData from sscma.models.detectors.base import BasePseudoLabelCreator from sscma.registry import MODELS From bdb98935541f53648db9395b67bacaab20ed6ab3 Mon Sep 17 00:00:00 2001 From: mjq2020 Date: Sun, 19 Nov 2023 16:12:05 +0000 Subject: [PATCH 20/33] add: EfficientTeacher class --- sscma/models/detectors/semidetect.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sscma/models/detectors/semidetect.py b/sscma/models/detectors/semidetect.py index 1178600f..308898b2 100644 --- a/sscma/models/detectors/semidetect.py +++ b/sscma/models/detectors/semidetect.py @@ -1,5 +1,4 @@ -import copy -from typing import Dict, Optional, Tuple, Union +from typing import Dict, Union from mmdet.models.detectors import SemiBaseDetector, BaseDetector from mmdet.structures import SampleList From 133dddffa88604d73bedec793f42a944184aabb9 Mon Sep 17 00:00:00 2001 From: mjq2020 Date: Sun, 19 Nov 2023 16:15:20 +0000 Subject: [PATCH 21/33] modify: unsup student get --- sscma/datasets/transforms/wrappers.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/sscma/datasets/transforms/wrappers.py b/sscma/datasets/transforms/wrappers.py index 9c11f6c2..92bc17a1 100644 --- a/sscma/datasets/transforms/wrappers.py +++ b/sscma/datasets/transforms/wrappers.py @@ -18,11 +18,7 @@ def transform(self, results: Dict) -> Optional[Union[Dict, Tuple[List, List]]]: for branch, pipeline in self.branch_pipelines.items(): branch_results = pipeline(copy.deepcopy(results)) if branch == 'unsup_teacher': - teach = branch_results['inputs'].permute(1, 2, 0).cpu().numpy() results['img'] = branch_results['inputs'].permute(1, 2, 0).cpu().numpy() - - elif branch == 'unsup_student': - stu = branch_results['inputs'].permute(1, 2, 0).cpu().numpy() # If one branch pipeline returns None, # it will sample another data from dataset. if branch_results is None: From 24a3d10421917f6f4094ed6fae43b7cf79e96d60 Mon Sep 17 00:00:00 2001 From: mjq2020 Date: Sun, 19 Nov 2023 16:20:55 +0000 Subject: [PATCH 22/33] fix: register name is class type bug --- sscma/datasets/dataset_wrappers.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/sscma/datasets/dataset_wrappers.py b/sscma/datasets/dataset_wrappers.py index 6949b18e..2c324cdb 100644 --- a/sscma/datasets/dataset_wrappers.py +++ b/sscma/datasets/dataset_wrappers.py @@ -1,8 +1,11 @@ +from typing import Union + from torch.utils.data.dataset import ConcatDataset as _ConcatDataset from sscma.registry import DATASETS +from mmdet.datasets import CocoDataset -@DATASETS.register_module(_ConcatDataset) +@DATASETS.register_module() class SemiDataset(_ConcatDataset): """ For merging real labeled and pseudo-labeled datasets in semi-supervised. @@ -13,12 +16,12 @@ class SemiDataset(_ConcatDataset): """ def __init__(self, sup_dataset: dict, unsup_dataset: dict, **kwargs) -> None: - self._sup_dataset = DATASETS.build(sup_dataset) - self._unsup_dataset = DATASETS.build(unsup_dataset) + self._sup_dataset: CocoDataset = DATASETS.build(sup_dataset) + self._unsup_dataset: CocoDataset = DATASETS.build(unsup_dataset) super(SemiDataset, self).__init__((self._sup_dataset, self._unsup_dataset)) - self.CLASSES = self.sup_dataset.CLASSES + self.CLASSES: Union[list, tuple] = self.sup_dataset.METAINFO['classes'] @property def sup_dataset(self): From 65a9144d44ba792242a1422f71deea4c0b09a7f2 Mon Sep 17 00:00:00 2001 From: mjq2020 Date: Sun, 19 Nov 2023 16:27:20 +0000 Subject: [PATCH 23/33] add: fair pseudo label creator --- sscma/models/semi/fairpseudolabel.py | 41 ++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 sscma/models/semi/fairpseudolabel.py diff --git a/sscma/models/semi/fairpseudolabel.py b/sscma/models/semi/fairpseudolabel.py new file mode 100644 index 00000000..2af35c6e --- /dev/null +++ b/sscma/models/semi/fairpseudolabel.py @@ -0,0 +1,41 @@ +import torch +from mmdet.structures.det_data_sample import SampleList +from mmengine.structures.instance_data import InstanceData +from sscma.models.detectors.base import BasePseudoLabelCreator +from sscma.registry import MODELS + + +@MODELS.register_module() +class FairPseudoLabel(BasePseudoLabelCreator): + def __init__(self, *args, **kwargs) -> None: + super().__init__(*args, **kwargs) + + self.cls_thr = None + self.cls_thr_ig = None + + def generate_pseudo_labels_online( + self, + teach_pred, + student_sample: SampleList, + ): + if self.cls_thr is not None and self.cls_thr_ig is not None: + for idx_sample, pred in enumerate(teach_pred): + pseudo_bboxs, pseudo_label = [], [] + pseudo_bboxs_ig, pseudo_label_ig = [], [] + gt = InstanceData() + ignore = InstanceData() + for idx, label in enumerate(pred.pred_instances.labels): + if pred.pred_instances.scores[idx] > self.cls_thr[int(label)]: + pseudo_bboxs.append(pred.pred_instances.bboxes[idx]) + pseudo_label.append(label) + elif pred.pred_instances.scores[idx] > self.cls_thr_ig[int(label)]: + pseudo_bboxs_ig.append(pred.pred_instances.bboxes[idx]) + pseudo_label_ig.append(label) + if len(pseudo_bboxs): + gt.bboxes = torch.concat([i.unsqueeze(0) for i in pseudo_bboxs], dim=0) + gt.labels = torch.concat([i.unsqueeze(0) for i in pseudo_label], dim=0) + if len(pseudo_bboxs_ig): + ignore.labels = torch.concat([i.unsqueeze(0) for i in pseudo_label_ig], dim=0) + ignore.bboxes = torch.concat([i.unsqueeze(0) for i in pseudo_bboxs_ig], dim=0) + student_sample[idx_sample].gt_instannces = gt + student_sample[idx_sample].ignored_instances = ignore From 1f61eabbe96bd0805eaf47affe82bf076d88fa57 Mon Sep 17 00:00:00 2001 From: mjq2020 Date: Mon, 27 Nov 2023 03:23:54 +0000 Subject: [PATCH 24/33] add: faster shuffllenetv2 backbone --- sscma/models/backbones/__init__.py | 3 +- sscma/models/backbones/shufflenetv2.py | 131 ++++++++++++++++++++++++- 2 files changed, 132 insertions(+), 2 deletions(-) diff --git a/sscma/models/backbones/__init__.py b/sscma/models/backbones/__init__.py index 2ae3dadf..9bc2d1c9 100644 --- a/sscma/models/backbones/__init__.py +++ b/sscma/models/backbones/__init__.py @@ -3,7 +3,7 @@ from .MobileNetv2 import MobileNetv2 from .MobileNetv3 import MobileNetV3 from .pfld_mobilenet_v2 import PfldMobileNetV2 -from .shufflenetv2 import CustomShuffleNetV2 +from .shufflenetv2 import CustomShuffleNetV2, FastShuffleNetV2 from .ShuffleNetV2 import ShuffleNetV2 from .SoundNet import SoundNetRaw from .SqueezeNet import SqueezeNet @@ -20,4 +20,5 @@ 'EfficientNet', 'MobileNetv2', 'MicroNet', + "FastShuffleNetV2", ] diff --git a/sscma/models/backbones/shufflenetv2.py b/sscma/models/backbones/shufflenetv2.py index d3825f4b..b38c23b7 100644 --- a/sscma/models/backbones/shufflenetv2.py +++ b/sscma/models/backbones/shufflenetv2.py @@ -1,12 +1,141 @@ import copy - +from typing import Tuple +import torch import torch.nn as nn from mmcv.cnn import ConvModule +from mmengine.model.base_module import BaseModule from mmpose.models.backbones.shufflenet_v2 import ShuffleNetV2 from sscma.registry import BACKBONES +class ShuffleV2Block(nn.Module): + """ + Reference: https://github.com/dog-qiuqiu/FastestDet/blob/50473cd155cb088aa4a99e64ff6a4b3c24fa07e1/module/shufflenetv2.py#L4 + """ + + def __init__(self, inp, oup, mid_channels, *, ksize, stride) -> None: + super(ShuffleV2Block, self).__init__() + self.stride = stride + assert stride in [1, 2] + + self.mid_channels = mid_channels + self.ksize = ksize + pad = ksize // 2 + self.pad = pad + self.inp = inp + + outputs = oup - inp + + branch_main = [ + # pw + nn.Conv2d(inp, mid_channels, 1, 1, 0, bias=False), + nn.BatchNorm2d(mid_channels), + nn.ReLU(inplace=True), + # dw + nn.Conv2d(mid_channels, mid_channels, ksize, stride, pad, groups=mid_channels, bias=False), + nn.BatchNorm2d(mid_channels), + # pw-linear + nn.Conv2d(mid_channels, outputs, 1, 1, 0, bias=False), + nn.BatchNorm2d(outputs), + nn.ReLU(inplace=True), + ] + self.branch_main = nn.Sequential(*branch_main) + + if stride == 2: + branch_proj = [ + # dw + nn.Conv2d(inp, inp, ksize, stride, pad, groups=inp, bias=False), + nn.BatchNorm2d(inp), + # pw-linear + nn.Conv2d(inp, inp, 1, 1, 0, bias=False), + nn.BatchNorm2d(inp), + nn.ReLU(inplace=True), + ] + self.branch_proj = nn.Sequential(*branch_proj) + else: + self.branch_proj = None + + def forward(self, old_x) -> torch.Tensor: + if self.stride == 1: + x_proj, x = self.channel_shuffle(old_x) + return torch.cat((x_proj, self.branch_main(x)), 1) + elif self.stride == 2: + x_proj = old_x + x = old_x + return torch.cat((self.branch_proj(x_proj), self.branch_main(x)), 1) + + def channel_shuffle(self, x) -> Tuple[torch.Tensor]: + batchsize, num_channels, height, width = x.data.size() + assert num_channels % 4 == 0 + x = x.reshape(batchsize * num_channels // 2, 2, height * width) + x = x.permute(1, 0, 2) + x = x.reshape(2, -1, num_channels // 2, height, width) + return x[0], x[1] + + +@BACKBONES.register_module() +class FastShuffleNetV2(BaseModule): + """ + Reference: https://github.com/dog-qiuqiu/FastestDet/blob/50473cd155cb088aa4a99e64ff6a4b3c24fa07e1/module/shufflenetv2.py#L64C6-L64C7 + """ + + def __init__(self, stage_repeats, stage_out_channels, pretrain=None, *args, **kwargs) -> None: + super(FastShuffleNetV2, self).__init__(*args, **kwargs) + + self.stage_repeats = stage_repeats + self.stage_out_channels = stage_out_channels + + # building first layer + input_channel = self.stage_out_channels[1] + self.first_conv = nn.Sequential( + nn.Conv2d(3, input_channel, 3, 2, 1, bias=False), + nn.BatchNorm2d(input_channel), + nn.ReLU(inplace=True), + ) + + self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1) + + stage_names = ["stage2", "stage3", "stage4"] + for idxstage in range(len(self.stage_repeats)): + numrepeat = self.stage_repeats[idxstage] + output_channel = self.stage_out_channels[idxstage + 2] + stageSeq = [] + for i in range(numrepeat): + if i == 0: + stageSeq.append( + ShuffleV2Block( + input_channel, output_channel, mid_channels=output_channel // 2, ksize=3, stride=2 + ) + ) + else: + stageSeq.append( + ShuffleV2Block( + input_channel // 2, output_channel, mid_channels=output_channel // 2, ksize=3, stride=1 + ) + ) + input_channel = output_channel + setattr(self, stage_names[idxstage], nn.Sequential(*stageSeq)) + + self._initialize_weights(pretrain) + + def forward(self, x) -> Tuple[torch.Tensor]: + x = self.first_conv(x) + x = self.maxpool(x) + P1 = self.stage2(x) + P2 = self.stage3(P1) + P3 = self.stage4(P2) + + return (P1, P2, P3) + + def _initialize_weights(self, param_file) -> None: + if param_file is not None: + print(f"Initialize params from:{param_file}") + self.load_state_dict(torch.load(param_file), strict=True) + else: + self.init_weights() + + @BACKBONES.register_module() class CustomShuffleNetV2(ShuffleNetV2): def __init__( From f2f117e71f5419be2dd9678e37d4c44954a96d18 Mon Sep 17 00:00:00 2001 From: mjq2020 Date: Mon, 27 Nov 2023 07:08:11 +0000 Subject: [PATCH 25/33] add: swift yolo config for 320x320 size --- .../swift_yolo_shuff_1xb16_300e_coco.py | 168 ++++++++++++++++++ sscma/models/backbones/shufflenetv2.py | 11 +- 2 files changed, 169 insertions(+), 10 deletions(-) create mode 100644 configs/yolov5/swift_yolo_shuff_1xb16_300e_coco.py diff --git a/configs/yolov5/swift_yolo_shuff_1xb16_300e_coco.py b/configs/yolov5/swift_yolo_shuff_1xb16_300e_coco.py new file mode 100644 index 00000000..f43deef6 --- /dev/null +++ b/configs/yolov5/swift_yolo_shuff_1xb16_300e_coco.py @@ -0,0 +1,168 @@ +_base_ = ['./base_arch.py'] + +# ========================Suggested optional parameters======================== +# MODEL +num_classes = 71 +deepen_factor = 1.0 +widen_factor = 1.0 + +# DATA +dataset_type = 'sscma.CustomYOLOv5CocoDataset' +train_ann = 'train/_annotations.coco.json' +train_data = 'train/' # Prefix of train image path +val_ann = 'valid/_annotations.coco.json' +val_data = 'valid/' # Prefix of val image path + +# dataset link: https://universe.roboflow.com/team-roboflow/coco-128 +data_root = 'https://universe.roboflow.com/ds/z5UOcgxZzD?key=bwx9LQUT0t' +height = 320 +width = 320 +batch = 16 +workers = 2 +val_batch = batch +val_workers = workers +imgsz = (width, height) + +# TRAIN +persistent_workers = True + +# ================================END================================= + +# DATA +affine_scale = 0.5 +# MODEL +strides = [8, 16, 21] + +anchors = [ + [(10, 13), (16, 30), (33, 23)], # P3/8 + [(30, 61), (62, 45), (59, 119)], # P4/16 + [(116, 90), (156, 198), (373, 326)], # P5/32 +] + +model = dict( + type='mmyolo.YOLODetector', + backbone=dict( + type='FastShuffleNetV2', + stage_repeats=[4, 8, 4], + stage_out_channels=[-1, 24, 48, 96, 192], + init_cfg=dict( + type='Pretrained', + checkpoint='https://files.seeedstudio.com/sscma/model_zoo/backbone/fastshufllenet2_sha1_90be6b843860adcc72555d8699dafaf99624bddd.pth', + ), + _delete_=True, + ), + neck=dict( + type='YOLOv5PAFPN', + deepen_factor=deepen_factor, + widen_factor=widen_factor, + in_channels=[48, 96, 192], + out_channels=[48, 96, 192], + num_csp_blocks=1, + # norm_cfg=norm_cfg, + act_cfg=dict(type='ReLU', inplace=True), + ), + bbox_head=dict( + head_module=dict( + num_classes=num_classes, + in_channels=[48, 96, 192], + widen_factor=widen_factor, + ), + ), +) + +# ======================datasets================== + + +batch_shapes_cfg = dict( + type='BatchShapePolicy', + batch_size=1, + img_size=imgsz[0], + # The image scale of padding should be divided by pad_size_divisor + size_divisor=32, + # Additional paddings for pixel scale + extra_pad_ratio=0.5, +) + +albu_train_transforms = [ + dict(type='Blur', p=0.01), + dict(type='MedianBlur', p=0.01), + dict(type='ToGray', p=0.01), + dict(type='CLAHE', p=0.01), +] + +pre_transform = [ + dict(type='LoadImageFromFile', file_client_args=dict(backend='disk')), + dict(type='LoadAnnotations', with_bbox=True), +] + +train_pipeline = [ + *pre_transform, + dict(type='Mosaic', img_scale=imgsz, pad_val=114.0, pre_transform=pre_transform), + dict( + type='YOLOv5RandomAffine', + max_rotate_degree=0.0, + max_shear_degree=0.0, + scaling_ratio_range=(1 - affine_scale, 1 + affine_scale), + # imgsz is (width, height) + border=(-imgsz[0] // 2, -imgsz[1] // 2), + border_val=(114, 114, 114), + ), + dict( + type='mmdet.Albu', + transforms=albu_train_transforms, + bbox_params=dict(type='BboxParams', format='pascal_voc', label_fields=['gt_bboxes_labels', 'gt_ignore_flags']), + keymap={'img': 'image', 'gt_bboxes': 'bboxes'}, + ), + dict(type='YOLOv5HSVRandomAug'), + dict(type='mmdet.RandomFlip', prob=0.5), + dict( + type='mmdet.PackDetInputs', meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape', 'flip', 'flip_direction') + ), +] + +train_dataloader = dict( + batch_size=batch, + num_workers=workers, + persistent_workers=persistent_workers, + pin_memory=True, + sampler=dict(type='DefaultSampler', shuffle=True), + dataset=dict( + type=dataset_type, + data_root=data_root, + ann_file=train_ann, + data_prefix=dict(img=train_data), + filter_cfg=dict(filter_empty_gt=False, min_size=32), + pipeline=train_pipeline, + ), +) + +test_pipeline = [ + dict(type='LoadImageFromFile', file_client_args=dict(backend='disk')), + dict(type='YOLOv5KeepRatioResize', scale=imgsz), + dict(type='LetterResize', scale=imgsz, allow_scale_up=False, pad_val=dict(img=114)), + dict(type='LoadAnnotations', with_bbox=True, _scope_='mmdet'), + dict( + type='mmdet.PackDetInputs', + meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape', 'scale_factor', 'pad_param'), + ), +] + +val_dataloader = dict( + batch_size=val_batch, + num_workers=val_workers, + persistent_workers=persistent_workers, + pin_memory=True, + drop_last=False, + sampler=dict(type='DefaultSampler', shuffle=False), + dataset=dict( + type=dataset_type, + data_root=data_root, + test_mode=True, + data_prefix=dict(img=val_data), + ann_file=val_ann, + pipeline=test_pipeline, + batch_shapes_cfg=batch_shapes_cfg, + ), +) + +test_dataloader = val_dataloader diff --git a/sscma/models/backbones/shufflenetv2.py b/sscma/models/backbones/shufflenetv2.py index b38c23b7..5797ac83 100644 --- a/sscma/models/backbones/shufflenetv2.py +++ b/sscma/models/backbones/shufflenetv2.py @@ -80,7 +80,7 @@ class FastShuffleNetV2(BaseModule): Reference: https://github.com/dog-qiuqiu/FastestDet/blob/50473cd155cb088aa4a99e64ff6a4b3c24fa07e1/module/shufflenetv2.py#L64C6-L64C7 """ - def __init__(self, stage_repeats, stage_out_channels, pretrain=None, *args, **kwargs) -> None: + def __init__(self, stage_repeats, stage_out_channels, *args, **kwargs) -> None: super(FastShuffleNetV2, self).__init__(*args, **kwargs) self.stage_repeats = stage_repeats @@ -117,8 +117,6 @@ def __init__(self, stage_repeats, stage_out_channels, pretrain=None, *args, **kw input_channel = output_channel setattr(self, stage_names[idxstage], nn.Sequential(*stageSeq)) - self._initialize_weights(pretrain) - def forward(self, x) -> Tuple[torch.Tensor]: x = self.first_conv(x) x = self.maxpool(x) @@ -128,13 +126,6 @@ def forward(self, x) -> Tuple[torch.Tensor]: return (P1, P2, P3) - def _initialize_weights(self, param_file) -> None: - if param_file is not None: - print(f"Initialize params from:{param_file}") - self.load_state_dict(torch.load(param_file), strict=True) - else: - self.init_weights() - @BACKBONES.register_module() class CustomShuffleNetV2(ShuffleNetV2): From 8208dc03b928cbd9115a2559f5a32ad6051abff2 Mon Sep 17 00:00:00 2001 From: mjq2020 Date: Mon, 27 Nov 2023 07:44:28 +0000 Subject: [PATCH 26/33] delete: yolo except swift yolo --- configs/ssd/bash_arch.py | 182 --------- configs/yolov3/README.md | 38 -- configs/yolov3/yolov3_mbv2_416_voc.py | 165 -------- ...o.py => swift_yolo_mb2_1xb16_300e_coco.py} | 0 ....py => swift_yolo_tiny_1xb16_300e_coco.py} | 0 configs/yolov5/yolov5_l_1xb16_300e_coco.py | 158 -------- configs/yolov5/yolov5_m_1xb16_300e_coco.py | 158 -------- configs/yolov5/yolov5_n_1xb16_300e_coco.py | 159 -------- configs/yolov5/yolov5_s_1xb16_300e_coco.py | 159 -------- configs/yolov6/base.py | 273 -------------- .../yolov6_l_syncbn_fast_8xb32-300e_coco.py | 29 -- .../yolov6_m_syncbn_fast_8xb32-300e_coco.py | 68 ---- .../yolov6_n_syncbn_fast_8xb32-400e_coco.py | 21 -- .../yolov6_s_syncbn_fast_8xb32-300e_coco.py | 273 -------------- .../yolov6_t_syncbn_fast_8xb32-300e_coco.py | 16 - configs/yolov7/base.py | 337 ----------------- ...yolov7_d-p6_syncbn_fast_8x16b-300e_coco.py | 19 - ...yolov7_e-p6_syncbn_fast_8x16b-300e_coco.py | 13 - .../yolov7_l_syncbn_fast_8x16b-300e_coco.py | 337 ----------------- ...yolov7_tiny_syncbn_fast_8x16b-300e_coco.py | 97 ----- ...yolov7_w-p6_syncbn_fast_8x16b-300e_coco.py | 182 --------- .../yolov7_x_syncbn_fast_8x16b-300e_coco.py | 12 - configs/yolox/base_arch.py | 351 ------------------ configs/yolox/yolox_tiny_1xb16_300e_coco.py | 349 ----------------- 24 files changed, 3396 deletions(-) delete mode 100644 configs/ssd/bash_arch.py delete mode 100644 configs/yolov3/README.md delete mode 100644 configs/yolov3/yolov3_mbv2_416_voc.py rename configs/yolov5/{yolov5_mb2_1xb16_300e_coco.py => swift_yolo_mb2_1xb16_300e_coco.py} (100%) rename configs/yolov5/{yolov5_tiny_1xb16_300e_coco.py => swift_yolo_tiny_1xb16_300e_coco.py} (100%) delete mode 100644 configs/yolov5/yolov5_l_1xb16_300e_coco.py delete mode 100644 configs/yolov5/yolov5_m_1xb16_300e_coco.py delete mode 100644 configs/yolov5/yolov5_n_1xb16_300e_coco.py delete mode 100644 configs/yolov5/yolov5_s_1xb16_300e_coco.py delete mode 100644 configs/yolov6/base.py delete mode 100644 configs/yolov6/yolov6_l_syncbn_fast_8xb32-300e_coco.py delete mode 100644 configs/yolov6/yolov6_m_syncbn_fast_8xb32-300e_coco.py delete mode 100644 configs/yolov6/yolov6_n_syncbn_fast_8xb32-400e_coco.py delete mode 100644 configs/yolov6/yolov6_s_syncbn_fast_8xb32-300e_coco.py delete mode 100644 configs/yolov6/yolov6_t_syncbn_fast_8xb32-300e_coco.py delete mode 100644 configs/yolov7/base.py delete mode 100644 configs/yolov7/yolov7_d-p6_syncbn_fast_8x16b-300e_coco.py delete mode 100644 configs/yolov7/yolov7_e-p6_syncbn_fast_8x16b-300e_coco.py delete mode 100644 configs/yolov7/yolov7_l_syncbn_fast_8x16b-300e_coco.py delete mode 100644 configs/yolov7/yolov7_tiny_syncbn_fast_8x16b-300e_coco.py delete mode 100644 configs/yolov7/yolov7_w-p6_syncbn_fast_8x16b-300e_coco.py delete mode 100644 configs/yolov7/yolov7_x_syncbn_fast_8x16b-300e_coco.py delete mode 100644 configs/yolox/base_arch.py delete mode 100644 configs/yolox/yolox_tiny_1xb16_300e_coco.py diff --git a/configs/ssd/bash_arch.py b/configs/ssd/bash_arch.py deleted file mode 100644 index 11711d55..00000000 --- a/configs/ssd/bash_arch.py +++ /dev/null @@ -1,182 +0,0 @@ -_base_ = [ - '../_base_/default_runtime_det.py', -] # model settings - -default_scope = 'mmdet' - -# ========================Suggested optional parameters======================== -# MODEL -num_classes = 71 - -# TRAIN -# dataset settings -dataset_type = 'sscma.CustomCocoDataset' -# dataset link: https://universe.roboflow.com/team-roboflow/coco-128 -data_root = 'https://universe.roboflow.com/ds/z5UOcgxZzD?key=bwx9LQUT0t' - -train_ann = 'train/_annotations.coco.json' -train_data = 'train/' -val_ann = 'valid/_annotations.coco.json' -val_data = 'valid/' - -height = 300 -width = 300 -imgsz = (width, height) -batch = 16 -workers = 4 -val_batch = batch -val_workers = workers - -# TRAIN -lr = 0.001 -epochs = 300 - -weight_decay = 0.0005 -momentum = 0.9 - -# ================================END================================= - - -model = dict( - type='SingleStageDetector', - data_preprocessor=dict( - type='DetDataPreprocessor', mean=[0.0, 0.0, 0.0], std=[255.0, 255.0, 255.0], bgr_to_rgb=True, pad_size_divisor=1 - ), - backbone=dict( - type='SSDVGG', - depth=16, - with_last_pool=False, - ceil_mode=True, - out_indices=(3, 4), - out_feature_indices=(22, 34), - init_cfg=dict(type='Pretrained', checkpoint='open-mmlab://vgg16_caffe'), - ), - neck=dict( - type='SSDNeck', - in_channels=(512, 1024), - out_channels=(512, 1024, 512, 256, 256, 256), - level_strides=(2, 2, 1, 1), - level_paddings=(1, 1, 0, 0), - l2_norm_scale=20, - ), - bbox_head=dict( - type='SSDHead', - in_channels=(512, 1024, 512, 256, 256, 256), - num_classes=num_classes, - anchor_generator=dict( - type='SSDAnchorGenerator', - scale_major=False, - input_size=height, - basesize_ratio_range=(0.15, 0.9), - strides=[8, 16, 32, 64, 100, 300], - ratios=[[2], [2, 3], [2, 3], [2, 3], [2], [2]], - ), - bbox_coder=dict(type='DeltaXYWHBBoxCoder', target_means=[0.0, 0.0, 0.0, 0.0], target_stds=[0.1, 0.1, 0.2, 0.2]), - ), - # model training and testing settings - train_cfg=dict( - assigner=dict( - type='MaxIoUAssigner', - pos_iou_thr=0.5, - neg_iou_thr=0.5, - min_pos_iou=0.0, - ignore_iof_thr=-1, - gt_max_assign_all=False, - ), - sampler=dict(type='PseudoSampler'), - smoothl1_beta=1.0, - allowed_border=-1, - pos_weight=-1, - neg_pos_ratio=3, - debug=False, - ), - test_cfg=dict( - nms_pre=1000, nms=dict(type='nms', iou_threshold=0.45), min_bbox_size=0, score_thr=0.02, max_per_img=200 - ), -) -cudnn_benchmark = True - -backend_args = None -train_pipeline = [ - dict(type='LoadImageFromFile', backend_args=backend_args), - dict(type='LoadAnnotations', with_bbox=True), - dict(type='Expand', mean=[0.0, 0.0, 0.0], to_rgb=True, ratio_range=(1, 4)), - dict(type='MinIoURandomCrop', min_ious=(0.1, 0.3, 0.5, 0.7, 0.9), min_crop_size=0.3), - dict(type='Resize', scale=imgsz, keep_ratio=False), - dict(type='RandomFlip', prob=0.5), - dict( - type='PhotoMetricDistortion', - brightness_delta=32, - contrast_range=(0.5, 1.5), - saturation_range=(0.5, 1.5), - hue_delta=18, - ), - dict(type='PackDetInputs'), -] -test_pipeline = [ - dict(type='LoadImageFromFile', backend_args=backend_args), - dict(type='Resize', scale=imgsz, keep_ratio=False), - dict(type='LoadAnnotations', with_bbox=True), - dict(type='PackDetInputs', meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape', 'scale_factor')), -] -train_dataloader = dict( - batch_size=batch, - num_workers=workers, - batch_sampler=None, - sampler=dict(type='DefaultSampler', shuffle=True, round_up=False), - dataset=dict( - type=dataset_type, - data_root=data_root, - ann_file=train_ann, - data_prefix=dict(img=train_data), - filter_cfg=dict(filter_empty_gt=True, min_size=32), - pipeline=train_pipeline, - backend_args=backend_args, - ), -) - -val_dataloader = dict( - batch_size=val_batch, - num_workers=val_workers, - persistent_workers=True, - drop_last=False, - sampler=dict(type='DefaultSampler', shuffle=False), - dataset=dict( - type=dataset_type, - data_root=data_root, - ann_file=val_ann, - data_prefix=dict(img=val_data), - test_mode=True, - pipeline=test_pipeline, - backend_args=backend_args, - ), -) -test_dataloader = val_dataloader - -val_evaluator = dict( - type='CocoMetric', - ann_file=data_root + val_ann, - metric='bbox', - format_only=False, - backend_args=backend_args, -) -test_evaluator = val_evaluator - - -# optimizer -optim_wrapper = dict( - type='OptimWrapper', optimizer=dict(type='SGD', lr=lr, momentum=momentum, weight_decay=weight_decay) -) - -custom_hooks = [ - dict(type='NumClassCheckHook'), - dict(type='CheckInvalidLossHook', interval=50, priority='VERY_LOW'), -] - -vis_backends = [dict(type='LocalVisBackend'), dict(type='TensorboardVisBackend')] -visualizer = dict(type='DetLocalVisualizer', vis_backends=vis_backends, name='visualizer') - -# NOTE: `auto_scale_lr` is for automatically scaling LR, -# USER SHOULD NOT CHANGE ITS VALUES. -# base_batch_size = (8 GPUs) x (8 samples per GPU) -auto_scale_lr = dict(base_batch_size=batch) diff --git a/configs/yolov3/README.md b/configs/yolov3/README.md deleted file mode 100644 index d5eda83f..00000000 --- a/configs/yolov3/README.md +++ /dev/null @@ -1,38 +0,0 @@ -# YOLOv3 - -> [YOLOv3: An Incremental Improvement](https://arxiv.org/abs/1804.02767) - - - -## Abstract - -We present some updates to YOLO! We made a bunch of little design changes to make it better. We also trained this new network that's pretty swell. It's a little bigger than last time but more accurate. It's still fast though, don't worry. At 320x320 YOLOv3 runs in 22 ms at 28.2 mAP, as accurate as SSD but three times faster. When we look at the old .5 IOU mAP detection metric YOLOv3 is quite good. It achieves 57.9 mAP@50 in 51 ms on a Titan X, compared to 57.5 mAP@50 in 198 ms by RetinaNet, similar performance but 3.8x faster. - -
- -
- -## Results and Models - -| Backbone | Scale | Lr schd | Mem (GB) | Inf time (fps) | box AP | Config | Download | -| :---------: | :---: | :-----: | :------: | :------------: | :----: | :----------------------------------------------------------------------------------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | -| MobileNetV2 | 416 | 300e | 5.3 | | 24.1 | [config](./yolov3_mbv2_416_coco.py) | [model](https://github.com/Seeed-Studio/edgelab/releases/download/model_zoo/yolov3_mbv2_416_coco.pth)| - -Notice: We reduce the number of channels to 96 in both head and neck. It can reduce the flops and parameters, which makes these models more suitable for edge devices. - -## Credit - -This implementation originates from the project of Haoyu Wu(@wuhy08) at Western Digital. - -## Citation - -```latex -@misc{redmon2018yolov3, - title={YOLOv3: An Incremental Improvement}, - author={Joseph Redmon and Ali Farhadi}, - year={2018}, - eprint={1804.02767}, - archivePrefix={arXiv}, - primaryClass={cs.CV} -} -``` diff --git a/configs/yolov3/yolov3_mbv2_416_voc.py b/configs/yolov3/yolov3_mbv2_416_voc.py deleted file mode 100644 index 54cb78d2..00000000 --- a/configs/yolov3/yolov3_mbv2_416_voc.py +++ /dev/null @@ -1,165 +0,0 @@ -_base_ = '../_base_/default_runtime_det.py' -default_scope = 'mmdet' - -# ========================Suggested optional parameters======================== -# MODEL -num_classes = 71 - -# TRAIN -# dataset settings -dataset_type = 'sscma.CustomCocoDataset' -# dataset link: https://universe.roboflow.com/team-roboflow/coco-128 -data_root = 'https://universe.roboflow.com/ds/z5UOcgxZzD?key=bwx9LQUT0t' - -train_ann = 'train/_annotations.coco.json' -train_data = 'train/' -val_ann = 'valid/_annotations.coco.json' -val_data = 'valid/' - -height = 320 -width = 320 -imgsz = (width, height) - - -# TRAIN -lr = 0.001 -epochs = 300 -batch = 32 -workers = 2 -val_batch=1 -val_workers=1 - -weight_decay = 0.0005 -momentum = 0.9 - -# ================================END================================= -# model settings - -data_preprocessor = dict( - type='DetDataPreprocessor', mean=[0, 0, 0], std=[255.0, 255.0, 255.0], bgr_to_rgb=True, pad_size_divisor=32 -) -model = dict( - type='YOLOV3', - data_preprocessor=data_preprocessor, - backbone=dict( - type='Darknet', - depth=53, - out_indices=(3, 4, 5), - # init_cfg=dict(type='Pretrained', checkpoint='open-mmlab://darknet53') - ), - neck=dict(type='YOLOV3Neck', num_scales=3, in_channels=[1024, 512, 256], out_channels=[512, 256, 128]), - bbox_head=dict( - type='YOLOV3Head', - num_classes=num_classes, - in_channels=[512, 256, 128], - out_channels=[1024, 512, 256], - anchor_generator=dict( - type='YOLOAnchorGenerator', - base_sizes=[ - [(116, 90), (156, 198), (373, 326)], - [(30, 61), (62, 45), (59, 119)], - [(10, 13), (16, 30), (33, 23)], - ], - strides=[32, 16, 8], - ), - bbox_coder=dict(type='YOLOBBoxCoder'), - featmap_strides=[32, 16, 8], - loss_cls=dict(type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0, reduction='sum'), - loss_conf=dict(type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0, reduction='sum'), - loss_xy=dict(type='CrossEntropyLoss', use_sigmoid=True, loss_weight=2.0, reduction='sum'), - loss_wh=dict(type='MSELoss', loss_weight=2.0, reduction='sum'), - ), - # training and testing settings - train_cfg=dict(assigner=dict(type='GridAssigner', pos_iou_thr=0.5, neg_iou_thr=0.5, min_pos_iou=0)), - test_cfg=dict( - nms_pre=1000, - min_bbox_size=0, - score_thr=0.05, - conf_thr=0.005, - nms=dict(type='nms', iou_threshold=0.45), - max_per_img=100, - ), -) - - -backend_args = None -train_pipeline = [ - dict(type='LoadImageFromFile', backend_args=backend_args), - dict(type='LoadAnnotations', with_bbox=True), - dict(type='Expand', mean=data_preprocessor['mean'], to_rgb=data_preprocessor['bgr_to_rgb'], ratio_range=(1, 2)), - dict(type='MinIoURandomCrop', min_ious=(0.4, 0.5, 0.6, 0.7, 0.8, 0.9), min_crop_size=0.3), - dict(type='RandomResize', scale=[(320, 320), imgsz], keep_ratio=True), - dict(type='RandomFlip', prob=0.5), - dict(type='PhotoMetricDistortion'), - dict(type='PackDetInputs'), -] -test_pipeline = [ - dict(type='LoadImageFromFile', backend_args=backend_args), - dict(type='Resize', scale=imgsz, keep_ratio=True), - dict(type='LoadAnnotations', with_bbox=True), - dict(type='PackDetInputs', meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape', 'scale_factor')), -] - - -train_dataloader = dict( - batch_size=batch, - num_workers=workers, - persistent_workers=True, - sampler=dict(type='DefaultSampler', shuffle=True), - batch_sampler=dict(type='AspectRatioBatchSampler'), - dataset=dict( - type=dataset_type, - data_root=data_root, - ann_file=train_ann, - data_prefix=dict(img=train_data), - filter_cfg=dict(filter_empty_gt=True, min_size=32), - pipeline=train_pipeline, - backend_args=backend_args, - ), -) - -val_dataloader = dict( - batch_size=val_batch, - num_workers=val_workers, - persistent_workers=True, - drop_last=False, - sampler=dict(type='DefaultSampler', shuffle=False), - dataset=dict( - type=dataset_type, - data_root=data_root, - ann_file=val_ann, - data_prefix=dict(img=val_data), - test_mode=True, - pipeline=test_pipeline, - backend_args=backend_args, - ), -) -test_dataloader = val_dataloader - -# evaluator -val_evaluator = dict(type='CocoMetric', ann_file=data_root + val_ann, metric='bbox', backend_args=backend_args) -test_evaluator = val_evaluator - - -find_unused_parameters = True - -optim_wrapper = dict( - type='OptimWrapper', - optimizer=dict(type='SGD', lr=lr, momentum=momentum, weight_decay=weight_decay), - clip_grad=dict(max_norm=35, norm_type=2), -) - -# learning policy -param_scheduler = [ - dict(type='LinearLR', start_factor=0.1, by_epoch=False, begin=0, end=2000), - dict(type='MultiStepLR', by_epoch=True, milestones=[218, 246], gamma=0.1), -] - -train_cfg = dict(by_epoch=True, max_epochs=epochs) -val_cfg = dict() -test_cfg = dict() -# learning policy -param_scheduler = [ - dict(type='LinearLR', begin=0, end=30, start_factor=0.001, by_epoch=False), # warm-up - dict(type='MultiStepLR', begin=1, end=500, milestones=[100, 200, 250], gamma=0.1, by_epoch=True), -] diff --git a/configs/yolov5/yolov5_mb2_1xb16_300e_coco.py b/configs/yolov5/swift_yolo_mb2_1xb16_300e_coco.py similarity index 100% rename from configs/yolov5/yolov5_mb2_1xb16_300e_coco.py rename to configs/yolov5/swift_yolo_mb2_1xb16_300e_coco.py diff --git a/configs/yolov5/yolov5_tiny_1xb16_300e_coco.py b/configs/yolov5/swift_yolo_tiny_1xb16_300e_coco.py similarity index 100% rename from configs/yolov5/yolov5_tiny_1xb16_300e_coco.py rename to configs/yolov5/swift_yolo_tiny_1xb16_300e_coco.py diff --git a/configs/yolov5/yolov5_l_1xb16_300e_coco.py b/configs/yolov5/yolov5_l_1xb16_300e_coco.py deleted file mode 100644 index 6b86b48f..00000000 --- a/configs/yolov5/yolov5_l_1xb16_300e_coco.py +++ /dev/null @@ -1,158 +0,0 @@ -_base_ = ['./base_arch.py'] - -# ========================Suggested optional parameters======================== -# MODEL -num_classes = 71 -deepen_factor = 1.0 -widen_factor = 1.0 - -# DATA -dataset_type = 'sscma.CustomYOLOv5CocoDataset' -train_ann = 'train/_annotations.coco.json' -train_data = 'train/' # Prefix of train image path -val_ann = 'valid/_annotations.coco.json' -val_data = 'valid/' # Prefix of val image path - -# dataset link: https://universe.roboflow.com/team-roboflow/coco-128 -data_root = 'https://universe.roboflow.com/ds/z5UOcgxZzD?key=bwx9LQUT0t' -height = 640 -width = 640 -batch = 16 -workers = 2 -val_batch = batch -val_workers = workers -imgsz = (width, height) - -# TRAIN -persistent_workers = True - -# ================================END================================= - -# DATA -affine_scale = 0.5 -# MODEL -strides = [8, 16, 32] - -anchors = [ - [(10, 13), (16, 30), (33, 23)], # P3/8 - [(30, 61), (62, 45), (59, 119)], # P4/16 - [(116, 90), (156, 198), (373, 326)], # P5/32 -] - -model = dict( - type='mmyolo.YOLODetector', - backbone=dict( - type='YOLOv5CSPDarknet', - deepen_factor=deepen_factor, - widen_factor=widen_factor, - ), - neck=dict( - type='YOLOv5PAFPN', - deepen_factor=deepen_factor, - widen_factor=widen_factor, - ), - bbox_head=dict( - head_module=dict( - num_classes=num_classes, - in_channels=[256, 512, 1024], - widen_factor=widen_factor, - ), - ), -) - -# ======================datasets================== - - -batch_shapes_cfg = dict( - type='BatchShapePolicy', - batch_size=1, - img_size=imgsz[0], - # The image scale of padding should be divided by pad_size_divisor - size_divisor=32, - # Additional paddings for pixel scale - extra_pad_ratio=0.5, -) - -albu_train_transforms = [ - dict(type='Blur', p=0.01), - dict(type='MedianBlur', p=0.01), - dict(type='ToGray', p=0.01), - dict(type='CLAHE', p=0.01), -] - -pre_transform = [ - dict(type='LoadImageFromFile', file_client_args=dict(backend='disk')), - dict(type='LoadAnnotations', with_bbox=True), -] - -train_pipeline = [ - *pre_transform, - dict(type='Mosaic', img_scale=imgsz, pad_val=114.0, pre_transform=pre_transform), - dict( - type='YOLOv5RandomAffine', - max_rotate_degree=0.0, - max_shear_degree=0.0, - scaling_ratio_range=(1 - affine_scale, 1 + affine_scale), - # imgsz is (width, height) - border=(-imgsz[0] // 2, -imgsz[1] // 2), - border_val=(114, 114, 114), - ), - dict( - type='mmdet.Albu', - transforms=albu_train_transforms, - bbox_params=dict(type='BboxParams', format='pascal_voc', label_fields=['gt_bboxes_labels', 'gt_ignore_flags']), - keymap={'img': 'image', 'gt_bboxes': 'bboxes'}, - ), - dict(type='YOLOv5HSVRandomAug'), - dict(type='mmdet.RandomFlip', prob=0.5), - dict( - type='mmdet.PackDetInputs', meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape', 'flip', 'flip_direction') - ), -] - -train_dataloader = dict( - batch_size=batch, - num_workers=workers, - persistent_workers=persistent_workers, - pin_memory=True, - sampler=dict(type='DefaultSampler', shuffle=True), - dataset=dict( - type=dataset_type, - data_root=data_root, - ann_file=train_ann, - data_prefix=dict(img=train_data), - filter_cfg=dict(filter_empty_gt=False, min_size=32), - pipeline=train_pipeline, - ), -) - -test_pipeline = [ - dict(type='LoadImageFromFile', file_client_args=dict(backend='disk')), - dict(type='YOLOv5KeepRatioResize', scale=imgsz), - dict(type='LetterResize', scale=imgsz, allow_scale_up=False, pad_val=dict(img=114)), - dict(type='LoadAnnotations', with_bbox=True, _scope_='mmdet'), - dict( - type='mmdet.PackDetInputs', - meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape', 'scale_factor', 'pad_param'), - ), -] - -val_dataloader = dict( - batch_size=val_batch, - num_workers=val_workers, - persistent_workers=persistent_workers, - pin_memory=True, - drop_last=False, - sampler=dict(type='DefaultSampler', shuffle=False), - dataset=dict( - type=dataset_type, - data_root=data_root, - test_mode=True, - data_prefix=dict(img=val_data), - ann_file=val_ann, - pipeline=test_pipeline, - batch_shapes_cfg=batch_shapes_cfg, - ), -) - -test_dataloader = val_dataloader diff --git a/configs/yolov5/yolov5_m_1xb16_300e_coco.py b/configs/yolov5/yolov5_m_1xb16_300e_coco.py deleted file mode 100644 index 2d525181..00000000 --- a/configs/yolov5/yolov5_m_1xb16_300e_coco.py +++ /dev/null @@ -1,158 +0,0 @@ -_base_ = ['./base_arch.py'] - -# ========================Suggested optional parameters======================== -# MODEL -num_classes = 71 -deepen_factor = 0.67 -widen_factor = 0.75 - -# DATA -dataset_type = 'sscma.CustomYOLOv5CocoDataset' -train_ann = 'train/_annotations.coco.json' -train_data = 'train/' # Prefix of train image path -val_ann = 'valid/_annotations.coco.json' -val_data = 'valid/' # Prefix of val image path - -# dataset link: https://universe.roboflow.com/team-roboflow/coco-128 -data_root = 'https://universe.roboflow.com/ds/z5UOcgxZzD?key=bwx9LQUT0t' -height = 640 -width = 640 -batch = 16 -workers = 2 -val_batch = batch -val_workers = workers -imgsz = (width, height) - -# TRAIN -persistent_workers = True - -# ================================END================================= - -# DATA -affine_scale = 0.5 -# MODEL -strides = [8, 16, 32] - -anchors = [ - [(10, 13), (16, 30), (33, 23)], # P3/8 - [(30, 61), (62, 45), (59, 119)], # P4/16 - [(116, 90), (156, 198), (373, 326)], # P5/32 -] - -model = dict( - type='mmyolo.YOLODetector', - backbone=dict( - type='YOLOv5CSPDarknet', - deepen_factor=deepen_factor, - widen_factor=widen_factor, - ), - neck=dict( - type='YOLOv5PAFPN', - deepen_factor=deepen_factor, - widen_factor=widen_factor, - ), - bbox_head=dict( - head_module=dict( - num_classes=num_classes, - in_channels=[256, 512, 1024], - widen_factor=widen_factor, - ), - ), -) - -# ======================datasets================== - - -batch_shapes_cfg = dict( - type='BatchShapePolicy', - batch_size=1, - img_size=imgsz[0], - # The image scale of padding should be divided by pad_size_divisor - size_divisor=32, - # Additional paddings for pixel scale - extra_pad_ratio=0.5, -) - -albu_train_transforms = [ - dict(type='Blur', p=0.01), - dict(type='MedianBlur', p=0.01), - dict(type='ToGray', p=0.01), - dict(type='CLAHE', p=0.01), -] - -pre_transform = [ - dict(type='LoadImageFromFile', file_client_args=dict(backend='disk')), - dict(type='LoadAnnotations', with_bbox=True), -] - -train_pipeline = [ - *pre_transform, - dict(type='Mosaic', img_scale=imgsz, pad_val=114.0, pre_transform=pre_transform), - dict( - type='YOLOv5RandomAffine', - max_rotate_degree=0.0, - max_shear_degree=0.0, - scaling_ratio_range=(1 - affine_scale, 1 + affine_scale), - # imgsz is (width, height) - border=(-imgsz[0] // 2, -imgsz[1] // 2), - border_val=(114, 114, 114), - ), - dict( - type='mmdet.Albu', - transforms=albu_train_transforms, - bbox_params=dict(type='BboxParams', format='pascal_voc', label_fields=['gt_bboxes_labels', 'gt_ignore_flags']), - keymap={'img': 'image', 'gt_bboxes': 'bboxes'}, - ), - dict(type='YOLOv5HSVRandomAug'), - dict(type='mmdet.RandomFlip', prob=0.5), - dict( - type='mmdet.PackDetInputs', meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape', 'flip', 'flip_direction') - ), -] - -train_dataloader = dict( - batch_size=batch, - num_workers=workers, - persistent_workers=persistent_workers, - pin_memory=True, - sampler=dict(type='DefaultSampler', shuffle=True), - dataset=dict( - type=dataset_type, - data_root=data_root, - ann_file=train_ann, - data_prefix=dict(img=train_data), - filter_cfg=dict(filter_empty_gt=False, min_size=32), - pipeline=train_pipeline, - ), -) - -test_pipeline = [ - dict(type='LoadImageFromFile', file_client_args=dict(backend='disk')), - dict(type='YOLOv5KeepRatioResize', scale=imgsz), - dict(type='LetterResize', scale=imgsz, allow_scale_up=False, pad_val=dict(img=114)), - dict(type='LoadAnnotations', with_bbox=True, _scope_='mmdet'), - dict( - type='mmdet.PackDetInputs', - meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape', 'scale_factor', 'pad_param'), - ), -] - -val_dataloader = dict( - batch_size=val_batch, - num_workers=val_workers, - persistent_workers=persistent_workers, - pin_memory=True, - drop_last=False, - sampler=dict(type='DefaultSampler', shuffle=False), - dataset=dict( - type=dataset_type, - data_root=data_root, - test_mode=True, - data_prefix=dict(img=val_data), - ann_file=val_ann, - pipeline=test_pipeline, - batch_shapes_cfg=batch_shapes_cfg, - ), -) - -test_dataloader = val_dataloader diff --git a/configs/yolov5/yolov5_n_1xb16_300e_coco.py b/configs/yolov5/yolov5_n_1xb16_300e_coco.py deleted file mode 100644 index 3df3c3aa..00000000 --- a/configs/yolov5/yolov5_n_1xb16_300e_coco.py +++ /dev/null @@ -1,159 +0,0 @@ -_base_ = ['./base_arch.py'] - -# ========================Suggested optional parameters======================== -# MODEL -num_classes = 71 -deepen_factor = 0.33 -widen_factor = 0.25 - -# DATA -dataset_type = 'sscma.CustomYOLOv5CocoDataset' -train_ann = 'train/_annotations.coco.json' -train_data = 'train/' # Prefix of train image path -val_ann = 'valid/_annotations.coco.json' -val_data = 'valid/' # Prefix of val image path - -# dataset link: https://universe.roboflow.com/team-roboflow/coco-128 -data_root = 'https://universe.roboflow.com/ds/z5UOcgxZzD?key=bwx9LQUT0t' -height = 640 -width = 640 -imgsz = (width, height) -batch = 16 -workers = 2 -val_batch = batch -val_workers = workers - - -# TRAIN -persistent_workers = True - -# ================================END================================= - -# DATA -affine_scale = 0.5 -# MODEL -strides = [8, 16, 32] - -anchors = [ - [(10, 13), (16, 30), (33, 23)], # P3/8 - [(30, 61), (62, 45), (59, 119)], # P4/16 - [(116, 90), (156, 198), (373, 326)], # P5/32 -] - -model = dict( - type='mmyolo.YOLODetector', - backbone=dict( - type='YOLOv5CSPDarknet', - deepen_factor=deepen_factor, - widen_factor=widen_factor, - ), - neck=dict( - type='YOLOv5PAFPN', - deepen_factor=deepen_factor, - widen_factor=widen_factor, - ), - bbox_head=dict( - head_module=dict( - num_classes=num_classes, - in_channels=[256, 512, 1024], - widen_factor=widen_factor, - ), - ), -) - -# ======================datasets================== - - -batch_shapes_cfg = dict( - type='BatchShapePolicy', - batch_size=1, - img_size=imgsz[0], - # The image scale of padding should be divided by pad_size_divisor - size_divisor=32, - # Additional paddings for pixel scale - extra_pad_ratio=0.5, -) - -albu_train_transforms = [ - dict(type='Blur', p=0.01), - dict(type='MedianBlur', p=0.01), - dict(type='ToGray', p=0.01), - dict(type='CLAHE', p=0.01), -] - -pre_transform = [ - dict(type='LoadImageFromFile', file_client_args=dict(backend='disk')), - dict(type='LoadAnnotations', with_bbox=True), -] - -train_pipeline = [ - *pre_transform, - dict(type='Mosaic', img_scale=imgsz, pad_val=114.0, pre_transform=pre_transform), - dict( - type='YOLOv5RandomAffine', - max_rotate_degree=0.0, - max_shear_degree=0.0, - scaling_ratio_range=(1 - affine_scale, 1 + affine_scale), - # imgsz is (width, height) - border=(-imgsz[0] // 2, -imgsz[1] // 2), - border_val=(114, 114, 114), - ), - dict( - type='mmdet.Albu', - transforms=albu_train_transforms, - bbox_params=dict(type='BboxParams', format='pascal_voc', label_fields=['gt_bboxes_labels', 'gt_ignore_flags']), - keymap={'img': 'image', 'gt_bboxes': 'bboxes'}, - ), - dict(type='YOLOv5HSVRandomAug'), - dict(type='mmdet.RandomFlip', prob=0.5), - dict( - type='mmdet.PackDetInputs', meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape', 'flip', 'flip_direction') - ), -] - -train_dataloader = dict( - batch_size=batch, - num_workers=workers, - persistent_workers=persistent_workers, - pin_memory=True, - sampler=dict(type='DefaultSampler', shuffle=True), - dataset=dict( - type=dataset_type, - data_root=data_root, - ann_file=train_ann, - data_prefix=dict(img=train_data), - filter_cfg=dict(filter_empty_gt=False, min_size=32), - pipeline=train_pipeline, - ), -) - -test_pipeline = [ - dict(type='LoadImageFromFile', file_client_args=dict(backend='disk')), - dict(type='YOLOv5KeepRatioResize', scale=imgsz), - dict(type='LetterResize', scale=imgsz, allow_scale_up=False, pad_val=dict(img=114)), - dict(type='LoadAnnotations', with_bbox=True, _scope_='mmdet'), - dict( - type='mmdet.PackDetInputs', - meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape', 'scale_factor', 'pad_param'), - ), -] - -val_dataloader = dict( - batch_size=val_batch, - num_workers=val_workers, - persistent_workers=persistent_workers, - pin_memory=True, - drop_last=False, - sampler=dict(type='DefaultSampler', shuffle=False), - dataset=dict( - type=dataset_type, - data_root=data_root, - test_mode=True, - data_prefix=dict(img=val_data), - ann_file=val_ann, - pipeline=test_pipeline, - batch_shapes_cfg=batch_shapes_cfg, - ), -) - -test_dataloader = val_dataloader diff --git a/configs/yolov5/yolov5_s_1xb16_300e_coco.py b/configs/yolov5/yolov5_s_1xb16_300e_coco.py deleted file mode 100644 index 971fc301..00000000 --- a/configs/yolov5/yolov5_s_1xb16_300e_coco.py +++ /dev/null @@ -1,159 +0,0 @@ -_base_ = ['./base_arch.py'] - -# ========================Suggested optional parameters======================== -# MODEL -num_classes = 71 -deepen_factor = 0.33 -widen_factor = 0.5 - -# DATA -dataset_type = 'sscma.CustomYOLOv5CocoDataset' -train_ann = 'train/_annotations.coco.json' -train_data = 'train/' # Prefix of train image path -val_ann = 'valid/_annotations.coco.json' -val_data = 'valid/' # Prefix of val image path - -# dataset link: https://universe.roboflow.com/team-roboflow/coco-128 -data_root = 'https://universe.roboflow.com/ds/z5UOcgxZzD?key=bwx9LQUT0t' -height = 640 -width = 640 -imgsz = (width, height) -batch = 16 -workers = 2 -val_batch = batch -val_workers = workers - - -# TRAIN -persistent_workers = True - -# ================================END================================= - -# DATA -affine_scale = 0.5 -# MODEL -strides = [8, 16, 32] - -anchors = [ - [(10, 13), (16, 30), (33, 23)], # P3/8 - [(30, 61), (62, 45), (59, 119)], # P4/16 - [(116, 90), (156, 198), (373, 326)], # P5/32 -] - -model = dict( - type='mmyolo.YOLODetector', - backbone=dict( - type='YOLOv5CSPDarknet', - deepen_factor=deepen_factor, - widen_factor=widen_factor, - ), - neck=dict( - type='YOLOv5PAFPN', - deepen_factor=deepen_factor, - widen_factor=widen_factor, - ), - bbox_head=dict( - head_module=dict( - num_classes=num_classes, - in_channels=[256, 512, 1024], - widen_factor=widen_factor, - ), - ), -) - -# ======================datasets================== - - -batch_shapes_cfg = dict( - type='BatchShapePolicy', - batch_size=1, - img_size=imgsz[0], - # The image scale of padding should be divided by pad_size_divisor - size_divisor=32, - # Additional paddings for pixel scale - extra_pad_ratio=0.5, -) - -albu_train_transforms = [ - dict(type='Blur', p=0.01), - dict(type='MedianBlur', p=0.01), - dict(type='ToGray', p=0.01), - dict(type='CLAHE', p=0.01), -] - -pre_transform = [ - dict(type='LoadImageFromFile', file_client_args=dict(backend='disk')), - dict(type='LoadAnnotations', with_bbox=True), -] - -train_pipeline = [ - *pre_transform, - dict(type='Mosaic', img_scale=imgsz, pad_val=114.0, pre_transform=pre_transform), - dict( - type='YOLOv5RandomAffine', - max_rotate_degree=0.0, - max_shear_degree=0.0, - scaling_ratio_range=(1 - affine_scale, 1 + affine_scale), - # imgsz is (width, height) - border=(-imgsz[0] // 2, -imgsz[1] // 2), - border_val=(114, 114, 114), - ), - dict( - type='mmdet.Albu', - transforms=albu_train_transforms, - bbox_params=dict(type='BboxParams', format='pascal_voc', label_fields=['gt_bboxes_labels', 'gt_ignore_flags']), - keymap={'img': 'image', 'gt_bboxes': 'bboxes'}, - ), - dict(type='YOLOv5HSVRandomAug'), - dict(type='mmdet.RandomFlip', prob=0.5), - dict( - type='mmdet.PackDetInputs', meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape', 'flip', 'flip_direction') - ), -] - -train_dataloader = dict( - batch_size=batch, - num_workers=workers, - persistent_workers=persistent_workers, - pin_memory=True, - sampler=dict(type='DefaultSampler', shuffle=True), - dataset=dict( - type=dataset_type, - data_root=data_root, - ann_file=train_ann, - data_prefix=dict(img=train_data), - filter_cfg=dict(filter_empty_gt=False, min_size=32), - pipeline=train_pipeline, - ), -) - -test_pipeline = [ - dict(type='LoadImageFromFile', file_client_args=dict(backend='disk')), - dict(type='YOLOv5KeepRatioResize', scale=imgsz), - dict(type='LetterResize', scale=imgsz, allow_scale_up=False, pad_val=dict(img=114)), - dict(type='LoadAnnotations', with_bbox=True, _scope_='mmdet'), - dict( - type='mmdet.PackDetInputs', - meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape', 'scale_factor', 'pad_param'), - ), -] - -val_dataloader = dict( - batch_size=val_batch, - num_workers=val_workers, - persistent_workers=persistent_workers, - pin_memory=True, - drop_last=False, - sampler=dict(type='DefaultSampler', shuffle=False), - dataset=dict( - type=dataset_type, - data_root=data_root, - test_mode=True, - data_prefix=dict(img=val_data), - ann_file=val_ann, - pipeline=test_pipeline, - batch_shapes_cfg=batch_shapes_cfg, - ), -) - -test_dataloader = val_dataloader diff --git a/configs/yolov6/base.py b/configs/yolov6/base.py deleted file mode 100644 index 531cef12..00000000 --- a/configs/yolov6/base.py +++ /dev/null @@ -1,273 +0,0 @@ -_base_ = ['../_base_/default_runtime_det.py'] -default_scope = 'mmyolo' - - -# ========================Suggested optional parameters======================== -# DATA -# Types of datasets ,The type of the dataset, you can follow sscma/datasets/ to see the types we have defined, -# or you can use the types used by other mmlab libraries, -# but you need to prefix them with the appropriate prefixes to be on the safe side -dataset_type = 'sscma.CustomYOLOv5CocoDataset' -# Path to the dataset's root directory -# dataset link: https://universe.roboflow.com/team-roboflow/coco-128 -data_root = 'https://universe.roboflow.com/ds/z5UOcgxZzD?key=bwx9LQUT0t' -# Path to the annotation file for the training set, both absolute and relative paths are acceptable, -# if it is a relative path, it must be relative to "data_root". -train_ann = 'train/_annotations.coco.json' -# Path to the training set data file, both absolute and relative, if relative, it must be relative to "data_root". -train_data = 'train/' -# Path to the validation set annotation file, both absolute and relative paths are acceptable, -# if it is a relative path, it must be a relative path to data_root. -val_ann = 'valid/_annotations.coco.json' -# Path to the validation set data file, both absolute and relative paths are allowed, -# if it is a relative path, it must be a relative path to data_root. -val_data = 'valid/' -# Height of the model input data -height = 640 -# Width of the model input data -width = 640 -# The width and height of the model input data -imgsz = (width, height) -# persistent_workers must be False if num_workers is 0 -persistent_workers = True - -# MODEL -# Scaling factor for model depth -deepen_factor = 0.33 -# Scaling factor for model width -widen_factor = 0.5 -# Number of categories in the dataset -num_classes = 71 - -# TRAIN -# Learning rate of the model -lr = 0.01 -# Total number of rounds of model training -epochs = 300 -# Number of input data per iteration in the model training phase -batch = 64 -# Number of threads used to load data during training, this value should be adjusted accordingly to the training batch -workers = 8 -# Model weight saving interval in epochs -save_interval = 5 -# Last epoch number to switch training pipeline -num_last_epochs = 15 -# Learning rate scaling factor -lr_factor = 0.01 -# Optimizer weight decay value -weight_decay = 0.0005 -momentum=0.937 - -# VAL -# Number of input data per iteration in the model validation phase -val_batch = 1 -# Number of threads used to load data during validation, this value should be adjusted accordingly to the validation batch -val_workers = workers -# Model validation interval in epoch -val_interval = 5 -# The maximum checkpoints to keep. -max_keep_ckpts = 3 -# ================================END================================= - -# Config of batch shapes. Only on val. -# It means not used if batch_shapes_cfg is None. -batch_shapes_cfg = dict( - type='BatchShapePolicy', - batch_size=val_batch, - img_size=imgsz[0], - size_divisor=32, - extra_pad_ratio=0.5, -) - -# -----train val related----- -affine_scale = 0.5 # YOLOv5RandomAffine scaling ratio - -# Single-scale training is recommended to -# be turned on, which can speed up training. -env_cfg = dict(cudnn_benchmark=True) - -# ============================== Unmodified in most cases =================== -model = dict( - type='YOLODetector', - data_preprocessor=dict( - type='YOLOv5DetDataPreprocessor', mean=[0.0, 0.0, 0.0], std=[255.0, 255.0, 255.0], bgr_to_rgb=True - ), - backbone=dict( - type='YOLOv6EfficientRep', - deepen_factor=deepen_factor, - widen_factor=widen_factor, - norm_cfg=dict(type='BN', momentum=0.03, eps=0.001), - act_cfg=dict(type='ReLU', inplace=True), - ), - neck=dict( - type='YOLOv6RepPAFPN', - deepen_factor=deepen_factor, - widen_factor=widen_factor, - in_channels=[256, 512, 1024], - out_channels=[128, 256, 512], - num_csp_blocks=12, - norm_cfg=dict(type='BN', momentum=0.03, eps=0.001), - act_cfg=dict(type='ReLU', inplace=True), - ), - bbox_head=dict( - type='YOLOv6Head', - head_module=dict( - type='YOLOv6HeadModule', - num_classes=num_classes, - in_channels=[128, 256, 512], - widen_factor=widen_factor, - norm_cfg=dict(type='BN', momentum=0.03, eps=0.001), - act_cfg=dict(type='SiLU', inplace=True), - featmap_strides=[8, 16, 32], - ), - loss_bbox=dict( - type='IoULoss', iou_mode='giou', bbox_format='xyxy', reduction='mean', loss_weight=2.5, return_iou=False - ), - ), - train_cfg=dict( - initial_epoch=4, - initial_assigner=dict( - type='BatchATSSAssigner', num_classes=num_classes, topk=9, iou_calculator=dict(type='mmdet.BboxOverlaps2D') - ), - assigner=dict(type='BatchTaskAlignedAssigner', num_classes=num_classes, topk=13, alpha=1, beta=6), - ), - test_cfg=dict( - multi_label=True, nms_pre=30000, score_thr=0.001, nms=dict(type='nms', iou_threshold=0.65), max_per_img=300 - ), -) - -# The training pipeline of YOLOv6 is basically the same as YOLOv5. -# The difference is that Mosaic and RandomAffine will be closed in the last 15 epochs. # noqa -pre_transform = [dict(type='LoadImageFromFile', backend_args=None), dict(type='LoadAnnotations', with_bbox=True)] - -train_pipeline = [ - *pre_transform, - dict(type='Mosaic', img_scale=imgsz, pad_val=114.0, pre_transform=pre_transform), - dict( - type='YOLOv5RandomAffine', - max_rotate_degree=0.0, - max_translate_ratio=0.1, - scaling_ratio_range=(1 - affine_scale, 1 + affine_scale), - # imgsz is (width, height) - border=(-imgsz[0] // 2, -imgsz[1] // 2), - border_val=(114, 114, 114), - max_shear_degree=0.0, - ), - dict(type='YOLOv5HSVRandomAug'), - dict(type='mmdet.RandomFlip', prob=0.5), - dict( - type='mmdet.PackDetInputs', meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape', 'flip', 'flip_direction') - ), -] - -train_pipeline_stage2 = [ - *pre_transform, - dict(type='YOLOv5KeepRatioResize', scale=imgsz), - dict(type='LetterResize', scale=imgsz, allow_scale_up=True, pad_val=dict(img=114)), - dict( - type='YOLOv5RandomAffine', - max_rotate_degree=0.0, - max_translate_ratio=0.1, - scaling_ratio_range=(1 - affine_scale, 1 + affine_scale), - max_shear_degree=0.0, - ), - dict(type='YOLOv5HSVRandomAug'), - dict(type='mmdet.RandomFlip', prob=0.5), - dict( - type='mmdet.PackDetInputs', meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape', 'flip', 'flip_direction') - ), -] - -train_dataloader = dict( - batch_size=batch, - num_workers=workers, - collate_fn=dict(type='yolov5_collate'), - persistent_workers=persistent_workers, - pin_memory=True, - sampler=dict(type='DefaultSampler', shuffle=True), - dataset=dict( - type=dataset_type, - data_root=data_root, - ann_file=train_ann, - data_prefix=dict(img=train_data), - filter_cfg=dict(filter_empty_gt=False, min_size=32), - pipeline=train_pipeline, - ), -) - -test_pipeline = [ - dict(type='LoadImageFromFile', backend_args=None), - dict(type='YOLOv5KeepRatioResize', scale=imgsz), - dict(type='LetterResize', scale=imgsz, allow_scale_up=False, pad_val=dict(img=114)), - dict(type='LoadAnnotations', with_bbox=True, _scope_='mmdet'), - dict( - type='mmdet.PackDetInputs', - meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape', 'scale_factor', 'pad_param'), - ), -] - -val_dataloader = dict( - batch_size=val_batch, - num_workers=val_workers, - persistent_workers=persistent_workers, - pin_memory=True, - drop_last=False, - sampler=dict(type='DefaultSampler', shuffle=False), - dataset=dict( - type=dataset_type, - data_root=data_root, - test_mode=True, - data_prefix=dict(img=val_data), - ann_file=val_ann, - pipeline=test_pipeline, - batch_shapes_cfg=batch_shapes_cfg, - ), -) - -test_dataloader = val_dataloader - -# Optimizer and learning rate scheduler of YOLOv6 are basically the same as YOLOv5. # noqa -# The difference is that the scheduler_type of YOLOv6 is cosine. -optim_wrapper = dict( - type='OptimWrapper', - optimizer=dict( - type='SGD', - lr=lr, - momentum=momentum, - weight_decay=weight_decay, - nesterov=True, - batch_size_per_gpu=batch, - ), - constructor='YOLOv5OptimizerConstructor', -) - -default_hooks = dict( - param_scheduler=dict( - type='YOLOv5ParamSchedulerHook', scheduler_type='cosine', lr_factor=lr_factor, max_epochs=epochs - ), - checkpoint=dict(type='CheckpointHook', interval=save_interval, max_keep_ckpts=max_keep_ckpts, save_best='auto'), -) - -custom_hooks = [ - dict( - type='EMAHook', ema_type='ExpMomentumEMA', momentum=0.0001, update_buffers=True, strict_load=False, priority=49 - ), - dict( - type='mmdet.PipelineSwitchHook', - switch_epoch=epochs - num_last_epochs, - switch_pipeline=train_pipeline_stage2, - ), -] - -val_evaluator = dict(type='mmdet.CocoMetric', proposal_nums=(100, 1, 10), ann_file=data_root + val_ann, metric='bbox') -test_evaluator = val_evaluator - -train_cfg = dict( - type='EpochBasedTrainLoop', - max_epochs=epochs, - val_interval=val_interval, - dynamic_intervals=[(epochs - num_last_epochs, 1)], - _delete_=True, -) -val_cfg = dict(type='ValLoop') -test_cfg = dict(type='TestLoop') diff --git a/configs/yolov6/yolov6_l_syncbn_fast_8xb32-300e_coco.py b/configs/yolov6/yolov6_l_syncbn_fast_8xb32-300e_coco.py deleted file mode 100644 index d2d66c8a..00000000 --- a/configs/yolov6/yolov6_l_syncbn_fast_8xb32-300e_coco.py +++ /dev/null @@ -1,29 +0,0 @@ -_base_ = './yolov6_m_syncbn_fast_8xb32-300e_coco.py' - -# ========================Suggested optional parameters======================== - -# -----model related----- -# The scaling factor that controls the depth of the network structure -deepen_factor = 1 -# The scaling factor that controls the width of the network structure -widen_factor = 1 -# ================================END================================= - -# ============================== Unmodified in most cases =================== -model = dict( - backbone=dict( - deepen_factor=deepen_factor, - widen_factor=widen_factor, - hidden_ratio=1.0 / 2, - block_cfg=dict(type='ConvWrapper', norm_cfg=dict(type='BN', momentum=0.03, eps=0.001)), - act_cfg=dict(type='SiLU', inplace=True), - ), - neck=dict( - deepen_factor=deepen_factor, - widen_factor=widen_factor, - hidden_ratio=1.0 / 2, - block_cfg=dict(type='ConvWrapper', norm_cfg=dict(type='BN', momentum=0.03, eps=0.001)), - block_act_cfg=dict(type='SiLU', inplace=True), - ), - bbox_head=dict(head_module=dict(widen_factor=widen_factor)), -) diff --git a/configs/yolov6/yolov6_m_syncbn_fast_8xb32-300e_coco.py b/configs/yolov6/yolov6_m_syncbn_fast_8xb32-300e_coco.py deleted file mode 100644 index 93a3365f..00000000 --- a/configs/yolov6/yolov6_m_syncbn_fast_8xb32-300e_coco.py +++ /dev/null @@ -1,68 +0,0 @@ -_base_ = './base.py' - -# ========================Suggested optional parameters======================== -# -----model related----- -# The scaling factor that controls the depth of the network structure -deepen_factor = 0.6 -# The scaling factor that controls the width of the network structure -widen_factor = 0.75 - -# DATA -height = 640 -width = 640 -imgsz = (width, height) -# ================================END================================= - -# -----train val related----- -affine_scale = 0.9 # YOLOv5RandomAffine scaling ratio - -# ============================== Unmodified in most cases =================== - - -model = dict( - backbone=dict( - type='YOLOv6CSPBep', - deepen_factor=deepen_factor, - widen_factor=widen_factor, - hidden_ratio=2.0 / 3, - block_cfg=dict(type='RepVGGBlock'), - act_cfg=dict(type='ReLU', inplace=True), - ), - neck=dict( - type='YOLOv6CSPRepPAFPN', - deepen_factor=deepen_factor, - widen_factor=widen_factor, - block_cfg=dict(type='RepVGGBlock'), - hidden_ratio=2.0 / 3, - block_act_cfg=dict(type='ReLU', inplace=True), - ), - bbox_head=dict(type='YOLOv6Head', head_module=dict(widen_factor=widen_factor)), -) - -pre_transform = [dict(type='LoadImageFromFile', backend_args=None), dict(type='LoadAnnotations', with_bbox=True)] - -mosaic_affine_pipeline = [ - dict(type='Mosaic', img_scale=imgsz, pad_val=114.0, pre_transform=pre_transform), - dict( - type='YOLOv5RandomAffine', - max_rotate_degree=0.0, - max_shear_degree=0.0, - scaling_ratio_range=(1 - affine_scale, 1 + affine_scale), - # imgsz is (width, height) - border=(-imgsz[0] // 2, -imgsz[1] // 2), - border_val=(114, 114, 114), - ), -] - -train_pipeline = [ - *pre_transform, - *mosaic_affine_pipeline, - dict(type='YOLOv5MixUp', prob=0.1, pre_transform=[*pre_transform, *mosaic_affine_pipeline]), - dict(type='YOLOv5HSVRandomAug'), - dict(type='mmdet.RandomFlip', prob=0.5), - dict( - type='mmdet.PackDetInputs', meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape', 'flip', 'flip_direction') - ), -] - -train_dataloader = dict(dataset=dict(pipeline=train_pipeline)) diff --git a/configs/yolov6/yolov6_n_syncbn_fast_8xb32-400e_coco.py b/configs/yolov6/yolov6_n_syncbn_fast_8xb32-400e_coco.py deleted file mode 100644 index ac7d5fb5..00000000 --- a/configs/yolov6/yolov6_n_syncbn_fast_8xb32-400e_coco.py +++ /dev/null @@ -1,21 +0,0 @@ -_base_ = './base.py' - -# ========================Suggested optional parameters======================== -# -----model related----- -# The scaling factor that controls the depth of the network structure -deepen_factor = 0.33 -# The scaling factor that controls the width of the network structure -widen_factor = 0.25 - -# -----train val related----- -lr_factor = 0.02 # Learning rate scaling factor -# ================================END================================= - -# ============================== Unmodified in most cases =================== -model = dict( - backbone=dict(deepen_factor=deepen_factor, widen_factor=widen_factor), - neck=dict(deepen_factor=deepen_factor, widen_factor=widen_factor), - bbox_head=dict(head_module=dict(widen_factor=widen_factor), loss_bbox=dict(iou_mode='siou')), -) - -default_hooks = dict(param_scheduler=dict(lr_factor=lr_factor)) diff --git a/configs/yolov6/yolov6_s_syncbn_fast_8xb32-300e_coco.py b/configs/yolov6/yolov6_s_syncbn_fast_8xb32-300e_coco.py deleted file mode 100644 index 531cef12..00000000 --- a/configs/yolov6/yolov6_s_syncbn_fast_8xb32-300e_coco.py +++ /dev/null @@ -1,273 +0,0 @@ -_base_ = ['../_base_/default_runtime_det.py'] -default_scope = 'mmyolo' - - -# ========================Suggested optional parameters======================== -# DATA -# Types of datasets ,The type of the dataset, you can follow sscma/datasets/ to see the types we have defined, -# or you can use the types used by other mmlab libraries, -# but you need to prefix them with the appropriate prefixes to be on the safe side -dataset_type = 'sscma.CustomYOLOv5CocoDataset' -# Path to the dataset's root directory -# dataset link: https://universe.roboflow.com/team-roboflow/coco-128 -data_root = 'https://universe.roboflow.com/ds/z5UOcgxZzD?key=bwx9LQUT0t' -# Path to the annotation file for the training set, both absolute and relative paths are acceptable, -# if it is a relative path, it must be relative to "data_root". -train_ann = 'train/_annotations.coco.json' -# Path to the training set data file, both absolute and relative, if relative, it must be relative to "data_root". -train_data = 'train/' -# Path to the validation set annotation file, both absolute and relative paths are acceptable, -# if it is a relative path, it must be a relative path to data_root. -val_ann = 'valid/_annotations.coco.json' -# Path to the validation set data file, both absolute and relative paths are allowed, -# if it is a relative path, it must be a relative path to data_root. -val_data = 'valid/' -# Height of the model input data -height = 640 -# Width of the model input data -width = 640 -# The width and height of the model input data -imgsz = (width, height) -# persistent_workers must be False if num_workers is 0 -persistent_workers = True - -# MODEL -# Scaling factor for model depth -deepen_factor = 0.33 -# Scaling factor for model width -widen_factor = 0.5 -# Number of categories in the dataset -num_classes = 71 - -# TRAIN -# Learning rate of the model -lr = 0.01 -# Total number of rounds of model training -epochs = 300 -# Number of input data per iteration in the model training phase -batch = 64 -# Number of threads used to load data during training, this value should be adjusted accordingly to the training batch -workers = 8 -# Model weight saving interval in epochs -save_interval = 5 -# Last epoch number to switch training pipeline -num_last_epochs = 15 -# Learning rate scaling factor -lr_factor = 0.01 -# Optimizer weight decay value -weight_decay = 0.0005 -momentum=0.937 - -# VAL -# Number of input data per iteration in the model validation phase -val_batch = 1 -# Number of threads used to load data during validation, this value should be adjusted accordingly to the validation batch -val_workers = workers -# Model validation interval in epoch -val_interval = 5 -# The maximum checkpoints to keep. -max_keep_ckpts = 3 -# ================================END================================= - -# Config of batch shapes. Only on val. -# It means not used if batch_shapes_cfg is None. -batch_shapes_cfg = dict( - type='BatchShapePolicy', - batch_size=val_batch, - img_size=imgsz[0], - size_divisor=32, - extra_pad_ratio=0.5, -) - -# -----train val related----- -affine_scale = 0.5 # YOLOv5RandomAffine scaling ratio - -# Single-scale training is recommended to -# be turned on, which can speed up training. -env_cfg = dict(cudnn_benchmark=True) - -# ============================== Unmodified in most cases =================== -model = dict( - type='YOLODetector', - data_preprocessor=dict( - type='YOLOv5DetDataPreprocessor', mean=[0.0, 0.0, 0.0], std=[255.0, 255.0, 255.0], bgr_to_rgb=True - ), - backbone=dict( - type='YOLOv6EfficientRep', - deepen_factor=deepen_factor, - widen_factor=widen_factor, - norm_cfg=dict(type='BN', momentum=0.03, eps=0.001), - act_cfg=dict(type='ReLU', inplace=True), - ), - neck=dict( - type='YOLOv6RepPAFPN', - deepen_factor=deepen_factor, - widen_factor=widen_factor, - in_channels=[256, 512, 1024], - out_channels=[128, 256, 512], - num_csp_blocks=12, - norm_cfg=dict(type='BN', momentum=0.03, eps=0.001), - act_cfg=dict(type='ReLU', inplace=True), - ), - bbox_head=dict( - type='YOLOv6Head', - head_module=dict( - type='YOLOv6HeadModule', - num_classes=num_classes, - in_channels=[128, 256, 512], - widen_factor=widen_factor, - norm_cfg=dict(type='BN', momentum=0.03, eps=0.001), - act_cfg=dict(type='SiLU', inplace=True), - featmap_strides=[8, 16, 32], - ), - loss_bbox=dict( - type='IoULoss', iou_mode='giou', bbox_format='xyxy', reduction='mean', loss_weight=2.5, return_iou=False - ), - ), - train_cfg=dict( - initial_epoch=4, - initial_assigner=dict( - type='BatchATSSAssigner', num_classes=num_classes, topk=9, iou_calculator=dict(type='mmdet.BboxOverlaps2D') - ), - assigner=dict(type='BatchTaskAlignedAssigner', num_classes=num_classes, topk=13, alpha=1, beta=6), - ), - test_cfg=dict( - multi_label=True, nms_pre=30000, score_thr=0.001, nms=dict(type='nms', iou_threshold=0.65), max_per_img=300 - ), -) - -# The training pipeline of YOLOv6 is basically the same as YOLOv5. -# The difference is that Mosaic and RandomAffine will be closed in the last 15 epochs. # noqa -pre_transform = [dict(type='LoadImageFromFile', backend_args=None), dict(type='LoadAnnotations', with_bbox=True)] - -train_pipeline = [ - *pre_transform, - dict(type='Mosaic', img_scale=imgsz, pad_val=114.0, pre_transform=pre_transform), - dict( - type='YOLOv5RandomAffine', - max_rotate_degree=0.0, - max_translate_ratio=0.1, - scaling_ratio_range=(1 - affine_scale, 1 + affine_scale), - # imgsz is (width, height) - border=(-imgsz[0] // 2, -imgsz[1] // 2), - border_val=(114, 114, 114), - max_shear_degree=0.0, - ), - dict(type='YOLOv5HSVRandomAug'), - dict(type='mmdet.RandomFlip', prob=0.5), - dict( - type='mmdet.PackDetInputs', meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape', 'flip', 'flip_direction') - ), -] - -train_pipeline_stage2 = [ - *pre_transform, - dict(type='YOLOv5KeepRatioResize', scale=imgsz), - dict(type='LetterResize', scale=imgsz, allow_scale_up=True, pad_val=dict(img=114)), - dict( - type='YOLOv5RandomAffine', - max_rotate_degree=0.0, - max_translate_ratio=0.1, - scaling_ratio_range=(1 - affine_scale, 1 + affine_scale), - max_shear_degree=0.0, - ), - dict(type='YOLOv5HSVRandomAug'), - dict(type='mmdet.RandomFlip', prob=0.5), - dict( - type='mmdet.PackDetInputs', meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape', 'flip', 'flip_direction') - ), -] - -train_dataloader = dict( - batch_size=batch, - num_workers=workers, - collate_fn=dict(type='yolov5_collate'), - persistent_workers=persistent_workers, - pin_memory=True, - sampler=dict(type='DefaultSampler', shuffle=True), - dataset=dict( - type=dataset_type, - data_root=data_root, - ann_file=train_ann, - data_prefix=dict(img=train_data), - filter_cfg=dict(filter_empty_gt=False, min_size=32), - pipeline=train_pipeline, - ), -) - -test_pipeline = [ - dict(type='LoadImageFromFile', backend_args=None), - dict(type='YOLOv5KeepRatioResize', scale=imgsz), - dict(type='LetterResize', scale=imgsz, allow_scale_up=False, pad_val=dict(img=114)), - dict(type='LoadAnnotations', with_bbox=True, _scope_='mmdet'), - dict( - type='mmdet.PackDetInputs', - meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape', 'scale_factor', 'pad_param'), - ), -] - -val_dataloader = dict( - batch_size=val_batch, - num_workers=val_workers, - persistent_workers=persistent_workers, - pin_memory=True, - drop_last=False, - sampler=dict(type='DefaultSampler', shuffle=False), - dataset=dict( - type=dataset_type, - data_root=data_root, - test_mode=True, - data_prefix=dict(img=val_data), - ann_file=val_ann, - pipeline=test_pipeline, - batch_shapes_cfg=batch_shapes_cfg, - ), -) - -test_dataloader = val_dataloader - -# Optimizer and learning rate scheduler of YOLOv6 are basically the same as YOLOv5. # noqa -# The difference is that the scheduler_type of YOLOv6 is cosine. -optim_wrapper = dict( - type='OptimWrapper', - optimizer=dict( - type='SGD', - lr=lr, - momentum=momentum, - weight_decay=weight_decay, - nesterov=True, - batch_size_per_gpu=batch, - ), - constructor='YOLOv5OptimizerConstructor', -) - -default_hooks = dict( - param_scheduler=dict( - type='YOLOv5ParamSchedulerHook', scheduler_type='cosine', lr_factor=lr_factor, max_epochs=epochs - ), - checkpoint=dict(type='CheckpointHook', interval=save_interval, max_keep_ckpts=max_keep_ckpts, save_best='auto'), -) - -custom_hooks = [ - dict( - type='EMAHook', ema_type='ExpMomentumEMA', momentum=0.0001, update_buffers=True, strict_load=False, priority=49 - ), - dict( - type='mmdet.PipelineSwitchHook', - switch_epoch=epochs - num_last_epochs, - switch_pipeline=train_pipeline_stage2, - ), -] - -val_evaluator = dict(type='mmdet.CocoMetric', proposal_nums=(100, 1, 10), ann_file=data_root + val_ann, metric='bbox') -test_evaluator = val_evaluator - -train_cfg = dict( - type='EpochBasedTrainLoop', - max_epochs=epochs, - val_interval=val_interval, - dynamic_intervals=[(epochs - num_last_epochs, 1)], - _delete_=True, -) -val_cfg = dict(type='ValLoop') -test_cfg = dict(type='TestLoop') diff --git a/configs/yolov6/yolov6_t_syncbn_fast_8xb32-300e_coco.py b/configs/yolov6/yolov6_t_syncbn_fast_8xb32-300e_coco.py deleted file mode 100644 index 8891e835..00000000 --- a/configs/yolov6/yolov6_t_syncbn_fast_8xb32-300e_coco.py +++ /dev/null @@ -1,16 +0,0 @@ -_base_ = './base.py' - -# ========================Suggested optional parameters======================== -# -----model related----- -# The scaling factor that controls the depth of the network structure -deepen_factor = 0.33 -# The scaling factor that controls the width of the network structure -widen_factor = 0.375 -# ================================END================================= - -# ============================== Unmodified in most cases =================== -model = dict( - backbone=dict(deepen_factor=deepen_factor, widen_factor=widen_factor), - neck=dict(deepen_factor=deepen_factor, widen_factor=widen_factor), - bbox_head=dict(type='YOLOv6Head', head_module=dict(widen_factor=widen_factor), loss_bbox=dict(iou_mode='siou')), -) diff --git a/configs/yolov7/base.py b/configs/yolov7/base.py deleted file mode 100644 index dba6a170..00000000 --- a/configs/yolov7/base.py +++ /dev/null @@ -1,337 +0,0 @@ -_base_ = ['../_base_/default_runtime_det.py'] -default_scope = 'mmyolo' - -# ========================Suggested optional parameters======================== -# DATA -# Types of datasets ,The type of the dataset, you can follow sscma/datasets/ to see the types we have defined, -# or you can use the types used by other mmlab libraries, -# but you need to prefix them with the appropriate prefixes to be on the safe side -dataset_type = 'sscma.CustomYOLOv5CocoDataset' -# Path to the dataset's root directory -# dataset link: https://universe.roboflow.com/team-roboflow/coco-128 -data_root = 'https://universe.roboflow.com/ds/z5UOcgxZzD?key=bwx9LQUT0t' -# Path to the annotation file for the training set, both absolute and relative paths are acceptable, -# if it is a relative path, it must be relative to "data_root". -train_ann = 'train/_annotations.coco.json' -# Path to the training set data file, both absolute and relative, if relative, it must be relative to "data_root". -train_data = 'train/' -# Path to the validation set annotation file, both absolute and relative paths are acceptable, -# if it is a relative path, it must be a relative path to data_root. -val_ann = 'valid/_annotations.coco.json' -# Path to the validation set data file, both absolute and relative paths are allowed, -# if it is a relative path, it must be a relative path to data_root. -val_data = 'valid/' -# Height of the model input data -height = 640 -# Width of the model input data -width = 640 -# The width and height of the model input data -imgsz = (width, height) -# persistent_workers must be False if num_workers is 0 -persistent_workers = True - -# MODEL -# Number of categories in the dataset -num_classes = 71 - -# TRAIN -# Learning rate of the model -lr = 0.01 -# Total number of rounds of model training -epochs = 300 -# Number of input data per iteration in the model training phase -batch = 32 -# Number of threads used to load data during training, this value should be adjusted accordingly to the training batch -workers = 8 -# Model weight saving interval in epochs -save_interval = 5 -# Last epoch number to switch training pipeline -num_last_epochs = 15 -# Learning rate scaling factor -lr_factor = 0.01 -# Optimizer weight decay value -weight_decay = 0.0005 -momentum=0.937 - -# VAL -# Number of input data per iteration in the model validation phase -val_batch = 1 -# Number of threads used to load data during validation, this value should be adjusted accordingly to the validation batch -val_workers = workers -# Model validation interval in epoch -val_interval = 5 -# The maximum checkpoints to keep. -max_keep_ckpts = 1 -# ================================END================================= -# -----model related----- -# Basic size of multi-scale prior box -anchors = [ - [(12, 16), (19, 36), (40, 28)], # P3/8 - [(36, 75), (76, 55), (72, 146)], # P4/16 - [(142, 110), (192, 243), (459, 401)], # P5/32 -] - - -num_epoch_stage2 = 30 # The last 30 epochs switch evaluation interval -val_interval_stage2 = 1 # Evaluation interval -# ========================Possible modified parameters======================== - - -model_test_cfg = dict( - # The config of multi-label for multi-class prediction. - multi_label=True, - # The number of boxes before NMS. - nms_pre=30000, - score_thr=0.001, # Threshold to filter out boxes. - nms=dict(type='nms', iou_threshold=0.65), # NMS type and threshold - max_per_img=300, -) # Max number of detections of each image - - -# Config of batch shapes. Only on val. -# It means not used if batch_shapes_cfg is None. -batch_shapes_cfg = dict( - type='BatchShapePolicy', - batch_size=val_batch, - img_size=imgsz[0], - # The image scale of padding should be divided by pad_size_divisor - size_divisor=32, - # Additional paddings for pixel scale - extra_pad_ratio=0.5, -) - -# -----model related----- -strides = [8, 16, 32] # Strides of multi-scale prior box -num_det_layers = 3 # The number of model output scales -norm_cfg = dict(type='BN', momentum=0.03, eps=0.001) - -# Data augmentation -max_translate_ratio = 0.2 # YOLOv5RandomAffine -scaling_ratio_range = (0.1, 2.0) # YOLOv5RandomAffine -mixup_prob = 0.15 # YOLOv5MixUp -randchoice_mosaic_prob = [0.8, 0.2] -mixup_alpha = 8.0 # YOLOv5MixUp -mixup_beta = 8.0 # YOLOv5MixUp - -# -----train val related----- -loss_cls_weight = 0.3 -loss_bbox_weight = 0.05 -loss_obj_weight = 0.7 -# BatchYOLOv7Assigner params -simota_candidate_topk = 10 -simota_iou_weight = 3.0 -simota_cls_weight = 1.0 -prior_match_thr = 4.0 # Priori box matching threshold -obj_level_weights = [4.0, 1.0, 0.4] # The obj loss weights of the three output layers - -# Single-scale training is recommended to -# be turned on, which can speed up training. -env_cfg = dict(cudnn_benchmark=True) - -# ===============================Unmodified in most cases==================== -model = dict( - type='YOLODetector', - data_preprocessor=dict( - type='YOLOv5DetDataPreprocessor', mean=[0.0, 0.0, 0.0], std=[255.0, 255.0, 255.0], bgr_to_rgb=True - ), - backbone=dict(type='YOLOv7Backbone', arch='L', norm_cfg=norm_cfg, act_cfg=dict(type='SiLU', inplace=True)), - neck=dict( - type='YOLOv7PAFPN', - block_cfg=dict(type='ELANBlock', middle_ratio=0.5, block_ratio=0.25, num_blocks=4, num_convs_in_block=1), - upsample_feats_cat_first=False, - in_channels=[512, 1024, 1024], - # The real output channel will be multiplied by 2 - out_channels=[128, 256, 512], - norm_cfg=norm_cfg, - act_cfg=dict(type='SiLU', inplace=True), - ), - bbox_head=dict( - type='YOLOv7Head', - head_module=dict( - type='YOLOv7HeadModule', - num_classes=num_classes, - in_channels=[256, 512, 1024], - featmap_strides=strides, - num_base_priors=3, - ), - prior_generator=dict(type='mmdet.YOLOAnchorGenerator', base_sizes=anchors, strides=strides), - # scaled based on number of detection layers - loss_cls=dict( - type='mmdet.CrossEntropyLoss', - use_sigmoid=True, - reduction='mean', - loss_weight=loss_cls_weight * (num_classes / 80 * 3 / num_det_layers), - ), - loss_bbox=dict( - type='IoULoss', - iou_mode='ciou', - bbox_format='xywh', - reduction='mean', - loss_weight=loss_bbox_weight * (3 / num_det_layers), - return_iou=True, - ), - loss_obj=dict( - type='mmdet.CrossEntropyLoss', - use_sigmoid=True, - reduction='mean', - loss_weight=loss_obj_weight * ((imgsz[0] / 640) ** 2 * 3 / num_det_layers), - ), - prior_match_thr=prior_match_thr, - obj_level_weights=obj_level_weights, - # BatchYOLOv7Assigner params - simota_candidate_topk=simota_candidate_topk, - simota_iou_weight=simota_iou_weight, - simota_cls_weight=simota_cls_weight, - ), - test_cfg=model_test_cfg, -) - -pre_transform = [dict(type='LoadImageFromFile', backend_args=None), dict(type='LoadAnnotations', with_bbox=True)] - -mosiac4_pipeline = [ - dict(type='Mosaic', img_scale=imgsz, pad_val=114.0, pre_transform=pre_transform), - dict( - type='YOLOv5RandomAffine', - max_rotate_degree=0.0, - max_shear_degree=0.0, - max_translate_ratio=max_translate_ratio, # note - scaling_ratio_range=scaling_ratio_range, # note - # imgsz is (width, height) - border=(-imgsz[0] // 2, -imgsz[1] // 2), - border_val=(114, 114, 114), - ), -] - -mosiac9_pipeline = [ - dict(type='Mosaic9', img_scale=imgsz, pad_val=114.0, pre_transform=pre_transform), - dict( - type='YOLOv5RandomAffine', - max_rotate_degree=0.0, - max_shear_degree=0.0, - max_translate_ratio=max_translate_ratio, # note - scaling_ratio_range=scaling_ratio_range, # note - # imgsz is (width, height) - border=(-imgsz[0] // 2, -imgsz[1] // 2), - border_val=(114, 114, 114), - ), -] - -randchoice_mosaic_pipeline = dict( - type='RandomChoice', transforms=[mosiac4_pipeline, mosiac9_pipeline], prob=randchoice_mosaic_prob -) - -train_pipeline = [ - *pre_transform, - randchoice_mosaic_pipeline, - dict( - type='YOLOv5MixUp', - alpha=mixup_alpha, # note - beta=mixup_beta, # note - prob=mixup_prob, - pre_transform=[*pre_transform, randchoice_mosaic_pipeline], - ), - dict(type='YOLOv5HSVRandomAug'), - dict(type='mmdet.RandomFlip', prob=0.5), - dict( - type='mmdet.PackDetInputs', meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape', 'flip', 'flip_direction') - ), -] - -train_dataloader = dict( - batch_size=batch, - num_workers=workers, - persistent_workers=persistent_workers, - pin_memory=True, - sampler=dict(type='DefaultSampler', shuffle=True), - collate_fn=dict(type='yolov5_collate'), # FASTER - dataset=dict( - type=dataset_type, - data_root=data_root, - ann_file=train_ann, - data_prefix=dict(img=train_data), - filter_cfg=dict(filter_empty_gt=False, min_size=32), - pipeline=train_pipeline, - ), -) - -test_pipeline = [ - dict(type='LoadImageFromFile', backend_args=None), - dict(type='YOLOv5KeepRatioResize', scale=imgsz), - dict(type='LetterResize', scale=imgsz, allow_scale_up=False, pad_val=dict(img=114)), - dict(type='LoadAnnotations', with_bbox=True, _scope_='mmdet'), - dict( - type='mmdet.PackDetInputs', - meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape', 'scale_factor', 'pad_param'), - ), -] - -val_dataloader = dict( - batch_size=val_batch, - num_workers=val_workers, - persistent_workers=persistent_workers, - pin_memory=True, - drop_last=False, - sampler=dict(type='DefaultSampler', shuffle=False), - dataset=dict( - type=dataset_type, - data_root=data_root, - test_mode=True, - data_prefix=dict(img=val_data), - ann_file=val_ann, - pipeline=test_pipeline, - batch_shapes_cfg=batch_shapes_cfg, - ), -) - -test_dataloader = val_dataloader - -param_scheduler = None -optim_wrapper = dict( - type='OptimWrapper', - optimizer=dict( - type='SGD', - lr=lr, - momentum=momentum, - weight_decay=weight_decay, - nesterov=True, - batch_size_per_gpu=batch, - ), - constructor='YOLOv7OptimWrapperConstructor', -) - -default_hooks = dict( - param_scheduler=dict( - type='YOLOv5ParamSchedulerHook', scheduler_type='cosine', lr_factor=lr_factor, max_epochs=epochs # note - ), - checkpoint=dict( - type='CheckpointHook', - save_param_scheduler=False, - interval=save_interval, - save_best='auto', - max_keep_ckpts=max_keep_ckpts, - ), -) - -custom_hooks = [ - dict( - type='EMAHook', ema_type='ExpMomentumEMA', momentum=0.0001, update_buffers=True, strict_load=False, priority=49 - ) -] - -val_evaluator = dict( - type='mmdet.CocoMetric', - proposal_nums=(100, 1, 10), # Can be accelerated - ann_file=data_root + val_ann, - metric='bbox', -) -test_evaluator = val_evaluator - -train_cfg = dict( - type='EpochBasedTrainLoop', - max_epochs=epochs, - val_interval=val_interval, - dynamic_intervals=[(epochs - num_epoch_stage2, val_interval_stage2)], - _delete_=True, -) -val_cfg = dict(type='ValLoop') -test_cfg = dict(type='TestLoop') diff --git a/configs/yolov7/yolov7_d-p6_syncbn_fast_8x16b-300e_coco.py b/configs/yolov7/yolov7_d-p6_syncbn_fast_8x16b-300e_coco.py deleted file mode 100644 index 12585f94..00000000 --- a/configs/yolov7/yolov7_d-p6_syncbn_fast_8x16b-300e_coco.py +++ /dev/null @@ -1,19 +0,0 @@ -_base_ = './yolov7_w-p6_syncbn_fast_8x16b-300e_coco.py' - -model = dict( - backbone=dict(arch='D'), - neck=dict( - use_maxpool_in_downsample=True, - use_in_channels_in_downsample=True, - block_cfg=dict(type='ELANBlock', middle_ratio=0.4, block_ratio=0.2, num_blocks=6, num_convs_in_block=1), - in_channels=[384, 768, 1152, 1536], - out_channels=[192, 384, 576, 768], - ), - bbox_head=dict( - head_module=dict( - in_channels=[192, 384, 576, 768], - main_out_channels=[384, 768, 1152, 1536], - aux_out_channels=[384, 768, 1152, 1536], - ) - ), -) diff --git a/configs/yolov7/yolov7_e-p6_syncbn_fast_8x16b-300e_coco.py b/configs/yolov7/yolov7_e-p6_syncbn_fast_8x16b-300e_coco.py deleted file mode 100644 index efbcf41d..00000000 --- a/configs/yolov7/yolov7_e-p6_syncbn_fast_8x16b-300e_coco.py +++ /dev/null @@ -1,13 +0,0 @@ -_base_ = './yolov7_w-p6_syncbn_fast_8x16b-300e_coco.py' - -model = dict( - backbone=dict(arch='E'), - neck=dict( - use_maxpool_in_downsample=True, - use_in_channels_in_downsample=True, - block_cfg=dict(type='ELANBlock', middle_ratio=0.4, block_ratio=0.2, num_blocks=6, num_convs_in_block=1), - in_channels=[320, 640, 960, 1280], - out_channels=[160, 320, 480, 640], - ), - bbox_head=dict(head_module=dict(in_channels=[160, 320, 480, 640], main_out_channels=[320, 640, 960, 1280])), -) diff --git a/configs/yolov7/yolov7_l_syncbn_fast_8x16b-300e_coco.py b/configs/yolov7/yolov7_l_syncbn_fast_8x16b-300e_coco.py deleted file mode 100644 index b73ea45c..00000000 --- a/configs/yolov7/yolov7_l_syncbn_fast_8x16b-300e_coco.py +++ /dev/null @@ -1,337 +0,0 @@ -_base_ = ['../_base_/default_runtime_det.py'] -default_scope = 'mmyolo' - -# ========================Suggested optional parameters======================== -# DATA -# Types of datasets ,The type of the dataset, you can follow sscma/datasets/ to see the types we have defined, -# or you can use the types used by other mmlab libraries, -# but you need to prefix them with the appropriate prefixes to be on the safe side -dataset_type = 'sscma.CustomYOLOv5CocoDataset' -# Path to the dataset's root directory -# dataset link: https://universe.roboflow.com/team-roboflow/coco-128 -data_root = 'https://universe.roboflow.com/ds/z5UOcgxZzD?key=bwx9LQUT0t' -# Path to the annotation file for the training set, both absolute and relative paths are acceptable, -# if it is a relative path, it must be relative to "data_root". -train_ann = 'train/_annotations.coco.json' -# Path to the training set data file, both absolute and relative, if relative, it must be relative to "data_root". -train_data = 'train/' -# Path to the validation set annotation file, both absolute and relative paths are acceptable, -# if it is a relative path, it must be a relative path to data_root. -val_ann = 'valid/_annotations.coco.json' -# Path to the validation set data file, both absolute and relative paths are allowed, -# if it is a relative path, it must be a relative path to data_root. -val_data = 'valid/' -# Height of the model input data -height = 640 -# Width of the model input data -width = 640 -# The width and height of the model input data -imgsz = (width, height) -# persistent_workers must be False if num_workers is 0 -persistent_workers = True - -# MODEL -# Number of categories in the dataset -num_classes = 71 - -# TRAIN -# Learning rate of the model -lr = 0.01 -# Total number of rounds of model training -epochs = 300 -# Number of input data per iteration in the model training phase -batch = 64 -# Number of threads used to load data during training, this value should be adjusted accordingly to the training batch -workers = 8 -# Model weight saving interval in epochs -save_interval = 5 -# Last epoch number to switch training pipeline -num_last_epochs = 15 -# Learning rate scaling factor -lr_factor = 0.01 -# Optimizer weight decay value -weight_decay = 0.0005 -momentum = 0.937 - -# VAL -# Number of input data per iteration in the model validation phase -val_batch = batch -# Number of threads used to load data during validation, this value should be adjusted accordingly to the validation batch -val_workers = workers -# Model validation interval in epoch -val_interval = 5 -# The maximum checkpoints to keep. -max_keep_ckpts = 3 -# ================================END================================= -# -----model related----- -# Basic size of multi-scale prior box -anchors = [ - [(12, 16), (19, 36), (40, 28)], # P3/8 - [(36, 75), (76, 55), (72, 146)], # P4/16 - [(142, 110), (192, 243), (459, 401)], # P5/32 -] - - -num_epoch_stage2 = 30 # The last 30 epochs switch evaluation interval -val_interval_stage2 = 1 # Evaluation interval -# ========================Possible modified parameters======================== - - -model_test_cfg = dict( - # The config of multi-label for multi-class prediction. - multi_label=True, - # The number of boxes before NMS. - nms_pre=30000, - score_thr=0.001, # Threshold to filter out boxes. - nms=dict(type='nms', iou_threshold=0.65), # NMS type and threshold - max_per_img=300, -) # Max number of detections of each image - - -# Config of batch shapes. Only on val. -# It means not used if batch_shapes_cfg is None. -batch_shapes_cfg = dict( - type='BatchShapePolicy', - batch_size=val_batch, - img_size=imgsz[0], - # The image scale of padding should be divided by pad_size_divisor - size_divisor=32, - # Additional paddings for pixel scale - extra_pad_ratio=0.5, -) - -# -----model related----- -strides = [8, 16, 32] # Strides of multi-scale prior box -num_det_layers = 3 # The number of model output scales -norm_cfg = dict(type='BN', momentum=0.03, eps=0.001) - -# Data augmentation -max_translate_ratio = 0.2 # YOLOv5RandomAffine -scaling_ratio_range = (0.1, 2.0) # YOLOv5RandomAffine -mixup_prob = 0.15 # YOLOv5MixUp -randchoice_mosaic_prob = [0.8, 0.2] -mixup_alpha = 8.0 # YOLOv5MixUp -mixup_beta = 8.0 # YOLOv5MixUp - -# -----train val related----- -loss_cls_weight = 0.3 -loss_bbox_weight = 0.05 -loss_obj_weight = 0.7 -# BatchYOLOv7Assigner params -simota_candidate_topk = 10 -simota_iou_weight = 3.0 -simota_cls_weight = 1.0 -prior_match_thr = 4.0 # Priori box matching threshold -obj_level_weights = [4.0, 1.0, 0.4] # The obj loss weights of the three output layers - -# Single-scale training is recommended to -# be turned on, which can speed up training. -env_cfg = dict(cudnn_benchmark=True) - -# ===============================Unmodified in most cases==================== -model = dict( - type='YOLODetector', - data_preprocessor=dict( - type='YOLOv5DetDataPreprocessor', mean=[0.0, 0.0, 0.0], std=[255.0, 255.0, 255.0], bgr_to_rgb=True - ), - backbone=dict(type='YOLOv7Backbone', arch='L', norm_cfg=norm_cfg, act_cfg=dict(type='SiLU', inplace=True)), - neck=dict( - type='YOLOv7PAFPN', - block_cfg=dict(type='ELANBlock', middle_ratio=0.5, block_ratio=0.25, num_blocks=4, num_convs_in_block=1), - upsample_feats_cat_first=False, - in_channels=[512, 1024, 1024], - # The real output channel will be multiplied by 2 - out_channels=[128, 256, 512], - norm_cfg=norm_cfg, - act_cfg=dict(type='SiLU', inplace=True), - ), - bbox_head=dict( - type='YOLOv7Head', - head_module=dict( - type='YOLOv7HeadModule', - num_classes=num_classes, - in_channels=[256, 512, 1024], - featmap_strides=strides, - num_base_priors=3, - ), - prior_generator=dict(type='mmdet.YOLOAnchorGenerator', base_sizes=anchors, strides=strides), - # scaled based on number of detection layers - loss_cls=dict( - type='mmdet.CrossEntropyLoss', - use_sigmoid=True, - reduction='mean', - loss_weight=loss_cls_weight * (num_classes / 80 * 3 / num_det_layers), - ), - loss_bbox=dict( - type='IoULoss', - iou_mode='ciou', - bbox_format='xywh', - reduction='mean', - loss_weight=loss_bbox_weight * (3 / num_det_layers), - return_iou=True, - ), - loss_obj=dict( - type='mmdet.CrossEntropyLoss', - use_sigmoid=True, - reduction='mean', - loss_weight=loss_obj_weight * ((imgsz[0] / 640) ** 2 * 3 / num_det_layers), - ), - prior_match_thr=prior_match_thr, - obj_level_weights=obj_level_weights, - # BatchYOLOv7Assigner params - simota_candidate_topk=simota_candidate_topk, - simota_iou_weight=simota_iou_weight, - simota_cls_weight=simota_cls_weight, - ), - test_cfg=model_test_cfg, -) - -pre_transform = [dict(type='LoadImageFromFile', backend_args=None), dict(type='LoadAnnotations', with_bbox=True)] - -mosiac4_pipeline = [ - dict(type='Mosaic', img_scale=imgsz, pad_val=114.0, pre_transform=pre_transform), - dict( - type='YOLOv5RandomAffine', - max_rotate_degree=0.0, - max_shear_degree=0.0, - max_translate_ratio=max_translate_ratio, # note - scaling_ratio_range=scaling_ratio_range, # note - # imgsz is (width, height) - border=(-imgsz[0] // 2, -imgsz[1] // 2), - border_val=(114, 114, 114), - ), -] - -mosiac9_pipeline = [ - dict(type='Mosaic9', img_scale=imgsz, pad_val=114.0, pre_transform=pre_transform), - dict( - type='YOLOv5RandomAffine', - max_rotate_degree=0.0, - max_shear_degree=0.0, - max_translate_ratio=max_translate_ratio, # note - scaling_ratio_range=scaling_ratio_range, # note - # imgsz is (width, height) - border=(-imgsz[0] // 2, -imgsz[1] // 2), - border_val=(114, 114, 114), - ), -] - -randchoice_mosaic_pipeline = dict( - type='RandomChoice', transforms=[mosiac4_pipeline, mosiac9_pipeline], prob=randchoice_mosaic_prob -) - -train_pipeline = [ - *pre_transform, - randchoice_mosaic_pipeline, - dict( - type='YOLOv5MixUp', - alpha=mixup_alpha, # note - beta=mixup_beta, # note - prob=mixup_prob, - pre_transform=[*pre_transform, randchoice_mosaic_pipeline], - ), - dict(type='YOLOv5HSVRandomAug'), - dict(type='mmdet.RandomFlip', prob=0.5), - dict( - type='mmdet.PackDetInputs', meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape', 'flip', 'flip_direction') - ), -] - -train_dataloader = dict( - batch_size=batch, - num_workers=workers, - persistent_workers=persistent_workers, - pin_memory=True, - sampler=dict(type='DefaultSampler', shuffle=True), - collate_fn=dict(type='yolov5_collate'), # FASTER - dataset=dict( - type=dataset_type, - data_root=data_root, - ann_file=train_ann, - data_prefix=dict(img=train_data), - filter_cfg=dict(filter_empty_gt=False, min_size=32), - pipeline=train_pipeline, - ), -) - -test_pipeline = [ - dict(type='LoadImageFromFile', backend_args=None), - dict(type='YOLOv5KeepRatioResize', scale=imgsz), - dict(type='LetterResize', scale=imgsz, allow_scale_up=False, pad_val=dict(img=114)), - dict(type='LoadAnnotations', with_bbox=True, _scope_='mmdet'), - dict( - type='mmdet.PackDetInputs', - meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape', 'scale_factor', 'pad_param'), - ), -] - -val_dataloader = dict( - batch_size=val_batch, - num_workers=val_workers, - persistent_workers=persistent_workers, - pin_memory=True, - drop_last=False, - sampler=dict(type='DefaultSampler', shuffle=False), - dataset=dict( - type=dataset_type, - data_root=data_root, - test_mode=True, - data_prefix=dict(img=val_data), - ann_file=val_ann, - pipeline=test_pipeline, - batch_shapes_cfg=batch_shapes_cfg, - ), -) - -test_dataloader = val_dataloader - -param_scheduler = None -optim_wrapper = dict( - type='OptimWrapper', - optimizer=dict( - type='SGD', - lr=lr, - momentum=momentum, - weight_decay=weight_decay, - nesterov=True, - batch_size_per_gpu=batch, - ), - constructor='YOLOv7OptimWrapperConstructor', -) - -default_hooks = dict( - param_scheduler=dict( - type='YOLOv5ParamSchedulerHook', scheduler_type='cosine', lr_factor=lr_factor, max_epochs=epochs # note - ), - checkpoint=dict( - type='CheckpointHook', - save_param_scheduler=False, - interval=save_interval, - save_best='auto', - max_keep_ckpts=max_keep_ckpts, - ), -) - -custom_hooks = [ - dict( - type='EMAHook', ema_type='ExpMomentumEMA', momentum=0.0001, update_buffers=True, strict_load=False, priority=49 - ) -] - -val_evaluator = dict( - type='mmdet.CocoMetric', - proposal_nums=(100, 1, 10), # Can be accelerated - ann_file=data_root + val_ann, - metric='bbox', -) -test_evaluator = val_evaluator - -train_cfg = dict( - type='EpochBasedTrainLoop', - max_epochs=epochs, - val_interval=val_interval, - dynamic_intervals=[(epochs - num_epoch_stage2, val_interval_stage2)], - _delete_=True, -) -val_cfg = dict(type='ValLoop') -test_cfg = dict(type='TestLoop') diff --git a/configs/yolov7/yolov7_tiny_syncbn_fast_8x16b-300e_coco.py b/configs/yolov7/yolov7_tiny_syncbn_fast_8x16b-300e_coco.py deleted file mode 100644 index 355e65cd..00000000 --- a/configs/yolov7/yolov7_tiny_syncbn_fast_8x16b-300e_coco.py +++ /dev/null @@ -1,97 +0,0 @@ -_base_ = './base.py' - -# ========================Suggested optional parameters======================== -# DATA -height = 640 -width = 640 -imgsz = (width, height) # width, height -num_classes = 71 - -# TRAIN -lr_factor = 0.01 # Learning rate scaling factor -# ================================END================================= -# -----model related----- -# Data augmentation -max_translate_ratio = 0.1 # YOLOv5RandomAffine -scaling_ratio_range = (0.5, 1.6) # YOLOv5RandomAffine -mixup_prob = 0.05 # YOLOv5MixUp -randchoice_mosaic_prob = [0.8, 0.2] -mixup_alpha = 8.0 # YOLOv5MixUp -mixup_beta = 8.0 # YOLOv5MixUp - -# -----train val related----- -loss_cls_weight = 0.5 -loss_obj_weight = 1.0 -num_det_layers = 3 - -# ===============================Unmodified in most cases==================== - -model = dict( - backbone=dict(arch='Tiny', act_cfg=dict(type='LeakyReLU', negative_slope=0.1)), - neck=dict( - is_tiny_version=True, - in_channels=[128, 256, 512], - out_channels=[64, 128, 256], - block_cfg=dict(_delete_=True, type='TinyDownSampleBlock', middle_ratio=0.25), - act_cfg=dict(type='LeakyReLU', negative_slope=0.1), - use_repconv_outs=False, - ), - bbox_head=dict( - head_module=dict(in_channels=[128, 256, 512]), - loss_cls=dict(loss_weight=loss_cls_weight * (num_classes / 80 * 3 / num_det_layers)), - loss_obj=dict(loss_weight=loss_obj_weight * ((imgsz[0] / 640) ** 2 * 3 / num_det_layers)), - ), -) - -pre_transform = [dict(type='LoadImageFromFile', backend_args=None), dict(type='LoadAnnotations', with_bbox=True)] - -mosiac4_pipeline = [ - dict(type='Mosaic', img_scale=imgsz, pad_val=114.0, pre_transform=pre_transform), - dict( - type='YOLOv5RandomAffine', - max_rotate_degree=0.0, - max_shear_degree=0.0, - max_translate_ratio=max_translate_ratio, # change - scaling_ratio_range=scaling_ratio_range, # change - # imgsz is (width, height) - border=(-imgsz[0] // 2, -imgsz[1] // 2), - border_val=(114, 114, 114), - ), -] - -mosiac9_pipeline = [ - dict(type='Mosaic9', img_scale=imgsz, pad_val=114.0, pre_transform=pre_transform), - dict( - type='YOLOv5RandomAffine', - max_rotate_degree=0.0, - max_shear_degree=0.0, - max_translate_ratio=max_translate_ratio, # change - scaling_ratio_range=scaling_ratio_range, # change - border=(-imgsz[0] // 2, -imgsz[1] // 2), - border_val=(114, 114, 114), - ), -] - -randchoice_mosaic_pipeline = dict( - type='RandomChoice', transforms=[mosiac4_pipeline, mosiac9_pipeline], prob=randchoice_mosaic_prob -) - -train_pipeline = [ - *pre_transform, - randchoice_mosaic_pipeline, - dict( - type='YOLOv5MixUp', - alpha=mixup_alpha, - beta=mixup_beta, - prob=mixup_prob, # change - pre_transform=[*pre_transform, randchoice_mosaic_pipeline], - ), - dict(type='YOLOv5HSVRandomAug'), - dict(type='mmdet.RandomFlip', prob=0.5), - dict( - type='mmdet.PackDetInputs', meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape', 'flip', 'flip_direction') - ), -] - -train_dataloader = dict(dataset=dict(pipeline=train_pipeline)) -default_hooks = dict(param_scheduler=dict(lr_factor=lr_factor)) diff --git a/configs/yolov7/yolov7_w-p6_syncbn_fast_8x16b-300e_coco.py b/configs/yolov7/yolov7_w-p6_syncbn_fast_8x16b-300e_coco.py deleted file mode 100644 index e93fe802..00000000 --- a/configs/yolov7/yolov7_w-p6_syncbn_fast_8x16b-300e_coco.py +++ /dev/null @@ -1,182 +0,0 @@ -_base_ = './base.py' - -# ========================Suggested optional parameters======================== -# DATA -height = 1280 -width = 1280 -imgsz = (width, height) # height, width -num_classes = 71 # Number of classes for classification - -# TRAIN -# The only difference between P6 and P5 in terms of -# hyperparameters is lr_factor -lr_factor = 0.2 -# ================================END================================= - -# Config of batch shapes. Only on val -# It means not used if batch_shapes_cfg is None. -batch_shapes_cfg = dict( - img_size=imgsz[0], size_divisor=64 # The image scale of padding should be divided by pad_size_divisor -) # Additional paddings for pixel scale -tta_img_scales = [(1280, 1280), (1024, 1024), (1536, 1536)] - -# -----model related----- -# Basic size of multi-scale prior box -anchors = [ - [(19, 27), (44, 40), (38, 94)], # P3/8 - [(96, 68), (86, 152), (180, 137)], # P4/16 - [(140, 301), (303, 264), (238, 542)], # P5/32 - [(436, 615), (739, 380), (925, 792)], # P6/64 -] -strides = [8, 16, 32, 64] # Strides of multi-scale prior box -num_det_layers = 4 # # The number of model output scales -norm_cfg = dict(type='BN', momentum=0.03, eps=0.001) - -# Data augmentation -max_translate_ratio = 0.2 # YOLOv5RandomAffine -scaling_ratio_range = (0.1, 2.0) # YOLOv5RandomAffine -mixup_prob = 0.15 # YOLOv5MixUp -randchoice_mosaic_prob = [0.8, 0.2] -mixup_alpha = 8.0 # YOLOv5MixUp -mixup_beta = 8.0 # YOLOv5MixUp - -# -----train val related----- -loss_cls_weight = 0.3 -loss_bbox_weight = 0.05 -loss_obj_weight = 0.7 -obj_level_weights = [4.0, 1.0, 0.25, 0.06] -simota_candidate_topk = 20 - -# ===============================Unmodified in most cases==================== -pre_transform = [dict(type='LoadImageFromFile', backend_args=None), dict(type='LoadAnnotations', with_bbox=True)] - - -model = dict( - backbone=dict(arch='W', out_indices=(2, 3, 4, 5)), - neck=dict( - in_channels=[256, 512, 768, 1024], - out_channels=[128, 256, 384, 512], - use_maxpool_in_downsample=False, - use_repconv_outs=False, - ), - bbox_head=dict( - head_module=dict( - type='YOLOv7p6HeadModule', - in_channels=[128, 256, 384, 512], - featmap_strides=strides, - norm_cfg=norm_cfg, - act_cfg=dict(type='SiLU', inplace=True), - ), - prior_generator=dict(base_sizes=anchors, strides=strides), - simota_candidate_topk=simota_candidate_topk, # note - # scaled based on number of detection layers - loss_cls=dict(loss_weight=loss_cls_weight * (num_classes / 80 * 3 / num_det_layers)), - loss_bbox=dict(loss_weight=loss_bbox_weight * (3 / num_det_layers)), - loss_obj=dict(loss_weight=loss_obj_weight * ((imgsz[0] / 640) ** 2 * 3 / num_det_layers)), - obj_level_weights=obj_level_weights, - ), -) - -mosiac4_pipeline = [ - dict(type='Mosaic', img_scale=imgsz, pad_val=114.0, pre_transform=pre_transform), - dict( - type='YOLOv5RandomAffine', - max_rotate_degree=0.0, - max_shear_degree=0.0, - max_translate_ratio=max_translate_ratio, # note - scaling_ratio_range=scaling_ratio_range, # note - # imgsz is (width, height) - border=(-imgsz[0] // 2, -imgsz[1] // 2), - border_val=(114, 114, 114), - ), -] - -mosiac9_pipeline = [ - dict(type='Mosaic9', img_scale=imgsz, pad_val=114.0, pre_transform=pre_transform), - dict( - type='YOLOv5RandomAffine', - max_rotate_degree=0.0, - max_shear_degree=0.0, - max_translate_ratio=max_translate_ratio, # note - scaling_ratio_range=scaling_ratio_range, # note - # imgsz is (width, height) - border=(-imgsz[0] // 2, -imgsz[1] // 2), - border_val=(114, 114, 114), - ), -] - -randchoice_mosaic_pipeline = dict( - type='RandomChoice', transforms=[mosiac4_pipeline, mosiac9_pipeline], prob=randchoice_mosaic_prob -) - -train_pipeline = [ - *pre_transform, - randchoice_mosaic_pipeline, - dict( - type='YOLOv5MixUp', - alpha=mixup_alpha, # note - beta=mixup_beta, # note - prob=mixup_prob, - pre_transform=[*pre_transform, randchoice_mosaic_pipeline], - ), - dict(type='YOLOv5HSVRandomAug'), - dict(type='mmdet.RandomFlip', prob=0.5), - dict( - type='mmdet.PackDetInputs', meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape', 'flip', 'flip_direction') - ), -] -train_dataloader = dict(dataset=dict(pipeline=train_pipeline)) - -test_pipeline = [ - dict(type='LoadImageFromFile', backend_args=None), - dict(type='YOLOv5KeepRatioResize', scale=imgsz), - dict(type='LetterResize', scale=imgsz, allow_scale_up=False, pad_val=dict(img=114)), - dict(type='LoadAnnotations', with_bbox=True, _scope_='mmdet'), - dict( - type='mmdet.PackDetInputs', - meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape', 'scale_factor', 'pad_param'), - ), -] -val_dataloader = dict(dataset=dict(pipeline=test_pipeline, batch_shapes_cfg=batch_shapes_cfg)) -test_dataloader = val_dataloader - -default_hooks = dict(param_scheduler=dict(lr_factor=lr_factor)) - -# Config for Test Time Augmentation. (TTA) -_multiscale_resize_transforms = [ - dict( - type='Compose', - transforms=[ - dict(type='YOLOv5KeepRatioResize', scale=s), - dict(type='LetterResize', scale=s, allow_scale_up=False, pad_val=dict(img=114)), - ], - ) - for s in tta_img_scales -] - -tta_pipeline = [ - dict(type='LoadImageFromFile', backend_args=None), - dict( - type='TestTimeAug', - transforms=[ - _multiscale_resize_transforms, - [dict(type='mmdet.RandomFlip', prob=1.0), dict(type='mmdet.RandomFlip', prob=0.0)], - [dict(type='mmdet.LoadAnnotations', with_bbox=True)], - [ - dict( - type='mmdet.PackDetInputs', - meta_keys=( - 'img_id', - 'img_path', - 'ori_shape', - 'img_shape', - 'scale_factor', - 'pad_param', - 'flip', - 'flip_direction', - ), - ) - ], - ], - ), -] diff --git a/configs/yolov7/yolov7_x_syncbn_fast_8x16b-300e_coco.py b/configs/yolov7/yolov7_x_syncbn_fast_8x16b-300e_coco.py deleted file mode 100644 index ffe368aa..00000000 --- a/configs/yolov7/yolov7_x_syncbn_fast_8x16b-300e_coco.py +++ /dev/null @@ -1,12 +0,0 @@ -_base_ = './base.py' - -model = dict( - backbone=dict(arch='X'), - neck=dict( - in_channels=[640, 1280, 1280], - out_channels=[160, 320, 640], - block_cfg=dict(type='ELANBlock', middle_ratio=0.4, block_ratio=0.4, num_blocks=3, num_convs_in_block=2), - use_repconv_outs=False, - ), - bbox_head=dict(head_module=dict(in_channels=[320, 640, 1280])), -) diff --git a/configs/yolox/base_arch.py b/configs/yolox/base_arch.py deleted file mode 100644 index b7949d7a..00000000 --- a/configs/yolox/base_arch.py +++ /dev/null @@ -1,351 +0,0 @@ -_base_ = [ - '../_base_/default_runtime_det.py', -] - -default_scope = 'mmyolo' -# ========================Suggested optional parameters======================== -# MODEL -# The scaling factor that controls the depth of the network structure -deepen_factor = 0.33 -# The scaling factor that controls the width of the network structure -widen_factor = 0.5 -# Number of classes for classification -num_classes = 71 - -# DATA -# Dataset type, this will be used to define the dataset -dataset_type = 'sscma.CustomYOLOv5CocoDataset' -# Root path of data -# dataset link: https://universe.roboflow.com/team-roboflow/coco-128 -data_root = 'https://universe.roboflow.com/ds/z5UOcgxZzD?key=bwx9LQUT0t' -# Path of train annotation file -train_ann = 'train/_annotations.coco.json' -# Prefix of train image path -train_data = 'train/' -# Path of val annotation file -val_ann = 'valid/_annotations.coco.json' -# Prefix of val image path -val_data = 'valid/' - -height = 640 -width = 640 -imgsz = (width, height) # width, height - -# TRAIN -# Base learning rate for optim_wrapper. Corresponding to 8xb16=128 bs -lr = 0.001 -# Maximum training epochs -epochs = 300 -# batch_size -batch = 32 -# workers -workers = 4 -# Batch size of a single GPU during validation -val_batch = 1 -# Worker to pre-fetch data for each single GPU during validation -val_workers = 1 -persistent_workers = True -# Learning rate scaling factor -lr_factor = 0.01 - -weight_decay = 0.0005 -momentum = 0.937 - -val_interval=5 -# Save model checkpoint and validation intervals -save_interval = val_interval -# The maximum checkpoints to keep. -max_keep_ckpts = 3 - - -# ================================END================================= - - -# -----model related----- -# Basic size of multi-scale prior box -anchors = [ - [(10, 13), (16, 30), (33, 23)], # P3/8 - [(30, 61), (62, 45), (59, 119)], # P4/16 - [(116, 90), (156, 198), (373, 326)], # P5/32 -] - -# -----train val related----- -model_test_cfg = dict( - # The config of multi-label for multi-class prediction. - multi_label=True, - # The number of boxes before NMS - nms_pre=30000, - score_thr=0.001, # Threshold to filter out boxes. - nms=dict(type='nms', iou_threshold=0.65), # NMS type and threshold - max_per_img=300, -) # Max number of detections of each image - - -# Config of batch shapes. Only on val. -# It means not used if batch_shapes_cfg is None. -batch_shapes_cfg = dict( - type='BatchShapePolicy', - batch_size=val_batch, - img_size=imgsz[0], - # The image scale of padding should be divided by pad_size_divisor - size_divisor=32, - # Additional paddings for pixel scale - extra_pad_ratio=0.5, -) - -# -----model related----- -# Strides of multi-scale prior box -strides = [8, 16, 32] -num_det_layers = 3 # The number of model output scales -norm_cfg = dict(type='BN', momentum=0.03, eps=0.001) # Normalization config - -# -----train val related----- -affine_scale = 0.5 # YOLOv5RandomAffine scaling ratio -loss_cls_weight = 1.0 -loss_bbox_weight = 5.0 -loss_obj_weight = 1.0 -loss_bbox_aux_weight = 1.0 -center_radius = 2.5 # SimOTAAssigner -prior_match_thr = 4.0 # Priori box matching threshold -# The obj loss weights of the three output layers -obj_level_weights = [4.0, 1.0, 0.4] - -# Single-scale training is recommended to -# be turned on, which can speed up training. -env_cfg = dict(cudnn_benchmark=True) - -# model arch -model = dict( - type='mmyolo.YOLODetector', - init_cfg=dict( - type='Kaiming', - layer='Conv2d', - a=2.23606797749979, # math.sqrt(5) - distribution='uniform', - mode='fan_in', - nonlinearity='leaky_relu', - ), - data_preprocessor=dict( - type='mmdet.DetDataPreprocessor', - mean=[0.0, 0.0, 0.0], - std=[255.0, 255.0, 255.0], - bgr_to_rgb=True, - ), - backbone=dict( - type='YOLOXCSPDarknet', - deepen_factor=deepen_factor, - widen_factor=widen_factor, - out_indices=(2, 3, 4), - spp_kernal_sizes=(5, 9, 13), - norm_cfg=norm_cfg, - act_cfg=dict(type='ReLU', inplace=True), - ), - neck=dict( - type='YOLOXPAFPN', - deepen_factor=deepen_factor, - widen_factor=widen_factor, - in_channels=[256, 512, 1024], - out_channels=256, - norm_cfg=norm_cfg, - act_cfg=dict(type='ReLU', inplace=True), - ), - bbox_head=dict( - type='YOLOXHead', - head_module=dict( - type='YOLOXHeadModule', - num_classes=num_classes, - in_channels=256, - feat_channels=256, - widen_factor=widen_factor, - stacked_convs=2, - featmap_strides=(8, 16, 32), - use_depthwise=False, - norm_cfg=norm_cfg, - act_cfg=dict(type='ReLU', inplace=True), - ), - loss_cls=dict( - type='mmdet.CrossEntropyLoss', - use_sigmoid=True, - reduction='sum', - loss_weight=loss_cls_weight, - ), - loss_bbox=dict( - type='mmdet.IoULoss', - mode='square', - eps=1e-16, - reduction='sum', - loss_weight=loss_bbox_weight, - ), - loss_obj=dict( - type='mmdet.CrossEntropyLoss', - use_sigmoid=True, - reduction='sum', - loss_weight=loss_obj_weight, - ), - loss_bbox_aux=dict(type='mmdet.L1Loss', reduction='sum', loss_weight=loss_bbox_aux_weight), - ), - train_cfg=dict( - assigner=dict( - type='mmdet.SimOTAAssigner', - center_radius=center_radius, - iou_calculator=dict(type='mmdet.BboxOverlaps2D'), - ) - ), - test_cfg=model_test_cfg, -) - -albu_train_transforms = [ - dict(type='Blur', p=0.01), - dict(type='MedianBlur', p=0.01), - dict(type='ToGray', p=0.01), - dict(type='CLAHE', p=0.01), -] - -pre_transform = [ - dict(type='LoadImageFromFile', file_client_args=dict(backend='disk')), - dict(type='LoadAnnotations', with_bbox=True), -] - -train_pipeline = [ - *pre_transform, - dict(type='Mosaic', img_scale=imgsz, pad_val=114.0, pre_transform=pre_transform), - dict( - type='YOLOv5RandomAffine', - max_rotate_degree=0.0, - max_shear_degree=0.0, - scaling_ratio_range=(1 - affine_scale, 1 + affine_scale), - # imgsz is (width, height) - border=(-imgsz[0] // 2, -imgsz[1] // 2), - border_val=(114, 114, 114), - ), - dict( - type='mmdet.Albu', - transforms=albu_train_transforms, - bbox_params=dict( - type='BboxParams', - format='pascal_voc', - label_fields=['gt_bboxes_labels', 'gt_ignore_flags'], - ), - keymap={'img': 'image', 'gt_bboxes': 'bboxes'}, - ), - dict(type='YOLOv5HSVRandomAug'), - # dict(type='mmdet.RandomFlip', prob=0.5), - dict( - type='mmdet.PackDetInputs', - meta_keys=( - 'img_id', - 'img_path', - 'ori_shape', - 'img_shape', - ), - ), -] - -train_dataloader = dict( - batch_size=batch, - num_workers=workers, - persistent_workers=persistent_workers, - pin_memory=True, - sampler=dict(type='DefaultSampler', shuffle=True), - dataset=dict( - type=dataset_type, - data_root=data_root, - ann_file=train_ann, - data_prefix=dict(img=train_data), - filter_cfg=dict(filter_empty_gt=False, min_size=32), - pipeline=train_pipeline, - ), -) - -test_pipeline = [ - dict(type='LoadImageFromFile', file_client_args=dict(backend='disk')), - dict(type='YOLOv5KeepRatioResize', scale=imgsz), - dict( - type='LetterResize', - scale=imgsz, - allow_scale_up=False, - pad_val=dict(img=114), - ), - dict(type='LoadAnnotations', with_bbox=True, _scope_='mmdet'), - dict( - type='mmdet.PackDetInputs', - meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape', 'scale_factor'), - ), -] - -val_dataloader = dict( - batch_size=val_batch, - num_workers=val_workers, - persistent_workers=persistent_workers, - pin_memory=True, - drop_last=False, - sampler=dict(type='DefaultSampler', shuffle=False), - dataset=dict( - type=dataset_type, - data_root=data_root, - test_mode=True, - data_prefix=dict(img=val_data), - ann_file=val_ann, - pipeline=test_pipeline, - batch_shapes_cfg=batch_shapes_cfg, - ), -) - -test_dataloader = val_dataloader - -param_scheduler = None -optim_wrapper = dict( - type='OptimWrapper', - optimizer=dict( - type='SGD', - lr=lr, - momentum=momentum, - weight_decay=weight_decay, - nesterov=True, - batch_size_per_gpu=batch, - ), - constructor='YOLOv5OptimizerConstructor', -) - -default_hooks = dict( - param_scheduler=dict( - type='YOLOv5ParamSchedulerHook', - scheduler_type='linear', - lr_factor=lr_factor, - max_epochs=epochs, - ), - checkpoint=dict( - type='CheckpointHook', - interval=save_interval, - save_best='auto', - max_keep_ckpts=max_keep_ckpts, - ), -) - -custom_hooks = [ - dict( - type='EMAHook', - ema_type='ExpMomentumEMA', - momentum=0.0001, - update_buffers=True, - strict_load=False, - priority=49, - ) -] - -val_evaluator = dict( - type='mmdet.CocoMetric', - proposal_nums=(100, 1, 10), - ann_file=data_root + val_ann, - metric='bbox', -) -test_evaluator = val_evaluator - -train_cfg = dict( - type='EpochBasedTrainLoop', - max_epochs=epochs, - val_interval=val_interval, - _delete_=True, -) -val_cfg = dict(type='ValLoop') -test_cfg = dict(type='TestLoop') diff --git a/configs/yolox/yolox_tiny_1xb16_300e_coco.py b/configs/yolox/yolox_tiny_1xb16_300e_coco.py deleted file mode 100644 index e86a85ee..00000000 --- a/configs/yolox/yolox_tiny_1xb16_300e_coco.py +++ /dev/null @@ -1,349 +0,0 @@ -_base_ = [ - '../_base_/default_runtime_det.py', -] - -default_scope = 'mmyolo' -# ========================Suggested optional parameters======================== -# MODEL -# The scaling factor that controls the depth of the network structure -deepen_factor = 0.33 -# The scaling factor that controls the width of the network structure -widen_factor = 0.125 -# Number of classes for classification -num_classes = 71 - -# DATA -# Dataset type, this will be used to define the dataset -dataset_type = 'sscma.CustomYOLOv5CocoDataset' -# Root path of data -# dataset link: https://universe.roboflow.com/team-roboflow/coco-128 -data_root = 'https://universe.roboflow.com/ds/z5UOcgxZzD?key=bwx9LQUT0t' -# Path of train annotation file -train_ann = 'train/_annotations.coco.json' -# Prefix of train image path -train_data = 'train/' -# Path of val annotation file -val_ann = 'valid/_annotations.coco.json' -# Prefix of val image path -val_data = 'valid/' - -height = 640 -width = 640 -imgsz = (width, height) # width, height - -# TRAIN -# Base learning rate for optim_wrapper. Corresponding to 8xb16=128 bs -lr = 0.001 -# Maximum training epochs -epochs = 300 -# batch_size -batch = 32 -# workers -workers = 4 -# Batch size of a single GPU during validation -val_batch = 1 -# Worker to pre-fetch data for each single GPU during validation -val_workers = 1 -persistent_workers = True -# Learning rate scaling factor -lr_factor = 0.01 - -weight_decay = 0.0005 -momentum = 0.937 -val_interval = 5 -# Save model checkpoint and validation intervals -save_interval = val_interval -# The maximum checkpoints to keep. -max_keep_ckpts = 3 - -# ================================END================================= - - -# -----model related----- -# Basic size of multi-scale prior box -anchors = [ - [(10, 13), (16, 30), (33, 23)], # P3/8 - [(30, 61), (62, 45), (59, 119)], # P4/16 - [(116, 90), (156, 198), (373, 326)], # P5/32 -] - -# -----train val related----- -model_test_cfg = dict( - # The config of multi-label for multi-class prediction. - multi_label=True, - # The number of boxes before NMS - nms_pre=30000, - score_thr=0.001, # Threshold to filter out boxes. - nms=dict(type='nms', iou_threshold=0.65), # NMS type and threshold - max_per_img=300, -) # Max number of detections of each image - - -# Config of batch shapes. Only on val. -# It means not used if batch_shapes_cfg is None. -batch_shapes_cfg = dict( - type='BatchShapePolicy', - batch_size=val_batch, - img_size=imgsz[0], - # The image scale of padding should be divided by pad_size_divisor - size_divisor=32, - # Additional paddings for pixel scale - extra_pad_ratio=0.5, -) - -# -----model related----- -# Strides of multi-scale prior box -strides = [8, 16, 32] -num_det_layers = 3 # The number of model output scales -norm_cfg = dict(type='BN', momentum=0.03, eps=0.001) # Normalization config - -# -----train val related----- -affine_scale = 0.5 # YOLOv5RandomAffine scaling ratio -loss_cls_weight = 1.0 -loss_bbox_weight = 5.0 -loss_obj_weight = 1.0 -loss_bbox_aux_weight = 1.0 -center_radius = 2.5 # SimOTAAssigner -prior_match_thr = 4.0 # Priori box matching threshold -# The obj loss weights of the three output layers -obj_level_weights = [4.0, 1.0, 0.4] - -# Single-scale training is recommended to -# be turned on, which can speed up training. -env_cfg = dict(cudnn_benchmark=True) - -# model arch -model = dict( - type='mmyolo.YOLODetector', - init_cfg=dict( - type='Kaiming', - layer='Conv2d', - a=2.23606797749979, # math.sqrt(5) - distribution='uniform', - mode='fan_in', - nonlinearity='leaky_relu', - ), - data_preprocessor=dict( - type='mmdet.DetDataPreprocessor', - mean=[0.0, 0.0, 0.0], - std=[255.0, 255.0, 255.0], - bgr_to_rgb=True, - ), - backbone=dict( - type='YOLOXCSPDarknet', - deepen_factor=deepen_factor, - widen_factor=widen_factor, - out_indices=(2, 3, 4), - spp_kernal_sizes=(5, 9, 13), - norm_cfg=norm_cfg, - act_cfg=dict(type='ReLU', inplace=True), - ), - neck=dict( - type='YOLOXPAFPN', - deepen_factor=deepen_factor, - widen_factor=widen_factor, - in_channels=[256, 512, 1024], - out_channels=256, - norm_cfg=norm_cfg, - act_cfg=dict(type='ReLU', inplace=True), - ), - bbox_head=dict( - type='YOLOXHead', - head_module=dict( - type='YOLOXHeadModule', - num_classes=num_classes, - in_channels=256, - feat_channels=256, - widen_factor=widen_factor, - stacked_convs=2, - featmap_strides=(8, 16, 32), - use_depthwise=False, - norm_cfg=norm_cfg, - act_cfg=dict(type='ReLU', inplace=True), - ), - loss_cls=dict( - type='mmdet.CrossEntropyLoss', - use_sigmoid=True, - reduction='sum', - loss_weight=loss_cls_weight, - ), - loss_bbox=dict( - type='mmdet.IoULoss', - mode='square', - eps=1e-16, - reduction='sum', - loss_weight=loss_bbox_weight, - ), - loss_obj=dict( - type='mmdet.CrossEntropyLoss', - use_sigmoid=True, - reduction='sum', - loss_weight=loss_obj_weight, - ), - loss_bbox_aux=dict(type='mmdet.L1Loss', reduction='sum', loss_weight=loss_bbox_aux_weight), - ), - train_cfg=dict( - assigner=dict( - type='mmdet.SimOTAAssigner', - center_radius=center_radius, - iou_calculator=dict(type='mmdet.BboxOverlaps2D'), - ) - ), - test_cfg=model_test_cfg, -) - -albu_train_transforms = [ - dict(type='Blur', p=0.01), - dict(type='MedianBlur', p=0.01), - dict(type='ToGray', p=0.01), - dict(type='CLAHE', p=0.01), -] - -pre_transform = [ - dict(type='LoadImageFromFile', file_client_args=dict(backend='disk')), - dict(type='LoadAnnotations', with_bbox=True), -] - -train_pipeline = [ - *pre_transform, - dict(type='Mosaic', img_scale=imgsz, pad_val=114.0, pre_transform=pre_transform), - dict( - type='YOLOv5RandomAffine', - max_rotate_degree=0.0, - max_shear_degree=0.0, - scaling_ratio_range=(1 - affine_scale, 1 + affine_scale), - # imgsz is (width, height) - border=(-imgsz[0] // 2, -imgsz[1] // 2), - border_val=(114, 114, 114), - ), - dict( - type='mmdet.Albu', - transforms=albu_train_transforms, - bbox_params=dict( - type='BboxParams', - format='pascal_voc', - label_fields=['gt_bboxes_labels', 'gt_ignore_flags'], - ), - keymap={'img': 'image', 'gt_bboxes': 'bboxes'}, - ), - dict(type='YOLOv5HSVRandomAug'), - # dict(type='mmdet.RandomFlip', prob=0.5), - dict( - type='mmdet.PackDetInputs', - meta_keys=( - 'img_id', - 'img_path', - 'ori_shape', - 'img_shape', - ), - ), -] - -train_dataloader = dict( - batch_size=batch, - num_workers=workers, - persistent_workers=persistent_workers, - pin_memory=True, - sampler=dict(type='DefaultSampler', shuffle=True), - dataset=dict( - type=dataset_type, - data_root=data_root, - ann_file=train_ann, - data_prefix=dict(img=train_data), - filter_cfg=dict(filter_empty_gt=False, min_size=32), - pipeline=train_pipeline, - ), -) - -test_pipeline = [ - dict(type='LoadImageFromFile', file_client_args=dict(backend='disk')), - dict(type='YOLOv5KeepRatioResize', scale=imgsz), - dict( - type='LetterResize', - scale=imgsz, - allow_scale_up=False, - pad_val=dict(img=114), - ), - dict(type='LoadAnnotations', with_bbox=True, _scope_='mmdet'), - dict( - type='mmdet.PackDetInputs', - meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape', 'scale_factor'), - ), -] - -val_dataloader = dict( - batch_size=val_batch, - num_workers=val_workers, - persistent_workers=persistent_workers, - pin_memory=True, - drop_last=False, - sampler=dict(type='DefaultSampler', shuffle=False), - dataset=dict( - type=dataset_type, - data_root=data_root, - test_mode=True, - data_prefix=dict(img=val_data), - ann_file=val_ann, - pipeline=test_pipeline, - batch_shapes_cfg=batch_shapes_cfg, - ), -) - -test_dataloader = val_dataloader - -param_scheduler = None -optim_wrapper = dict( - type='OptimWrapper', - optimizer=dict( - type='SGD', - lr=lr, - momentum=momentum, - weight_decay=weight_decay, - nesterov=True, - batch_size_per_gpu=batch, - ), - constructor='YOLOv5OptimizerConstructor', -) - -default_hooks = dict( - param_scheduler=dict( - type='YOLOv5ParamSchedulerHook', - scheduler_type='linear', - lr_factor=lr_factor, - max_epochs=epochs, - ), - checkpoint=dict( - type='CheckpointHook', - interval=save_interval, - save_best='auto', - max_keep_ckpts=max_keep_ckpts, - ), -) - -custom_hooks = [ - dict( - type='EMAHook', - ema_type='ExpMomentumEMA', - momentum=0.0001, - update_buffers=True, - strict_load=False, - priority=49, - ) -] - -val_evaluator = dict( - type='mmdet.CocoMetric', - proposal_nums=(100, 1, 10), - ann_file=data_root + val_ann, - metric='bbox', -) -test_evaluator = val_evaluator - -train_cfg = dict( - type='EpochBasedTrainLoop', - max_epochs=epochs, - val_interval=val_interval, - _delete_=True, -) -val_cfg = dict(type='ValLoop') -test_cfg = dict(type='TestLoop') From 069c6fe33ddbbef2530de6c2c03a59aa3c700491 Mon Sep 17 00:00:00 2001 From: mjq2020 Date: Mon, 27 Nov 2023 07:52:07 +0000 Subject: [PATCH 27/33] add: optimized swift yolo --- configs/{yolov5 => swift_yolo}/base_arch.py | 0 .../swift_yolo/swift_yolo_1xb16_300e_coco.py | 179 ++++++++++++++++++ .../swift_yolo_labelmatch_coco.py | 0 .../swift_yolo_mb2_1xb16_300e_coco.py | 0 .../swift_yolo_shuff_1xb16_300e_coco.py | 0 .../swift_yolo_tiny_1xb16_300e_coco.py | 0 6 files changed, 179 insertions(+) rename configs/{yolov5 => swift_yolo}/base_arch.py (100%) create mode 100644 configs/swift_yolo/swift_yolo_1xb16_300e_coco.py rename configs/{yolov5 => swift_yolo}/swift_yolo_labelmatch_coco.py (100%) rename configs/{yolov5 => swift_yolo}/swift_yolo_mb2_1xb16_300e_coco.py (100%) rename configs/{yolov5 => swift_yolo}/swift_yolo_shuff_1xb16_300e_coco.py (100%) rename configs/{yolov5 => swift_yolo}/swift_yolo_tiny_1xb16_300e_coco.py (100%) diff --git a/configs/yolov5/base_arch.py b/configs/swift_yolo/base_arch.py similarity index 100% rename from configs/yolov5/base_arch.py rename to configs/swift_yolo/base_arch.py diff --git a/configs/swift_yolo/swift_yolo_1xb16_300e_coco.py b/configs/swift_yolo/swift_yolo_1xb16_300e_coco.py new file mode 100644 index 00000000..2226a8a8 --- /dev/null +++ b/configs/swift_yolo/swift_yolo_1xb16_300e_coco.py @@ -0,0 +1,179 @@ +_base_ = ['./base_arch.py'] + +# ========================Suggested optional parameters======================== +# MODEL +num_classes = 71 +deepen_factor = 0.33 +widen_factor = 0.15 + +# DATA +dataset_type = 'sscma.CustomYOLOv5CocoDataset' +train_ann = 'train/_annotations.coco.json' +train_data = 'train/' # Prefix of train image path +val_ann = 'valid/_annotations.coco.json' +val_data = 'valid/' # Prefix of val image path + +# dataset link: https://universe.roboflow.com/team-roboflow/coco-128 +data_root = 'https://universe.roboflow.com/ds/z5UOcgxZzD?key=bwx9LQUT0t' +height = 640 +width = 640 +batch = 16 +workers = 2 +val_batch = batch +val_workers = workers +imgsz = (width, height) + +# TRAIN +persistent_workers = True + +# ================================END================================= + +# DATA +affine_scale = 0.5 +# MODEL +strides = [8, 16, 32] + +anchors = [ + [(10, 13), (16, 30), (33, 23)], # P3/8 + [(30, 61), (62, 45), (59, 119)], # P4/16 + [(116, 90), (156, 198), (373, 326)], # P5/32 +] + +model = dict( + type='mmyolo.YOLODetector', + backbone=dict( + type='YOLOv5CSPDarknet', + deepen_factor=deepen_factor, + widen_factor=widen_factor, + ), + neck=dict( + type='YOLOv5PAFPN', + deepen_factor=deepen_factor, + widen_factor=widen_factor, + ), + bbox_head=dict( + head_module=dict( + num_classes=num_classes, + in_channels=[256, 512, 1024], + widen_factor=widen_factor, + ), + ), +) + +# ======================datasets================== + + +batch_shapes_cfg = dict( + type='BatchShapePolicy', + batch_size=1, + img_size=imgsz[0], + # The image scale of padding should be divided by pad_size_divisor + size_divisor=32, + # Additional paddings for pixel scale + extra_pad_ratio=0.5, +) + +albu_train_transforms = [ + dict(type='Blur', p=0.01), + dict(type='MedianBlur', p=0.01), + dict(type='ToGray', p=0.01), + dict(type='CLAHE', p=0.01), +] + +pre_transform = [ + dict(type='LoadImageFromFile', file_client_args=dict(backend='disk')), + dict(type='LoadAnnotations', with_bbox=True), +] + +# from mmyolo.datasets.transforms import YOLOv5RandomAffine + +color_space = [ + [dict(type='mmdet.ColorTransform')], + [dict(type='mmdet.AutoContrast')], + [dict(type='mmdet.Equalize')], + [dict(type='mmdet.Sharpness')], + [dict(type='mmdet.Posterize')], + [dict(type='mmdet.Solarize')], + [dict(type='mmdet.Color')], + [dict(type='mmdet.Contrast')], + [dict(type='mmdet.Brightness')], +] +train_pipeline = [ + dict(type='LoadImageFromFile', file_client_args=dict(backend='disk')), + dict(type='LoadAnnotations', with_bbox=True), + dict(type='Mosaic', img_scale=imgsz, pad_val=114.0, pre_transform=pre_transform), + dict( + type='YOLOv5RandomAffine', + max_rotate_degree=0.0, + max_shear_degree=0.0, + scaling_ratio_range=(1 - affine_scale, 1 + affine_scale), + # imgsz is (width, height) + border=(-imgsz[0] // 2, -imgsz[1] // 2), + border_val=(114, 114, 114), + ), + dict( + type='mmdet.Albu', + transforms=albu_train_transforms, + bbox_params=dict(type='BboxParams', format='pascal_voc', label_fields=['gt_bboxes_labels', 'gt_ignore_flags']), + keymap={'img': 'image', 'gt_bboxes': 'bboxes'}, + ), + dict( + type='mmdet.RandomOrder', + transforms=[ + dict(type='mmdet.RandAugment', aug_space=color_space, aug_num=1), + # dict(type='mmdet.RandAugment', aug_space=geometric, aug_num=1), + ], + ), + dict(type='YOLOv5HSVRandomAug'), + dict(type='mmdet.RandomFlip', prob=0.5), + dict( + type='mmdet.PackDetInputs', meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape', 'flip', 'flip_direction') + ), +] + +train_dataloader = dict( + batch_size=batch, + num_workers=workers, + persistent_workers=persistent_workers, + pin_memory=True, + sampler=dict(type='DefaultSampler', shuffle=True), + dataset=dict( + type=dataset_type, + data_root=data_root, + ann_file=train_ann, + data_prefix=dict(img=train_data), + filter_cfg=dict(filter_empty_gt=False, min_size=32), + pipeline=train_pipeline, + ), +) + +test_pipeline = [ + dict(type='LoadImageFromFile', file_client_args=dict(backend='disk')), + dict(type='YOLOv5KeepRatioResize', scale=imgsz), + dict(type='LetterResize', scale=imgsz, allow_scale_up=False, pad_val=dict(img=114)), + dict(type='LoadAnnotations', with_bbox=True, _scope_='mmdet'), + dict( + type='mmdet.PackDetInputs', + meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape', 'scale_factor', 'pad_param'), + ), +] + +val_dataloader = dict( + batch_size=val_batch, + num_workers=val_workers, + persistent_workers=persistent_workers, + pin_memory=True, + drop_last=False, + sampler=dict(type='DefaultSampler', shuffle=False), + dataset=dict( + type=dataset_type, + data_root=data_root, + test_mode=True, + data_prefix=dict(img=val_data), + ann_file=val_ann, + pipeline=test_pipeline, + batch_shapes_cfg=batch_shapes_cfg, + ), +) + +test_dataloader = val_dataloader diff --git a/configs/yolov5/swift_yolo_labelmatch_coco.py b/configs/swift_yolo/swift_yolo_labelmatch_coco.py similarity index 100% rename from configs/yolov5/swift_yolo_labelmatch_coco.py rename to configs/swift_yolo/swift_yolo_labelmatch_coco.py diff --git a/configs/yolov5/swift_yolo_mb2_1xb16_300e_coco.py b/configs/swift_yolo/swift_yolo_mb2_1xb16_300e_coco.py similarity index 100% rename from configs/yolov5/swift_yolo_mb2_1xb16_300e_coco.py rename to configs/swift_yolo/swift_yolo_mb2_1xb16_300e_coco.py diff --git a/configs/yolov5/swift_yolo_shuff_1xb16_300e_coco.py b/configs/swift_yolo/swift_yolo_shuff_1xb16_300e_coco.py similarity index 100% rename from configs/yolov5/swift_yolo_shuff_1xb16_300e_coco.py rename to configs/swift_yolo/swift_yolo_shuff_1xb16_300e_coco.py diff --git a/configs/yolov5/swift_yolo_tiny_1xb16_300e_coco.py b/configs/swift_yolo/swift_yolo_tiny_1xb16_300e_coco.py similarity index 100% rename from configs/yolov5/swift_yolo_tiny_1xb16_300e_coco.py rename to configs/swift_yolo/swift_yolo_tiny_1xb16_300e_coco.py From c55fda7f30aec3205a34de9813a1bd1d566fd5b6 Mon Sep 17 00:00:00 2001 From: mjq2020 Date: Fri, 22 Dec 2023 09:34:47 +0000 Subject: [PATCH 28/33] add: this project to the pythonpath environment variable --- tools/export.py | 7 +++++-- tools/inference.py | 4 ++++ tools/train.py | 4 ++++ 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/tools/export.py b/tools/export.py index 6facacd4..3097c969 100644 --- a/tools/export.py +++ b/tools/export.py @@ -1,7 +1,10 @@ import argparse import os import tempfile - +import sys +import os.path as osp +current_path = osp.dirname(osp.abspath(__file__)) +sys.path.append(osp.dirname(current_path)) import torch from tqdm import tqdm @@ -446,7 +449,7 @@ def export_vela(args, model): if args.vela is not None: for key, value in args.vela.items(): vela_args.append('--' + key) - vela_args.append(value) + vela_args.append(str(value)) vela_main(vela_args) diff --git a/tools/inference.py b/tools/inference.py index 99879c7d..acfdf036 100644 --- a/tools/inference.py +++ b/tools/inference.py @@ -1,6 +1,10 @@ import argparse import os import tempfile +import sys +import os.path as osp +current_path = osp.dirname(osp.abspath(__file__)) +sys.path.append(osp.dirname(current_path)) import torch diff --git a/tools/train.py b/tools/train.py index 84b1703f..d43ad941 100644 --- a/tools/train.py +++ b/tools/train.py @@ -1,6 +1,10 @@ import argparse import os import tempfile +import sys +import os.path as osp +current_path = osp.dirname(osp.abspath(__file__)) +sys.path.append(osp.dirname(current_path)) import torch From ae8346a2730cdb07115a0ee2a6e8687d7e3ae460 Mon Sep 17 00:00:00 2001 From: mjq2020 Date: Fri, 22 Dec 2023 09:35:42 +0000 Subject: [PATCH 29/33] fix: no file exit bug --- scripts/test_functional.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/test_functional.sh b/scripts/test_functional.sh index afe4de97..d855b428 100755 --- a/scripts/test_functional.sh +++ b/scripts/test_functional.sh @@ -16,7 +16,7 @@ classification_test() # detection case detection_test() { - CONFIG_FILE="configs/yolov5/yolov5_tiny_1xb16_300e_coco.py" + CONFIG_FILE="configs/swift_yolo/swift_yolo_tiny_1xb16_300e_coco.py" DATASETS_URL="https://files.seeedstudio.com/sscma/datasets/COCO128.zip" functional_test_core "$1" "${CONFIG_FILE}" "${DATASETS_URL}" From a6bca6edc8d1b835a33d2bf50f91e2ec65aefc4b Mon Sep 17 00:00:00 2001 From: mjq2020 Date: Fri, 22 Dec 2023 09:36:49 +0000 Subject: [PATCH 30/33] modify: thirty library version limit --- requirements/base.txt | 2 ++ requirements/inference.txt | 1 + requirements/pytorch_cpu.txt | 6 +++--- requirements/pytorch_cuda.txt | 8 ++++---- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/requirements/base.txt b/requirements/base.txt index 45ec5d2b..66c012d5 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -1,5 +1,7 @@ # common albumentations>=1.3.0 +libusb1 + # sensor cbor diff --git a/requirements/inference.txt b/requirements/inference.txt index 11fae067..ef713162 100644 --- a/requirements/inference.txt +++ b/requirements/inference.txt @@ -1,4 +1,5 @@ libusb1>=3.0.0 +pnnx==0.0.4 ncnn>=1.0.20230517 onnx>=1.14.0 onnxmltools>=1.11.2 diff --git a/requirements/pytorch_cpu.txt b/requirements/pytorch_cpu.txt index 27f854db..ed22dab4 100644 --- a/requirements/pytorch_cpu.txt +++ b/requirements/pytorch_cpu.txt @@ -1,5 +1,5 @@ # -i https://download.pytorch.org/whl/cpu -torch>=2.0.0 -torchaudio>=2.0.0 -torchvision>=0.15.0 +torch<=2.0.1 +torchaudio<=2.0.2 +torchvision<=0.15.2 diff --git a/requirements/pytorch_cuda.txt b/requirements/pytorch_cuda.txt index 29298aa7..990e36a2 100644 --- a/requirements/pytorch_cuda.txt +++ b/requirements/pytorch_cuda.txt @@ -1,5 +1,5 @@ --i https://download.pytorch.org/whl/cu117 +-i https://download.pytorch.org/whl/cu118 -torch>=2.0.0 -torchaudio>=2.0.0 -torchvision>=0.15.0 +torch<=2.0.1 +torchaudio<=2.0.2 +torchvision<=0.15.2 From 2c48589e5b75e6e73764d83dcb01355888f9bce7 Mon Sep 17 00:00:00 2001 From: mjq2020 Date: Fri, 22 Dec 2023 09:43:57 +0000 Subject: [PATCH 31/33] fix: mmdet bug --- sscma/datasets/cocodataset.py | 45 ++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/sscma/datasets/cocodataset.py b/sscma/datasets/cocodataset.py index 63cc6850..46b25ca6 100644 --- a/sscma/datasets/cocodataset.py +++ b/sscma/datasets/cocodataset.py @@ -1,8 +1,9 @@ import json import os.path as osp -from typing import Optional, Sequence +from typing import Optional, Sequence, List from mmdet.datasets.coco import CocoDataset +from mmengine.fileio import get_local_path from sscma.registry import DATASETS @@ -133,3 +134,45 @@ def __init__( data_root=data_root, **kwargs, ) + + def load_data_list(self) -> List[dict]: + """Load annotations from an annotation file named as ``self.ann_file`` + + Returns: + List[dict]: A list of annotation. + """ # noqa: E501 + + with get_local_path(self.ann_file, backend_args=self.backend_args) as local_path: + self.coco = self.COCOAPI(local_path) + # The order of returned `cat_ids` will not + # change with the order of the `classes` + self.cat_ids = self.coco.get_cat_ids( + cat_names=self.metainfo['classes'] + if len(self.metainfo['classes']) + else [cat['name'] for cat in self.coco.dataset['categories'] if (cat['supercategory'] != "none")], + sup_names=[ + cat['supercategory'] for cat in self.coco.dataset['categories'] if (cat['supercategory'] != "none") + ], + ) + self.cat2label = {cat_id: i for i, cat_id in enumerate(self.cat_ids)} + self.cat_img_map = self.coco.cat_img_map + + img_ids = self.coco.get_img_ids() + data_list = [] + total_ann_ids = [] + for img_id in img_ids: + raw_img_info = self.coco.load_imgs([img_id])[0] + raw_img_info['img_id'] = img_id + + ann_ids = self.coco.get_ann_ids(img_ids=[img_id]) + raw_ann_info = self.coco.load_anns(ann_ids) + total_ann_ids.extend(ann_ids) + + parsed_data_info = self.parse_data_info({'raw_ann_info': raw_ann_info, 'raw_img_info': raw_img_info}) + data_list.append(parsed_data_info) + if self.ANN_ID_UNIQUE: + assert len(set(total_ann_ids)) == len(total_ann_ids), f"Annotation ids in '{self.ann_file}' are not unique!" + + del self.coco + + return data_list From 90d0f777f59c23af35711cf7148fa5fc1c847e72 Mon Sep 17 00:00:00 2001 From: mjq2020 Date: Fri, 22 Dec 2023 10:41:11 +0000 Subject: [PATCH 32/33] fix: ci error bug --- requirements/mmlab.txt | 2 +- tools/export.py | 5 +++-- tools/inference.py | 4 ++-- tools/train.py | 4 ++-- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/requirements/mmlab.txt b/requirements/mmlab.txt index 451a9e5e..f5e98891 100644 --- a/requirements/mmlab.txt +++ b/requirements/mmlab.txt @@ -1,6 +1,6 @@ # use openmim to install mmcls>=1.0.0.rc6 -mmcv>=2.0.0 +mmcv<=2.1.0 mmdet>=3.0.0, <3.1.0 # mmyolo currently does not support mmdet 3.1.0 mmengine>=0.8.2 mmpose>=1.0.0 diff --git a/tools/export.py b/tools/export.py index 3097c969..96d6ed9e 100644 --- a/tools/export.py +++ b/tools/export.py @@ -3,11 +3,12 @@ import tempfile import sys import os.path as osp -current_path = osp.dirname(osp.abspath(__file__)) -sys.path.append(osp.dirname(current_path)) import torch from tqdm import tqdm +current_path = osp.dirname(osp.abspath(__file__)) +sys.path.append(osp.dirname(current_path)) + # TODO: Move to config file import sscma.datasets # noqa import sscma.engine # noqa diff --git a/tools/inference.py b/tools/inference.py index acfdf036..0972c4d4 100644 --- a/tools/inference.py +++ b/tools/inference.py @@ -3,11 +3,11 @@ import tempfile import sys import os.path as osp +import torch + current_path = osp.dirname(osp.abspath(__file__)) sys.path.append(osp.dirname(current_path)) -import torch - # TODO: Move to config file import sscma.datasets # noqa import sscma.engine # noqa diff --git a/tools/train.py b/tools/train.py index d43ad941..18376861 100644 --- a/tools/train.py +++ b/tools/train.py @@ -3,11 +3,11 @@ import tempfile import sys import os.path as osp +import torch + current_path = osp.dirname(osp.abspath(__file__)) sys.path.append(osp.dirname(current_path)) -import torch - # TODO: Move to config file import sscma.datasets # noqa import sscma.engine # noqa From 8eb4ec0dff2d0fa5ac9b79517ddb95810621d3e4 Mon Sep 17 00:00:00 2001 From: mjq2020 Date: Fri, 22 Dec 2023 10:56:51 +0000 Subject: [PATCH 33/33] add: init import --- sscma/datasets/transforms/__init__.py | 3 ++- sscma/datasets/transforms/wrappers.py | 2 +- tools/export.py | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/sscma/datasets/transforms/__init__.py b/sscma/datasets/transforms/__init__.py index 797403bb..d9d59dc8 100644 --- a/sscma/datasets/transforms/__init__.py +++ b/sscma/datasets/transforms/__init__.py @@ -1,4 +1,5 @@ from .formatting import PackSensorInputs from .loading import LoadSensorFromFile +from .wrappers import MutiBranchPipe -__all__ = ['PackSensorInputs', 'LoadSensorFromFile'] +__all__ = ['PackSensorInputs', 'LoadSensorFromFile', 'MutiBranchPipe'] diff --git a/sscma/datasets/transforms/wrappers.py b/sscma/datasets/transforms/wrappers.py index 92bc17a1..edea1e4f 100644 --- a/sscma/datasets/transforms/wrappers.py +++ b/sscma/datasets/transforms/wrappers.py @@ -17,7 +17,7 @@ def transform(self, results: Dict) -> Optional[Union[Dict, Tuple[List, List]]]: multi_results[branch] = {'inputs': None, 'data_samples': None} for branch, pipeline in self.branch_pipelines.items(): branch_results = pipeline(copy.deepcopy(results)) - if branch == 'unsup_teacher': + if branch == self.piece_key: results['img'] = branch_results['inputs'].permute(1, 2, 0).cpu().numpy() # If one branch pipeline returns None, # it will sample another data from dataset. diff --git a/tools/export.py b/tools/export.py index a077123d..0bf443b5 100644 --- a/tools/export.py +++ b/tools/export.py @@ -15,7 +15,7 @@ import sscma.evaluation # noqa import sscma.models # noqa import sscma.visualization # noqa -from sscma.utils.check import check_lib +from sscma.utils.check import check_lib # noqa def parse_args():