henry000 commited on
Commit
dcceddd
Β·
1 Parent(s): 9ae3eb5

🚚 [Rename] All utils, tools, higher readability

Browse files
docs/HOWTO.md CHANGED
@@ -88,3 +88,61 @@ Custom transformations should be designed to accept an image and its bounding bo
88
  # ... (Other Transform)
89
  CustomTransform: 0.5
90
  ```
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
  # ... (Other Transform)
89
  CustomTransform: 0.5
90
  ```
91
+
92
+
93
+ - **Utils**
94
+ - **bbox_utils**
95
+ - `class` Anchor2Box: transform predicted anchor to bounding box
96
+ - `class` Matcher: given prediction and groudtruth, find the groundtruth for each prediction
97
+ - `func` calculate_iou: calculate iou for given two list of bbox
98
+ - `func` transform_bbox: transform bbox from {xywh, xyxy, xcycwh} to {xywh, xyxy, xcycwh}
99
+ - `func` generate_anchors: given image size, make the anchor point for the given size
100
+ - **dataset_utils**
101
+ - `func` locate_label_paths:
102
+ - `func` create_image_metadata:
103
+ - `func` organize_annotations_by_image:
104
+ - `func` scale_segmentation:
105
+ - **logging_utils**
106
+ - `func` custom_log: custom loguru, overiding the origin logger
107
+ - `class` ProgressTracker: A class to handle output for each batch, epoch
108
+ - `func` log_model_structure: give a torch model, print it as a table
109
+ - `func` validate_log_directory: for given experiemnt, check if the log folder already existed
110
+ - **model_utils**
111
+ - `class` ExponentialMovingAverage: a mirror of model, do ema on model
112
+ - `func` create_optimizer: return a optimzer, for example SDG, ADAM
113
+ - `func` create_scheduler: return a scheduler, for example Step, Lambda
114
+ - **module_utils**
115
+ - `func` get_layer_map:
116
+ - `func` auto_pad: given a convolution block, return how many pixel should conv padding
117
+ - `func` create_activation_function: given a `func` name, return a activation `func`tion
118
+ - `func` round_up: given number and divider, return a number is mutliplcation of divider
119
+ - `func` divide_into_chunks: for a given list and n, seperate list to n sub list
120
+ - **trainer**
121
+ - `class` Trainer: a class can automatic train the model
122
+ - **Tools**
123
+ - **converter_json2txt**
124
+ - `func` discretize_categories: given the dictionary class, turn id from 1: class
125
+ - `func` process_annotations: handle the whole dataset annotations
126
+ - `func` process_annotation: handle a annotation(a list of bounding box)
127
+ - `func` normalize_segmentation: normalize segmentation position to 0~1
128
+ - `func` convert_annotations: convert json annotations to txt file structure
129
+ - **data_augment**
130
+ - `class` AugmentationComposer: Compose a list of data augmentation strategy
131
+ - `class` VerticalFlip: a custom data augmentation, Random Vertical Flip
132
+ - `class` Mosaic: a data augmentation strategy, follow YOLOv5
133
+ - **dataloader**
134
+ - `class` YoloDataset: a custom dataset for training yolo's model
135
+ - `class` YoloDataLoader: a dataloader base on torch's dataloader, with custom allocate function
136
+ - `func` create_dataloader: given a config file, return a YOLO dataloader
137
+ - **drawer**
138
+ - `func` draw_bboxes: given a image and list of bbox, draw bbox on the image
139
+ - `func` draw_model: visualize the given model
140
+ - **get_dataset**
141
+ - `func` download_file: for a given link, downlaod the file
142
+ - `func` unzip_file: unzip the downlaoded zip to data/
143
+ - `func` check_files: check if the dataset file numbers is correct
144
+ - `func` prepare_dataset: automatic downlaod the dataset and check if it is correct
145
+ - **loss**
146
+ - `class` BoxLoss: a Custom Loss for bounding box
147
+ - `class` YOLOLoss: a implementation of yolov9 loss
148
+ - `class` DualLoss: a implementation of yolov9 loss with auxiliary detection head
examples/example_train.py CHANGED
@@ -9,23 +9,23 @@ project_root = Path(__file__).resolve().parent.parent
9
  sys.path.append(str(project_root))
10
 
11
  from yolo.config.config import Config
12
- from yolo.tools.log_helper import custom_logger, get_valid_folder
13
- from yolo.tools.trainer import Trainer
14
- from yolo.utils.dataloader import get_dataloader
15
- from yolo.utils.get_dataset import prepare_dataset
16
 
17
 
18
  @hydra.main(config_path="../yolo/config", config_name="config", version_base=None)
19
  def main(cfg: Config):
20
  custom_logger()
21
- save_path = get_valid_folder(cfg.hyper.general, cfg.name)
22
  if cfg.download.auto:
23
  prepare_dataset(cfg.download)
24
 
25
- dataloader = get_dataloader(cfg)
26
  # TODO: get_device or rank, for DDP mode
27
  device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
28
- trainer = Trainer(cfg, save_path, device)
29
  trainer.train(dataloader, cfg.hyper.train.epoch)
30
 
31
 
 
9
  sys.path.append(str(project_root))
10
 
11
  from yolo.config.config import Config
12
+ from yolo.tools.data_loader import create_dataloader
13
+ from yolo.tools.dataset_preparation import prepare_dataset
14
+ from yolo.tools.trainer import ModelTrainer
15
+ from yolo.utils.logging_utils import custom_logger, validate_log_directory
16
 
17
 
18
  @hydra.main(config_path="../yolo/config", config_name="config", version_base=None)
19
  def main(cfg: Config):
20
  custom_logger()
21
+ save_path = validate_log_directory(cfg.hyper.general, cfg.name)
22
  if cfg.download.auto:
23
  prepare_dataset(cfg.download)
24
 
25
+ dataloader = create_dataloader(cfg)
26
  # TODO: get_device or rank, for DDP mode
27
  device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
28
+ trainer = ModelTrainer(cfg, save_path, device)
29
  trainer.train(dataloader, cfg.hyper.train.epoch)
30
 
31
 
tests/test_utils/test_dataaugment.py CHANGED
@@ -9,7 +9,7 @@ from torchvision.transforms import functional as TF
9
  project_root = Path(__file__).resolve().parent.parent.parent
10
  sys.path.append(str(project_root))
11
 
12
- from yolo.utils.data_augment import Compose, HorizontalFlip, Mosaic, VerticalFlip
13
 
14
 
15
  def test_horizontal_flip():
 
9
  project_root = Path(__file__).resolve().parent.parent.parent
10
  sys.path.append(str(project_root))
11
 
12
+ from yolo.utils.data_augmentation import Compose, HorizontalFlip, Mosaic, VerticalFlip
13
 
14
 
15
  def test_horizontal_flip():
tests/test_utils/test_loss.py CHANGED
@@ -8,7 +8,7 @@ from hydra import compose, initialize
8
  project_root = Path(__file__).resolve().parent.parent.parent
9
  sys.path.append(str(project_root))
10
 
11
- from yolo.utils.loss import YOLOLoss
12
 
13
 
14
  @pytest.fixture
 
8
  project_root = Path(__file__).resolve().parent.parent.parent
9
  sys.path.append(str(project_root))
10
 
11
+ from yolo.utils.loss_functions import YOLOLoss
12
 
13
 
14
  @pytest.fixture
yolo/model/module.py CHANGED
@@ -6,7 +6,7 @@ from loguru import logger
6
  from torch import Tensor, nn
7
  from torch.nn.common_types import _size_2_t
8
 
9
- from yolo.tools.module_helper import auto_pad, get_activation, round_up
10
 
11
 
12
  # ----------- Basic Class ----------- #
@@ -26,7 +26,7 @@ class Conv(nn.Module):
26
  kwargs.setdefault("padding", auto_pad(kernel_size, **kwargs))
27
  self.conv = nn.Conv2d(in_channels, out_channels, kernel_size, bias=False, **kwargs)
28
  self.bn = nn.BatchNorm2d(out_channels, eps=1e-3, momentum=3e-2)
29
- self.act = get_activation(activation)
30
 
31
  def forward(self, x: Tensor) -> Tensor:
32
  return self.act(self.bn(self.conv(x)))
@@ -109,7 +109,7 @@ class RepConv(nn.Module):
109
  **kwargs
110
  ):
111
  super().__init__()
112
- self.act = get_activation(activation)
113
  self.conv1 = Conv(in_channels, out_channels, kernel_size, activation=False, **kwargs)
114
  self.conv2 = Conv(in_channels, out_channels, 1, activation=False, **kwargs)
115
 
 
6
  from torch import Tensor, nn
7
  from torch.nn.common_types import _size_2_t
8
 
9
+ from yolo.utils.module_utils import auto_pad, create_activation_function, round_up
10
 
11
 
12
  # ----------- Basic Class ----------- #
 
26
  kwargs.setdefault("padding", auto_pad(kernel_size, **kwargs))
27
  self.conv = nn.Conv2d(in_channels, out_channels, kernel_size, bias=False, **kwargs)
28
  self.bn = nn.BatchNorm2d(out_channels, eps=1e-3, momentum=3e-2)
29
+ self.act = create_activation_function(activation)
30
 
31
  def forward(self, x: Tensor) -> Tensor:
32
  return self.act(self.bn(self.conv(x)))
 
109
  **kwargs
110
  ):
111
  super().__init__()
112
+ self.act = create_activation_function(activation)
113
  self.conv1 = Conv(in_channels, out_channels, kernel_size, activation=False, **kwargs)
114
  self.conv2 = Conv(in_channels, out_channels, 1, activation=False, **kwargs)
115
 
yolo/model/yolo.py CHANGED
@@ -5,9 +5,9 @@ from loguru import logger
5
  from omegaconf import ListConfig, OmegaConf
6
 
7
  from yolo.config.config import Config, Model, YOLOLayer
8
- from yolo.tools.log_helper import log_model
9
- from yolo.tools.module_helper import get_layer_map
10
- from yolo.utils.drawer import draw_model
11
 
12
 
13
  class YOLO(nn.Module):
@@ -125,6 +125,6 @@ def get_model(cfg: Config) -> YOLO:
125
  OmegaConf.set_struct(cfg.model, False)
126
  model = YOLO(cfg.model, cfg.hyper.data.class_num)
127
  logger.info("βœ… Success load model")
128
- log_model(model.model)
129
  # draw_model(model=model)
130
  return model
 
5
  from omegaconf import ListConfig, OmegaConf
6
 
7
  from yolo.config.config import Config, Model, YOLOLayer
8
+ from yolo.tools.drawer import draw_model
9
+ from yolo.utils.logging_utils import log_model_structure
10
+ from yolo.utils.module_utils import get_layer_map
11
 
12
 
13
  class YOLO(nn.Module):
 
125
  OmegaConf.set_struct(cfg.model, False)
126
  model = YOLO(cfg.model, cfg.hyper.data.class_num)
127
  logger.info("βœ… Success load model")
128
+ log_model_structure(model.model)
129
  # draw_model(model=model)
130
  return model
yolo/{utils/data_augment.py β†’ tools/data_augmentation.py} RENAMED
@@ -4,7 +4,7 @@ from PIL import Image
4
  from torchvision.transforms import functional as TF
5
 
6
 
7
- class Compose:
8
  """Composes several transforms together."""
9
 
10
  def __init__(self, transforms, image_size: int = 640):
 
4
  from torchvision.transforms import functional as TF
5
 
6
 
7
+ class AugmentationComposer:
8
  """Composes several transforms together."""
9
 
10
  def __init__(self, transforms, image_size: int = 640):
yolo/{utils/converter_json2txt.py β†’ tools/data_conversion.py} RENAMED
File without changes
yolo/{utils/dataloader.py β†’ tools/data_loader.py} RENAMED
@@ -13,13 +13,19 @@ from torchvision.transforms import functional as TF
13
  from tqdm.rich import tqdm
14
 
15
  from yolo.config.config import Config
16
- from yolo.tools.dataset_helper import (
17
- create_image_info_dict,
18
- find_labels_path,
19
- get_scaled_segmentation,
 
 
 
 
 
 
 
 
20
  )
21
- from yolo.utils.data_augment import Compose, HorizontalFlip, MixUp, Mosaic, VerticalFlip
22
- from yolo.utils.drawer import draw_bboxes
23
 
24
 
25
  class YoloDataset(Dataset):
@@ -30,7 +36,7 @@ class YoloDataset(Dataset):
30
  self.image_size = image_size
31
 
32
  transforms = [eval(aug)(prob) for aug, prob in augment_cfg.items()]
33
- self.transform = Compose(transforms, self.image_size)
34
  self.transform.get_more_data = self.get_more_data
35
  self.data = self.load_data(dataset_cfg.path, phase_name)
36
 
@@ -68,10 +74,10 @@ class YoloDataset(Dataset):
68
  list: A list of tuples, each containing the path to an image file and its associated segmentation as a tensor.
69
  """
70
  images_path = path.join(dataset_path, "images", phase_name)
71
- labels_path, data_type = find_labels_path(dataset_path, phase_name)
72
  images_list = sorted(os.listdir(images_path))
73
  if data_type == "json":
74
- annotations_index, image_info_dict = create_image_info_dict(labels_path)
75
 
76
  data = []
77
  valid_inputs = 0
@@ -85,7 +91,7 @@ class YoloDataset(Dataset):
85
  if image_info is None:
86
  continue
87
  annotations = annotations_index.get(image_info["id"], [])
88
- image_seg_annotations = get_scaled_segmentation(annotations, image_info)
89
  if not image_seg_annotations:
90
  continue
91
 
@@ -191,13 +197,13 @@ class YoloDataLoader(DataLoader):
191
  return batch_images, batch_targets
192
 
193
 
194
- def get_dataloader(config):
195
  return YoloDataLoader(config)
196
 
197
 
198
  @hydra.main(config_path="../config", config_name="config", version_base=None)
199
  def main(cfg):
200
- dataloader = get_dataloader(cfg)
201
  draw_bboxes(*next(iter(dataloader)))
202
 
203
 
@@ -205,7 +211,7 @@ if __name__ == "__main__":
205
  import sys
206
 
207
  sys.path.append("./")
208
- from tools.log_helper import custom_logger
209
 
210
  custom_logger()
211
  main()
 
13
  from tqdm.rich import tqdm
14
 
15
  from yolo.config.config import Config
16
+ from yolo.tools.data_augmentation import (
17
+ AugmentationComposer,
18
+ HorizontalFlip,
19
+ MixUp,
20
+ Mosaic,
21
+ VerticalFlip,
22
+ )
23
+ from yolo.tools.drawer import draw_bboxes
24
+ from yolo.utils.dataset_utils import (
25
+ create_image_metadata,
26
+ locate_label_paths,
27
+ scale_segmentation,
28
  )
 
 
29
 
30
 
31
  class YoloDataset(Dataset):
 
36
  self.image_size = image_size
37
 
38
  transforms = [eval(aug)(prob) for aug, prob in augment_cfg.items()]
39
+ self.transform = AugmentationComposer(transforms, self.image_size)
40
  self.transform.get_more_data = self.get_more_data
41
  self.data = self.load_data(dataset_cfg.path, phase_name)
42
 
 
74
  list: A list of tuples, each containing the path to an image file and its associated segmentation as a tensor.
75
  """
76
  images_path = path.join(dataset_path, "images", phase_name)
77
+ labels_path, data_type = locate_label_paths(dataset_path, phase_name)
78
  images_list = sorted(os.listdir(images_path))
79
  if data_type == "json":
80
+ annotations_index, image_info_dict = create_image_metadata(labels_path)
81
 
82
  data = []
83
  valid_inputs = 0
 
91
  if image_info is None:
92
  continue
93
  annotations = annotations_index.get(image_info["id"], [])
94
+ image_seg_annotations = scale_segmentation(annotations, image_info)
95
  if not image_seg_annotations:
96
  continue
97
 
 
197
  return batch_images, batch_targets
198
 
199
 
200
+ def create_dataloader(config):
201
  return YoloDataLoader(config)
202
 
203
 
204
  @hydra.main(config_path="../config", config_name="config", version_base=None)
205
  def main(cfg):
206
+ dataloader = create_dataloader(cfg)
207
  draw_bboxes(*next(iter(dataloader)))
208
 
209
 
 
211
  import sys
212
 
213
  sys.path.append("./")
214
+ from tools.logging_utils import custom_logger
215
 
216
  custom_logger()
217
  main()
yolo/{utils/get_dataset.py β†’ tools/dataset_preparation.py} RENAMED
File without changes
yolo/{utils β†’ tools}/drawer.py RENAMED
File without changes
yolo/tools/layer_helper.py DELETED
@@ -1,5 +0,0 @@
1
- import inspect
2
-
3
- import torch.nn as nn
4
-
5
- from yolo.model import module
 
 
 
 
 
 
yolo/{utils/loss.py β†’ tools/loss_functions.py} RENAMED
@@ -8,8 +8,13 @@ from torch import Tensor, nn
8
  from torch.nn import BCEWithLogitsLoss
9
 
10
  from yolo.config.config import Config
11
- from yolo.tools.bbox_helper import Anchor2Box, BoxMatcher, calculate_iou, make_anchor
12
- from yolo.tools.module_helper import make_chunk
 
 
 
 
 
13
 
14
 
15
  class BCELoss(nn.Module):
@@ -78,14 +83,14 @@ class YOLOLoss:
78
  self.reverse_reg = torch.arange(self.reg_max, dtype=torch.float32, device=device)
79
  self.scale_up = torch.tensor(self.image_size * 2, device=device)
80
 
81
- self.anchors, self.scaler = make_anchor(self.image_size, self.strides, device)
82
 
83
  self.cls = BCELoss()
84
  self.dfl = DFLoss(self.anchors, self.scaler, self.reg_max)
85
  self.iou = BoxLoss()
86
 
87
  self.matcher = BoxMatcher(cfg.hyper.train.loss.matcher, self.class_num, self.anchors)
88
- self.box_converter = Anchor2Box(cfg, device)
89
 
90
  def separate_anchor(self, anchors):
91
  """
@@ -132,7 +137,7 @@ class DualLoss:
132
  targets[:, :, 1:] = targets[:, :, 1:] * self.loss.scale_up
133
 
134
  # TODO: Need Refactor this region, make it flexible!
135
- predicts = make_chunk(predicts[0], 2)
136
  aux_iou, aux_dfl, aux_cls = self.loss(predicts[0], targets)
137
  main_iou, main_dfl, main_cls = self.loss(predicts[1], targets)
138
 
 
8
  from torch.nn import BCEWithLogitsLoss
9
 
10
  from yolo.config.config import Config
11
+ from yolo.utils.bounding_box_utils import (
12
+ AnchorBoxConverter,
13
+ BoxMatcher,
14
+ calculate_iou,
15
+ generate_anchors,
16
+ )
17
+ from yolo.utils.module_utils import divide_into_chunks
18
 
19
 
20
  class BCELoss(nn.Module):
 
83
  self.reverse_reg = torch.arange(self.reg_max, dtype=torch.float32, device=device)
84
  self.scale_up = torch.tensor(self.image_size * 2, device=device)
85
 
86
+ self.anchors, self.scaler = generate_anchors(self.image_size, self.strides, device)
87
 
88
  self.cls = BCELoss()
89
  self.dfl = DFLoss(self.anchors, self.scaler, self.reg_max)
90
  self.iou = BoxLoss()
91
 
92
  self.matcher = BoxMatcher(cfg.hyper.train.loss.matcher, self.class_num, self.anchors)
93
+ self.box_converter = AnchorBoxConverter(cfg, device)
94
 
95
  def separate_anchor(self, anchors):
96
  """
 
137
  targets[:, :, 1:] = targets[:, :, 1:] * self.loss.scale_up
138
 
139
  # TODO: Need Refactor this region, make it flexible!
140
+ predicts = divide_into_chunks(predicts[0], 2)
141
  aux_iou, aux_dfl, aux_cls = self.loss(predicts[0], targets)
142
  main_iou, main_dfl, main_cls = self.loss(predicts[1], targets)
143
 
yolo/tools/trainer.py CHANGED
@@ -7,25 +7,29 @@ from torch.cuda.amp import GradScaler, autocast
7
 
8
  from yolo.config.config import Config, TrainConfig
9
  from yolo.model.yolo import get_model
10
- from yolo.tools.log_helper import CustomProgress
11
- from yolo.tools.model_helper import EMA, get_optimizer, get_scheduler
12
- from yolo.utils.loss import get_loss_function
 
 
 
 
13
 
14
 
15
- class Trainer:
16
  def __init__(self, cfg: Config, save_path: str, device):
17
  train_cfg: TrainConfig = cfg.hyper.train
18
  model = get_model(cfg)
19
 
20
  self.model = model.to(device)
21
  self.device = device
22
- self.optimizer = get_optimizer(model, train_cfg.optimizer)
23
- self.scheduler = get_scheduler(self.optimizer, train_cfg.scheduler)
24
  self.loss_fn = get_loss_function(cfg)
25
- self.progress = CustomProgress(cfg, save_path, use_wandb=True)
26
 
27
  if getattr(train_cfg.ema, "enabled", False):
28
- self.ema = EMA(model, decay=train_cfg.ema.decay)
29
  else:
30
  self.ema = None
31
  self.scaler = GradScaler()
 
7
 
8
  from yolo.config.config import Config, TrainConfig
9
  from yolo.model.yolo import get_model
10
+ from yolo.tools.loss_functions import get_loss_function
11
+ from yolo.utils.logging_utils import ProgressTracker
12
+ from yolo.utils.model_utils import (
13
+ ExponentialMovingAverage,
14
+ create_optimizer,
15
+ create_scheduler,
16
+ )
17
 
18
 
19
+ class ModelTrainer:
20
  def __init__(self, cfg: Config, save_path: str, device):
21
  train_cfg: TrainConfig = cfg.hyper.train
22
  model = get_model(cfg)
23
 
24
  self.model = model.to(device)
25
  self.device = device
26
+ self.optimizer = create_optimizer(model, train_cfg.optimizer)
27
+ self.scheduler = create_scheduler(self.optimizer, train_cfg.scheduler)
28
  self.loss_fn = get_loss_function(cfg)
29
+ self.progress = ProgressTracker(cfg, save_path, use_wandb=True)
30
 
31
  if getattr(train_cfg.ema, "enabled", False):
32
+ self.ema = ExponentialMovingAverage(model, decay=train_cfg.ema.decay)
33
  else:
34
  self.ema = None
35
  self.scaler = GradScaler()
yolo/utils/README.md DELETED
@@ -1,7 +0,0 @@
1
- task/train.py
2
-
3
- task/validate.py
4
-
5
- task/inference.py
6
-
7
- etc.
 
 
 
 
 
 
 
 
yolo/{tools β†’ utils}/__init__.py RENAMED
File without changes
yolo/{tools/bbox_helper.py β†’ utils/bounding_box_utils.py} RENAMED
@@ -106,7 +106,7 @@ def transform_bbox(bbox: Tensor, indicator="xywh -> xyxy"):
106
  return bbox.to(dtype=data_type)
107
 
108
 
109
- def make_anchor(image_size: List[int], strides: List[int], device):
110
  W, H = image_size
111
  anchors = []
112
  scaler = []
@@ -124,7 +124,7 @@ def make_anchor(image_size: List[int], strides: List[int], device):
124
  return all_anchors, all_scalers
125
 
126
 
127
- class Anchor2Box:
128
  def __init__(self, cfg: Config, device: torch.device) -> None:
129
  self.reg_max = cfg.model.anchor.reg_max
130
  self.class_num = cfg.hyper.data.class_num
@@ -132,7 +132,7 @@ class Anchor2Box:
132
  self.strides = cfg.model.anchor.strides
133
 
134
  self.scale_up = torch.tensor(self.image_size * 2, device=device)
135
- self.anchors, self.scaler = make_anchor(self.image_size, self.strides, device)
136
  self.reverse_reg = torch.arange(self.reg_max, dtype=torch.float32, device=device)
137
 
138
  def __call__(self, predicts: List[Tensor], with_logits=False) -> Tensor:
 
106
  return bbox.to(dtype=data_type)
107
 
108
 
109
+ def generate_anchors(image_size: List[int], strides: List[int], device):
110
  W, H = image_size
111
  anchors = []
112
  scaler = []
 
124
  return all_anchors, all_scalers
125
 
126
 
127
+ class AnchorBoxConverter:
128
  def __init__(self, cfg: Config, device: torch.device) -> None:
129
  self.reg_max = cfg.model.anchor.reg_max
130
  self.class_num = cfg.hyper.data.class_num
 
132
  self.strides = cfg.model.anchor.strides
133
 
134
  self.scale_up = torch.tensor(self.image_size * 2, device=device)
135
+ self.anchors, self.scaler = generate_anchors(self.image_size, self.strides, device)
136
  self.reverse_reg = torch.arange(self.reg_max, dtype=torch.float32, device=device)
137
 
138
  def __call__(self, predicts: List[Tensor], with_logits=False) -> Tensor:
yolo/{tools/dataset_helper.py β†’ utils/dataset_utils.py} RENAMED
@@ -6,10 +6,10 @@ from typing import Any, Dict, List, Optional, Tuple
6
 
7
  import numpy as np
8
 
9
- from yolo.utils.converter_json2txt import discretize_categories
10
 
11
 
12
- def find_labels_path(dataset_path: str, phase_name: str):
13
  """
14
  Find the path to label files for a specified dataset and phase(e.g. training).
15
 
@@ -35,7 +35,7 @@ def find_labels_path(dataset_path: str, phase_name: str):
35
  raise FileNotFoundError("No labels found in the specified dataset path and phase name.")
36
 
37
 
38
- def create_image_info_dict(labels_path: str) -> Tuple[Dict[str, List], Dict[str, Dict]]:
39
  """
40
  Create a dictionary containing image information and annotations indexed by image ID.
41
 
@@ -49,12 +49,12 @@ def create_image_info_dict(labels_path: str) -> Tuple[Dict[str, List], Dict[str,
49
  with open(labels_path, "r") as file:
50
  labels_data = json.load(file)
51
  id_to_idx = discretize_categories(labels_data.get("categories", [])) if "categories" in labels_data else None
52
- annotations_index = index_annotations_by_image(labels_data, id_to_idx) # check lookup is a good name?
53
  image_info_dict = {path.splitext(img["file_name"])[0]: img for img in labels_data["images"]}
54
  return annotations_index, image_info_dict
55
 
56
 
57
- def index_annotations_by_image(data: Dict[str, Any], id_to_idx: Optional[Dict[int, int]]):
58
  """
59
  Use image index to lookup every annotations
60
  Args:
@@ -78,7 +78,7 @@ def index_annotations_by_image(data: Dict[str, Any], id_to_idx: Optional[Dict[in
78
  return annotation_lookup
79
 
80
 
81
- def get_scaled_segmentation(
82
  annotations: List[Dict[str, Any]], image_dimensions: Dict[str, int]
83
  ) -> Optional[List[List[float]]]:
84
  """
 
6
 
7
  import numpy as np
8
 
9
+ from yolo.tools.data_conversion import discretize_categories
10
 
11
 
12
+ def locate_label_paths(dataset_path: str, phase_name: str):
13
  """
14
  Find the path to label files for a specified dataset and phase(e.g. training).
15
 
 
35
  raise FileNotFoundError("No labels found in the specified dataset path and phase name.")
36
 
37
 
38
+ def create_image_metadata(labels_path: str) -> Tuple[Dict[str, List], Dict[str, Dict]]:
39
  """
40
  Create a dictionary containing image information and annotations indexed by image ID.
41
 
 
49
  with open(labels_path, "r") as file:
50
  labels_data = json.load(file)
51
  id_to_idx = discretize_categories(labels_data.get("categories", [])) if "categories" in labels_data else None
52
+ annotations_index = organize_annotations_by_image(labels_data, id_to_idx) # check lookup is a good name?
53
  image_info_dict = {path.splitext(img["file_name"])[0]: img for img in labels_data["images"]}
54
  return annotations_index, image_info_dict
55
 
56
 
57
+ def organize_annotations_by_image(data: Dict[str, Any], id_to_idx: Optional[Dict[int, int]]):
58
  """
59
  Use image index to lookup every annotations
60
  Args:
 
78
  return annotation_lookup
79
 
80
 
81
+ def scale_segmentation(
82
  annotations: List[Dict[str, Any]], image_dimensions: Dict[str, int]
83
  ) -> Optional[List[List[float]]]:
84
  """
yolo/{tools/log_helper.py β†’ utils/logging_utils.py} RENAMED
@@ -35,7 +35,7 @@ def custom_logger():
35
  )
36
 
37
 
38
- class CustomProgress:
39
  def __init__(self, cfg: Config, save_path: str, use_wandb: bool = False):
40
  self.progress = Progress(
41
  TextColumn("[progress.description]{task.description}"),
@@ -87,7 +87,7 @@ def custom_wandb_log(string="", level=int, newline=True, repeat=True, prefix=Tru
87
  logger.opt(raw=not newline, colors=True).info("🌐 " + line)
88
 
89
 
90
- def log_model(model: List[YOLOLayer]):
91
  console = Console()
92
  table = Table(title="Model Layers")
93
 
@@ -108,7 +108,7 @@ def log_model(model: List[YOLOLayer]):
108
  console.print(table)
109
 
110
 
111
- def get_valid_folder(general_cfg: GeneralConfig, exp_name):
112
  base_path = os.path.join(general_cfg.out_path, general_cfg.task)
113
  save_path = os.path.join(base_path, exp_name)
114
 
 
35
  )
36
 
37
 
38
+ class ProgressTracker:
39
  def __init__(self, cfg: Config, save_path: str, use_wandb: bool = False):
40
  self.progress = Progress(
41
  TextColumn("[progress.description]{task.description}"),
 
87
  logger.opt(raw=not newline, colors=True).info("🌐 " + line)
88
 
89
 
90
+ def log_model_structure(model: List[YOLOLayer]):
91
  console = Console()
92
  table = Table(title="Model Layers")
93
 
 
108
  console.print(table)
109
 
110
 
111
+ def validate_log_directory(general_cfg: GeneralConfig, exp_name):
112
  base_path = os.path.join(general_cfg.out_path, general_cfg.task)
113
  save_path = os.path.join(base_path, exp_name)
114
 
yolo/{tools/model_helper.py β†’ utils/model_utils.py} RENAMED
@@ -8,7 +8,7 @@ from yolo.config.config import OptimizerConfig, SchedulerConfig
8
  from yolo.model.yolo import YOLO
9
 
10
 
11
- class EMA:
12
  def __init__(self, model: torch.nn.Module, decay: float):
13
  self.model = model
14
  self.decay = decay
@@ -32,7 +32,7 @@ class EMA:
32
  self.shadow[name].copy_(param.data)
33
 
34
 
35
- def get_optimizer(model: YOLO, optim_cfg: OptimizerConfig) -> Optimizer:
36
  """Create an optimizer for the given model parameters based on the configuration.
37
 
38
  Returns:
@@ -52,7 +52,7 @@ def get_optimizer(model: YOLO, optim_cfg: OptimizerConfig) -> Optimizer:
52
  return optimizer_class(model_parameters, **optim_cfg.args)
53
 
54
 
55
- def get_scheduler(optimizer: Optimizer, schedule_cfg: SchedulerConfig) -> _LRScheduler:
56
  """Create a learning rate scheduler for the given optimizer based on the configuration.
57
 
58
  Returns:
 
8
  from yolo.model.yolo import YOLO
9
 
10
 
11
+ class ExponentialMovingAverage:
12
  def __init__(self, model: torch.nn.Module, decay: float):
13
  self.model = model
14
  self.decay = decay
 
32
  self.shadow[name].copy_(param.data)
33
 
34
 
35
+ def create_optimizer(model: YOLO, optim_cfg: OptimizerConfig) -> Optimizer:
36
  """Create an optimizer for the given model parameters based on the configuration.
37
 
38
  Returns:
 
52
  return optimizer_class(model_parameters, **optim_cfg.args)
53
 
54
 
55
+ def create_scheduler(optimizer: Optimizer, schedule_cfg: SchedulerConfig) -> _LRScheduler:
56
  """Create a learning rate scheduler for the given optimizer based on the configuration.
57
 
58
  Returns:
yolo/{tools/module_helper.py β†’ utils/module_utils.py} RENAMED
@@ -5,20 +5,6 @@ from torch import Tensor, nn
5
  from torch.nn.common_types import _size_2_t
6
 
7
 
8
- def auto_pad(kernel_size: _size_2_t, dilation: _size_2_t = 1, **kwargs) -> Tuple[int, int]:
9
- """
10
- Auto Padding for the convolution blocks
11
- """
12
- if isinstance(kernel_size, int):
13
- kernel_size = (kernel_size, kernel_size)
14
- if isinstance(dilation, int):
15
- dilation = (dilation, dilation)
16
-
17
- pad_h = ((kernel_size[0] - 1) * dilation[0]) // 2
18
- pad_w = ((kernel_size[1] - 1) * dilation[1]) // 2
19
- return (pad_h, pad_w)
20
-
21
-
22
  def get_layer_map():
23
  """
24
  Dynamically generates a dictionary mapping class names to classes,
@@ -34,7 +20,21 @@ def get_layer_map():
34
  return layer_map
35
 
36
 
37
- def get_activation(activation: str) -> nn.Module:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
  """
39
  Retrieves an activation function from the PyTorch nn module based on its name, case-insensitively.
40
  """
@@ -59,7 +59,7 @@ def round_up(x: Union[int, Tensor], div: int = 1) -> Union[int, Tensor]:
59
  return x + (-x % div)
60
 
61
 
62
- def make_chunk(input_list, chunk_num):
63
  """
64
  Args: input_list: [0, 1, 2, 3, 4, 5], chunk: 2
65
  Return: [[0, 1, 2], [3, 4, 5]]
 
5
  from torch.nn.common_types import _size_2_t
6
 
7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
  def get_layer_map():
9
  """
10
  Dynamically generates a dictionary mapping class names to classes,
 
20
  return layer_map
21
 
22
 
23
+ def auto_pad(kernel_size: _size_2_t, dilation: _size_2_t = 1, **kwargs) -> Tuple[int, int]:
24
+ """
25
+ Auto Padding for the convolution blocks
26
+ """
27
+ if isinstance(kernel_size, int):
28
+ kernel_size = (kernel_size, kernel_size)
29
+ if isinstance(dilation, int):
30
+ dilation = (dilation, dilation)
31
+
32
+ pad_h = ((kernel_size[0] - 1) * dilation[0]) // 2
33
+ pad_w = ((kernel_size[1] - 1) * dilation[1]) // 2
34
+ return (pad_h, pad_w)
35
+
36
+
37
+ def create_activation_function(activation: str) -> nn.Module:
38
  """
39
  Retrieves an activation function from the PyTorch nn module based on its name, case-insensitively.
40
  """
 
59
  return x + (-x % div)
60
 
61
 
62
+ def divide_into_chunks(input_list, chunk_num):
63
  """
64
  Args: input_list: [0, 1, 2, 3, 4, 5], chunk: 2
65
  Return: [[0, 1, 2], [3, 4, 5]]