Spaces:
Running
Running
Merge branch 'MODEL' of github.com:LucyTuan/yolov9mit into MODEL
Browse files- yolo/model/module.py +43 -2
- yolo/model/yolo.py +2 -7
- yolo/tools/module_helper.py +26 -2
yolo/model/module.py
CHANGED
|
@@ -1,10 +1,10 @@
|
|
| 1 |
-
from typing import Any, Dict, Optional, Tuple
|
| 2 |
|
| 3 |
import torch
|
| 4 |
from torch import Tensor, nn
|
| 5 |
from torch.nn.common_types import _size_2_t
|
| 6 |
|
| 7 |
-
from yolo.tools.module_helper import auto_pad, get_activation
|
| 8 |
|
| 9 |
|
| 10 |
class Conv(nn.Module):
|
|
@@ -99,6 +99,47 @@ class SPPELAN(nn.Module):
|
|
| 99 |
#### -- ####
|
| 100 |
|
| 101 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 102 |
# RepVGG
|
| 103 |
class RepConv(nn.Module):
|
| 104 |
"""A convolutional block that combines two convolution layers (kernel and point-wise)."""
|
|
|
|
| 1 |
+
from typing import Any, Dict, List, Optional, Tuple
|
| 2 |
|
| 3 |
import torch
|
| 4 |
from torch import Tensor, nn
|
| 5 |
from torch.nn.common_types import _size_2_t
|
| 6 |
|
| 7 |
+
from yolo.tools.module_helper import auto_pad, get_activation, round_up
|
| 8 |
|
| 9 |
|
| 10 |
class Conv(nn.Module):
|
|
|
|
| 99 |
#### -- ####
|
| 100 |
|
| 101 |
|
| 102 |
+
class Detection(nn.Module):
|
| 103 |
+
"""A single YOLO Detection head for detection models"""
|
| 104 |
+
|
| 105 |
+
def __init__(self, in_channels: int, num_classes: int, *, reg_max: int = 16, use_group: bool = True):
|
| 106 |
+
super().__init__()
|
| 107 |
+
|
| 108 |
+
groups = 4 if use_group else 1
|
| 109 |
+
anchor_channels = 4 * reg_max
|
| 110 |
+
# TODO: round up head[0] channels or each head?
|
| 111 |
+
anchor_neck = max(round_up(in_channels // 4, groups), anchor_channels, 16)
|
| 112 |
+
class_neck = max(in_channels, min(num_classes * 2, 128))
|
| 113 |
+
|
| 114 |
+
self.anchor_conv = nn.Sequential(
|
| 115 |
+
Conv(in_channels, anchor_neck, 3),
|
| 116 |
+
Conv(anchor_neck, anchor_neck, 3, groups=groups),
|
| 117 |
+
nn.Conv2d(anchor_neck, anchor_channels, 1, groups=groups),
|
| 118 |
+
)
|
| 119 |
+
self.class_conv = nn.Sequential(
|
| 120 |
+
Conv(in_channels, class_neck, 3), Conv(class_neck, class_neck, 3), nn.Conv2d(class_neck, num_classes, 1)
|
| 121 |
+
)
|
| 122 |
+
|
| 123 |
+
def forward(self, x: List[Tensor]) -> List[Tensor]:
|
| 124 |
+
anchor_x = self.anchor_conv(x)
|
| 125 |
+
class_x = self.class_conv(x)
|
| 126 |
+
return torch.cat([anchor_x, class_x], dim=1)
|
| 127 |
+
|
| 128 |
+
|
| 129 |
+
class MultiheadDetection(nn.Module):
|
| 130 |
+
"""Mutlihead Detection module for Dual detect or Triple detect"""
|
| 131 |
+
|
| 132 |
+
def __init__(self, in_channels: List[int], num_classes: int, **head_kwargs):
|
| 133 |
+
super().__init__()
|
| 134 |
+
self.heads = nn.ModuleList(
|
| 135 |
+
[Detection(head_in_channels, num_classes, **head_kwargs) for head_in_channels in in_channels]
|
| 136 |
+
)
|
| 137 |
+
|
| 138 |
+
def forward(self, x_list: List[torch.Tensor]) -> List[torch.Tensor]:
|
| 139 |
+
return [head(x) for x, head in zip(x_list, self.heads)]
|
| 140 |
+
|
| 141 |
+
|
| 142 |
+
#### -- ####
|
| 143 |
# RepVGG
|
| 144 |
class RepConv(nn.Module):
|
| 145 |
"""A convolutional block that combines two convolution layers (kernel and point-wise)."""
|
yolo/model/yolo.py
CHANGED
|
@@ -27,8 +27,9 @@ class YOLO(nn.Module):
|
|
| 27 |
model_list = nn.ModuleList()
|
| 28 |
output_dim = [3]
|
| 29 |
layer_indices_by_tag = {}
|
|
|
|
| 30 |
for arch_name in model_arch:
|
| 31 |
-
logger.info(f"🏗️ Building
|
| 32 |
for layer_idx, layer_spec in enumerate(model_arch[arch_name], start=1):
|
| 33 |
layer_type, layer_info = next(iter(layer_spec.items()))
|
| 34 |
layer_args = layer_info.get("args", {})
|
|
@@ -102,9 +103,3 @@ def get_model(model_cfg: dict) -> YOLO:
|
|
| 102 |
model = YOLO(model_cfg)
|
| 103 |
logger.info("✅ Success load model")
|
| 104 |
return model
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
if __name__ == "__main__":
|
| 108 |
-
model_cfg = load_model_cfg("v7-base")
|
| 109 |
-
|
| 110 |
-
YOLO(model_cfg)
|
|
|
|
| 27 |
model_list = nn.ModuleList()
|
| 28 |
output_dim = [3]
|
| 29 |
layer_indices_by_tag = {}
|
| 30 |
+
logger.info(f"🚜 Building YOLO")
|
| 31 |
for arch_name in model_arch:
|
| 32 |
+
logger.info(f" 🏗️ Building {arch_name}")
|
| 33 |
for layer_idx, layer_spec in enumerate(model_arch[arch_name], start=1):
|
| 34 |
layer_type, layer_info = next(iter(layer_spec.items()))
|
| 35 |
layer_args = layer_info.get("args", {})
|
|
|
|
| 103 |
model = YOLO(model_cfg)
|
| 104 |
logger.info("✅ Success load model")
|
| 105 |
return model
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
yolo/tools/module_helper.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
| 1 |
-
from typing import Tuple
|
| 2 |
|
| 3 |
-
from torch import nn
|
| 4 |
from torch.nn.common_types import _size_2_t
|
| 5 |
|
| 6 |
|
|
@@ -34,3 +34,27 @@ def get_activation(activation: str) -> nn.Module:
|
|
| 34 |
return activation_map[activation.lower()]()
|
| 35 |
else:
|
| 36 |
raise ValueError(f"Activation function '{activation}' is not found in torch.nn")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import Tuple, Union
|
| 2 |
|
| 3 |
+
from torch import Tensor, nn
|
| 4 |
from torch.nn.common_types import _size_2_t
|
| 5 |
|
| 6 |
|
|
|
|
| 34 |
return activation_map[activation.lower()]()
|
| 35 |
else:
|
| 36 |
raise ValueError(f"Activation function '{activation}' is not found in torch.nn")
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
def round_up(x: Union[int, Tensor], div: int = 1) -> Union[int, Tensor]:
|
| 40 |
+
"""
|
| 41 |
+
Rounds up `x` to the bigger-nearest multiple of `div`.
|
| 42 |
+
"""
|
| 43 |
+
return x + (-x % div)
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
def make_chunk(input_list, chunk_num):
|
| 47 |
+
"""
|
| 48 |
+
Args: input_list: [0, 1, 2, 3, 4, 5], chunk: 2
|
| 49 |
+
Return: [[0, 1, 2], [3, 4, 5]]
|
| 50 |
+
"""
|
| 51 |
+
list_size = len(input_list)
|
| 52 |
+
|
| 53 |
+
if list_size % chunk_num != 0:
|
| 54 |
+
raise ValueError(
|
| 55 |
+
f"The length of the input list ({list_size}) must be exactly\
|
| 56 |
+
divisible by the number of chunks ({chunk_num})."
|
| 57 |
+
)
|
| 58 |
+
|
| 59 |
+
chunk_size = list_size // chunk_num
|
| 60 |
+
return [input_list[i : i + chunk_size] for i in range(0, list_size, chunk_size)]
|