henry000 commited on
Commit
684d672
Β·
2 Parent(s): 033231b 6707904

πŸ”€ [Merge] branch 'DATASET'

Browse files
LICENSE CHANGED
@@ -1,6 +1,6 @@
1
  MIT License
2
 
3
- Copyright (c) 2024 Kin-Yiu, Wong
4
 
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
  of this software and associated documentation files (the "Software"), to deal
 
1
  MIT License
2
 
3
+ Copyright (c) 2024 Kin-Yiu, Wong and Hao-Tang, Tsui
4
 
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
  of this software and associated documentation files (the "Software"), to deal
yolo/config/config.py CHANGED
@@ -72,7 +72,7 @@ class MatcherConfig:
72
  @dataclass
73
  class LossConfig:
74
  objective: List[List]
75
- aux: bool
76
  matcher: MatcherConfig
77
 
78
 
 
72
  @dataclass
73
  class LossConfig:
74
  objective: List[List]
75
+ aux: Union[bool, float]
76
  matcher: MatcherConfig
77
 
78
 
yolo/config/hyper/default.yaml CHANGED
@@ -1,7 +1,7 @@
1
  data:
2
- batch_size: 8
3
  shuffle: True
4
- num_workers: 4
5
  pin_memory: True
6
  class_num: 80
7
  image_size: [640, 640]
@@ -13,11 +13,11 @@ train:
13
  weight_decay: 0.0001
14
  loss:
15
  objective:
16
- - [BCELoss, 0.1]
17
- - [BoxLoss, 0.1]
18
- - [DFLoss, 0.1]
19
  aux:
20
- True
21
  matcher:
22
  iou: CIoU
23
  topk: 10
 
1
  data:
2
+ batch_size: 16
3
  shuffle: True
4
+ num_workers: 16
5
  pin_memory: True
6
  class_num: 80
7
  image_size: [640, 640]
 
13
  weight_decay: 0.0001
14
  loss:
15
  objective:
16
+ BCELoss: 0.5
17
+ BoxLoss: 7.5
18
+ DFLoss: 1.5
19
  aux:
20
+ 0.25
21
  matcher:
22
  iou: CIoU
23
  topk: 10
yolo/tools/log_helper.py CHANGED
@@ -16,6 +16,7 @@ from typing import List
16
 
17
  from loguru import logger
18
  from rich.console import Console
 
19
  from rich.table import Table
20
 
21
  from yolo.config.config import YOLOLayer
@@ -29,6 +30,34 @@ def custom_logger():
29
  )
30
 
31
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
  def log_model(model: List[YOLOLayer]):
33
  console = Console()
34
  table = Table(title="Model Layers")
 
16
 
17
  from loguru import logger
18
  from rich.console import Console
19
+ from rich.progress import BarColumn, Progress, TextColumn, TimeRemainingColumn
20
  from rich.table import Table
21
 
22
  from yolo.config.config import YOLOLayer
 
30
  )
31
 
32
 
33
+ class CustomProgress:
34
+ def __init__(self):
35
+ self.progress = Progress(
36
+ TextColumn("[progress.description]{task.description}"),
37
+ BarColumn(bar_width=None),
38
+ TextColumn("{task.completed}/{task.total}"),
39
+ TimeRemainingColumn(),
40
+ )
41
+
42
+ def start_train(self, num_epochs: int):
43
+ self.task_epoch = self.progress.add_task("[cyan]Epochs", total=num_epochs)
44
+
45
+ def one_epoch(self):
46
+ self.progress.update(self.task_epoch, advance=1)
47
+
48
+ def start_batch(self, num_batches):
49
+ self.batch_task = self.progress.add_task("[green]Batches", total=num_batches)
50
+
51
+ def one_batch(self, loss_each):
52
+ loss_iou, loss_dfl, loss_cls = loss_each
53
+ # TODO: make it flexible? if need add more loss
54
+ loss_str = f"Loss IoU: {loss_iou:.3f}, DFL: {loss_dfl:.3f}, CLS: {loss_cls:.3f}"
55
+ self.progress.update(self.batch_task, advance=1, description=f"[green]Batches {loss_str}")
56
+
57
+ def finish_batch(self):
58
+ self.progress.remove_task(self.batch_task)
59
+
60
+
61
  def log_model(model: List[YOLOLayer]):
62
  console = Console()
63
  table = Table(title="Model Layers")
yolo/tools/trainer.py CHANGED
@@ -1,11 +1,13 @@
1
  import torch
2
  from loguru import logger
3
  from torch import Tensor
 
 
4
  from torch.cuda.amp import GradScaler, autocast
5
- from tqdm import tqdm
6
 
7
  from yolo.config.config import Config, TrainConfig
8
  from yolo.model.yolo import YOLO
 
9
  from yolo.tools.model_helper import EMA, get_optimizer, get_scheduler
10
  from yolo.utils.loss import get_loss_function
11
 
@@ -26,16 +28,13 @@ class Trainer:
26
  self.ema = None
27
  self.scaler = GradScaler()
28
 
29
- def train_one_batch(self, data: Tensor, targets: Tensor, progress: tqdm):
30
  data, targets = data.to(self.device), targets.to(self.device)
31
  self.optimizer.zero_grad()
32
 
33
  with autocast():
34
  outputs = self.model(data)
35
  loss, loss_item = self.loss_fn(outputs, targets)
36
- loss_iou, loss_dfl, loss_cls = loss_item
37
-
38
- progress.set_description(f"Loss IoU: {loss_iou:.5f}, DFL: {loss_dfl:.5f}, CLS: {loss_cls:.5f}")
39
 
40
  self.scaler.scale(loss).backward()
41
  self.scaler.step(self.optimizer)
@@ -43,17 +42,21 @@ class Trainer:
43
 
44
  return loss.item(), loss_item
45
 
46
- return loss.item()
47
-
48
- def train_one_epoch(self, dataloader):
49
  self.model.train()
50
  total_loss = 0
51
- with tqdm(dataloader, desc="Training") as progress:
52
- for data, targets in progress:
53
- loss = self.train_one_batch(data, targets, progress)
54
- total_loss += loss
55
- if self.scheduler:
56
- self.scheduler.step()
 
 
 
 
 
 
57
  return total_loss / len(dataloader)
58
 
59
  def save_checkpoint(self, epoch: int, filename="checkpoint.pt"):
@@ -69,9 +72,16 @@ class Trainer:
69
  torch.save(checkpoint, filename)
70
 
71
  def train(self, dataloader, num_epochs):
72
- logger.info("start train")
73
- for epoch in range(num_epochs):
74
- epoch_loss = self.train_one_epoch(dataloader)
75
- logger.info(f"Epoch {epoch+1}/{num_epochs}, Loss: {epoch_loss:.4f}")
76
- if (epoch + 1) % 5 == 0:
77
- self.save_checkpoint(epoch, f"checkpoint_epoch_{epoch+1}.pth")
 
 
 
 
 
 
 
 
1
  import torch
2
  from loguru import logger
3
  from torch import Tensor
4
+
5
+ # TODO: We may can't use CUDA?
6
  from torch.cuda.amp import GradScaler, autocast
 
7
 
8
  from yolo.config.config import Config, TrainConfig
9
  from yolo.model.yolo import YOLO
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
 
 
28
  self.ema = None
29
  self.scaler = GradScaler()
30
 
31
+ def train_one_batch(self, data: Tensor, targets: Tensor):
32
  data, targets = data.to(self.device), targets.to(self.device)
33
  self.optimizer.zero_grad()
34
 
35
  with autocast():
36
  outputs = self.model(data)
37
  loss, loss_item = self.loss_fn(outputs, targets)
 
 
 
38
 
39
  self.scaler.scale(loss).backward()
40
  self.scaler.step(self.optimizer)
 
42
 
43
  return loss.item(), loss_item
44
 
45
+ def train_one_epoch(self, dataloader, progress: CustomProgress):
 
 
46
  self.model.train()
47
  total_loss = 0
48
+ progress.start_batch(len(dataloader))
49
+
50
+ for data, targets in dataloader:
51
+ loss, loss_each = self.train_one_batch(data, targets)
52
+
53
+ total_loss += loss
54
+ progress.one_batch(loss_each)
55
+
56
+ if self.scheduler:
57
+ self.scheduler.step()
58
+
59
+ progress.finish_batch()
60
  return total_loss / len(dataloader)
61
 
62
  def save_checkpoint(self, epoch: int, filename="checkpoint.pt"):
 
72
  torch.save(checkpoint, filename)
73
 
74
  def train(self, dataloader, num_epochs):
75
+ logger.info("πŸš„ Start Training!")
76
+ progress = CustomProgress()
77
+
78
+ with progress.progress:
79
+ progress.start_train(num_epochs)
80
+ for epoch in range(num_epochs):
81
+
82
+ epoch_loss = self.train_one_epoch(dataloader, progress)
83
+ progress.one_epoch()
84
+
85
+ logger.info(f"Epoch {epoch+1}/{num_epochs}, Loss: {epoch_loss:.4f}")
86
+ if (epoch + 1) % 5 == 0:
87
+ self.save_checkpoint(epoch, f"checkpoint_epoch_{epoch+1}.pth")
yolo/utils/dataloader.py CHANGED
@@ -178,13 +178,15 @@ class YoloDataLoader(DataLoader):
178
  - A tensor of batched images.
179
  - A list of tensors, each corresponding to bboxes for each image in the batch.
180
  """
181
- batch_size, target_size = len(batch), [item[1].size(0) for item in batch]
182
- batch_targets = torch.zeros(batch_size, max(target_size), 5)
183
- images = []
184
- for idx, (image, target) in enumerate(batch):
185
- images.append(image)
186
- batch_targets[idx, : target_size[idx]] = target
187
- batch_images = torch.stack(images)
 
 
188
  return batch_images, batch_targets
189
 
190
 
 
178
  - A tensor of batched images.
179
  - A list of tensors, each corresponding to bboxes for each image in the batch.
180
  """
181
+ batch_size = len(batch)
182
+ target_sizes = [item[1].size(0) for item in batch]
183
+ # TODO: Improve readability of these proccess
184
+ batch_targets = torch.zeros(batch_size, max(target_sizes), 5)
185
+ for idx, target_size in enumerate(target_sizes):
186
+ batch_targets[idx, :target_size] = batch[idx][1]
187
+
188
+ batch_images = torch.stack([item[0] for item in batch])
189
+
190
  return batch_images, batch_targets
191
 
192
 
yolo/utils/loss.py CHANGED
@@ -15,6 +15,7 @@ from yolo.tools.bbox_helper import (
15
  make_anchor,
16
  transform_bbox,
17
  )
 
18
 
19
 
20
  class BCELoss(nn.Module):
@@ -135,14 +136,10 @@ class YOLOLoss:
135
  anchors_box = anchors_box / self.scaler[None, :, None]
136
  return anchors_cls, anchors_box
137
 
138
- @torch.autocast("cuda" if torch.cuda.is_available() else "cpu")
139
  def __call__(self, predicts: List[Tensor], targets: Tensor) -> Tuple[Tensor, Tensor, Tensor]:
140
  # Batch_Size x (Anchor + Class) x H x W
141
  # TODO: check datatype, why targets has a little bit error with origin version
142
- predicts, predicts_anc = self.parse_predicts(predicts[0])
143
- # TODO: Refactor this operator
144
- # targets = self.parse_targets(targets, batch_size=predicts.size(0))
145
- targets[:, :, 1:] = targets[:, :, 1:] * self.scale_up
146
 
147
  align_targets, valid_masks = self.matcher(targets, predicts)
148
  # calculate loss between with instance and predict
@@ -160,11 +157,35 @@ class YOLOLoss:
160
  ## -- DFL -- ##
161
  loss_dfl = self.dfl(predicts_anc, targets_bbox, valid_masks, box_norm, cls_norm)
162
 
163
- loss_sum = loss_iou * 0.5 + loss_dfl * 1.5 + loss_cls * 0.5
164
- return loss_sum, (loss_iou, loss_dfl, loss_cls)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
165
 
166
 
167
  def get_loss_function(cfg: Config) -> YOLOLoss:
168
- loss_function = YOLOLoss(cfg)
169
  logger.info("βœ… Success load loss function")
170
  return loss_function
 
15
  make_anchor,
16
  transform_bbox,
17
  )
18
+ from yolo.tools.module_helper import make_chunk
19
 
20
 
21
  class BCELoss(nn.Module):
 
136
  anchors_box = anchors_box / self.scaler[None, :, None]
137
  return anchors_cls, anchors_box
138
 
 
139
  def __call__(self, predicts: List[Tensor], targets: Tensor) -> Tuple[Tensor, Tensor, Tensor]:
140
  # Batch_Size x (Anchor + Class) x H x W
141
  # TODO: check datatype, why targets has a little bit error with origin version
142
+ predicts, predicts_anc = self.parse_predicts(predicts)
 
 
 
143
 
144
  align_targets, valid_masks = self.matcher(targets, predicts)
145
  # calculate loss between with instance and predict
 
157
  ## -- DFL -- ##
158
  loss_dfl = self.dfl(predicts_anc, targets_bbox, valid_masks, box_norm, cls_norm)
159
 
160
+ return loss_iou, loss_dfl, loss_cls
161
+
162
+
163
+ class DualLoss:
164
+ def __init__(self, cfg: Config) -> None:
165
+ self.loss = YOLOLoss(cfg)
166
+ self.aux_rate = cfg.hyper.train.loss.aux
167
+
168
+ self.iou_rate = cfg.hyper.train.loss.objective["BoxLoss"]
169
+ self.dfl_rate = cfg.hyper.train.loss.objective["DFLoss"]
170
+ self.cls_rate = cfg.hyper.train.loss.objective["BCELoss"]
171
+
172
+ def __call__(self, predicts: List[Tensor], targets: Tensor) -> Tuple[Tensor, Tuple[Tensor]]:
173
+ targets[:, :, 1:] = targets[:, :, 1:] * self.loss.scale_up
174
+
175
+ # TODO: Need Refactor this region, make it flexible!
176
+ predicts = make_chunk(predicts[0], 2)
177
+ aux_iou, aux_dfl, aux_cls = self.loss(predicts[0], targets)
178
+ main_iou, main_dfl, main_cls = self.loss(predicts[1], targets)
179
+
180
+ loss_iou = self.iou_rate * (aux_iou * self.aux_rate + main_iou)
181
+ loss_dfl = self.dfl_rate * (aux_dfl * self.aux_rate + main_dfl)
182
+ loss_cls = self.cls_rate * (aux_cls * self.aux_rate + main_cls)
183
+
184
+ loss = (loss_iou + loss_dfl + loss_cls) / 3
185
+ return loss, (loss_iou, loss_dfl, loss_cls)
186
 
187
 
188
  def get_loss_function(cfg: Config) -> YOLOLoss:
189
+ loss_function = DualLoss(cfg)
190
  logger.info("βœ… Success load loss function")
191
  return loss_function