Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Latest updates on performance optimization and validation #88

Open
wants to merge 34 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
b27a21d
fix input shape parsing
matteobeltrami Jan 16, 2024
c1dc2d6
modified
matteobeltrami Feb 9, 2024
5bdc66f
train yolov8n
matteobeltrami Feb 9, 2024
4b20b82
some progress
matteobeltrami Feb 15, 2024
a069697
weights
matteobeltrami Feb 15, 2024
5369f6d
tests on coco
matteobeltrami Feb 15, 2024
513f4d3
Merge branch 'train_optim' of https://github.com/matteobeltrami/micro…
matteobeltrami Feb 15, 2024
0abd2de
clean - cit.
matteobeltrami Feb 15, 2024
92a197c
opt matches ultra w/ param groups and fit
matteobeltrami Feb 16, 2024
f107ece
align with ultralytics optimization -- maybe ?
matteobeltrami Feb 16, 2024
82475e0
fix lr print
matteobeltrami Feb 16, 2024
662f6f0
fix lrf
matteobeltrami Feb 16, 2024
88e2fe1
remove opt neck
matteobeltrami Feb 20, 2024
c9ae383
load phinetODL
fpaissan Feb 21, 2024
5528221
change config
fpaissan Feb 21, 2024
b5ee9f0
start experiments on VOC with all heads
fpaissan Feb 21, 2024
51d80cc
res=320
fpaissan Feb 21, 2024
36cf415
removed eval step
fpaissan Feb 21, 2024
7d3a7c6
coco eval
matteobeltrami Feb 21, 2024
8914379
switch to opt neck
matteobeltrami Feb 21, 2024
e9f8cca
fix yoloneck scale
fpaissan Feb 21, 2024
925026e
working validation
matteobeltrami Feb 21, 2024
b1aaebb
fix checkpointing problem
fpaissan Feb 21, 2024
dfd1de7
coco phinet
matteobeltrami Feb 21, 2024
3594c82
voc configs
fpaissan Feb 22, 2024
35a5f65
merge valid?
fpaissan Feb 22, 2024
c21fd69
fix validation on precision
fpaissan Feb 22, 2024
97dd313
Merge pull request #1 from fpaissan/merge_valid
fpaissan Feb 26, 2024
e96cc32
LR=LR*10 test..
fpaissan Feb 26, 2024
146fc3c
running again on precision
fpaissan Feb 27, 2024
e87d80a
yolo neck (gamma=2)
fpaissan Feb 28, 2024
d95a177
latest setup
fpaissan Mar 8, 2024
49ba903
last changes on validation
matteobeltrami Mar 28, 2024
658f3b2
performance and validation opt
matteobeltrami Apr 5, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 34 additions & 16 deletions micromind/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
Authors:
- Francesco Paissan, 2023
"""

from abc import ABC, abstractmethod
from argparse import Namespace
from dataclasses import dataclass
Expand Down Expand Up @@ -331,7 +332,7 @@ def add_forward_to_modules(self):
self.modules.device = self.device

@torch.no_grad()
def compute_params(self):
def compute_params(self, str="total"):
"""Computes the number of parameters for the modules inside `self.modules`.
Returns a dictionary with the parameter count for each module.

Expand All @@ -341,8 +342,12 @@ def compute_params(self):
"""
self.eval()
params = {}
for k, m in self.modules.items():
params[k] = summary(m, verbose=0).total_params
if str == "total":
for k, m in self.modules.items():
params[k] = summary(m, verbose=0).total_params
if str == "trainable":
for k, m in self.modules.items():
params[k] = summary(m, verbose=0).trainable_params

return params

Expand Down Expand Up @@ -451,6 +456,10 @@ def on_train_end(self):
"""Runs at the end of each training. Cleans up before exiting."""
pass

def on_train_epoch_end(self):
"""Runs at the end of each training epoch. Cleans up before exiting."""
pass

def eval(self):
self.modules.eval()

Expand All @@ -460,6 +469,7 @@ def train(
datasets: Dict = {},
metrics: List[Metric] = [],
checkpointer: Optional[Checkpointer] = None,
max_norm=10.0,
debug: Optional[bool] = False,
) -> None:
"""
Expand Down Expand Up @@ -525,12 +535,12 @@ def train(
loss_epoch += loss.item()

self.accelerator.backward(loss)
self.accelerator.clip_grad_norm_(
self.modules.parameters(), max_norm=max_norm
)
self.opt.step()

loss_epoch += loss.item()
if hasattr(self, "lr_sched"):
# ok for cos_lr
self.lr_sched.step()

for m in self.metrics:
if (
Expand Down Expand Up @@ -563,21 +573,29 @@ def train(

if "val" in datasets:
val_metrics = self.validate()
if (
self.accelerator.is_local_main_process
and self.checkpointer is not None
):
self.checkpointer(
self,
train_metrics,
val_metrics,
)
else:
val_metrics = train_metrics.update({"val_loss": loss_epoch / (idx + 1)})
train_metrics.update({"val_loss": loss_epoch / (idx + 1)})
val_metrics = train_metrics

self.on_train_epoch_end()

if self.accelerator.is_local_main_process and self.checkpointer is not None:
self.checkpointer(
self,
train_metrics,
val_metrics,
)

if e >= 1 and self.debug:
break

if hasattr(self, "lr_sched"):
# ok for cos_lr
# self.lr_sched.step(val_metrics["val_loss"])

self.lr_sched.step()
print(f"sched step - new LR={self.lr_sched.get_lr()}")

self.on_train_end()
return None

Expand Down
87 changes: 84 additions & 3 deletions micromind/networks/yolo.py
Original file line number Diff line number Diff line change
Expand Up @@ -464,13 +464,18 @@ def __init__(
self.heads = heads
self.up1 = Upsample(up[0], mode="nearest")
self.up2 = Upsample(up[1], mode="nearest")

# print(filters, heads)
# breakpoint()

self.n1 = XiConv(
c_in=int(filters[1] + filters[2]),
c_out=int(filters[1]),
kernel_size=3,
gamma=3,
skip_tensor_in=False,
)

self.n2 = XiConv(
int(filters[0] + filters[1]),
int(filters[0]),
Expand All @@ -483,6 +488,10 @@ def __init__(
the needed blocks. Otherwise the not needed blocks would be initialized
(and thus would occupy space) but will never be used.
"""
self.n3 = None
self.n4 = None
self.n5 = None
self.n6 = None
if self.heads[1] or self.heads[2]:
self.n3 = XiConv(
int(filters[0]),
Expand Down Expand Up @@ -519,6 +528,75 @@ def __init__(
)


class Yolov8NeckOpt_gamma2(Yolov8Neck):
def __init__(
self, filters=[256, 512, 768], up=[2, 2], heads=[True, True, True], d=1
):
super().__init__()
self.heads = heads
self.up1 = Upsample(up[0], mode="nearest")
self.up2 = Upsample(up[1], mode="nearest")

self.n1 = XiConv(
c_in=int(filters[1] + filters[2]),
c_out=int(filters[1]),
kernel_size=3,
gamma=2,
skip_tensor_in=False,
)

self.n2 = XiConv(
int(filters[0] + filters[1]),
int(filters[0]),
kernel_size=3,
gamma=2,
skip_tensor_in=False,
)
"""
Only if we decide to use the 2nd and 3rd detection head we define
the needed blocks. Otherwise the not needed blocks would be initialized
(and thus would occupy space) but will never be used.
"""
self.n3 = None
self.n4 = None
self.n5 = None
self.n6 = None
if self.heads[1] or self.heads[2]:
self.n3 = XiConv(
int(filters[0]),
int(filters[0]),
kernel_size=3,
gamma=2,
stride=2,
padding=1,
skip_tensor_in=False,
)
self.n4 = XiConv(
int(filters[0] + filters[1]),
int(filters[1]),
kernel_size=3,
gamma=2,
skip_tensor_in=False,
)
if self.heads[2]:
self.n5 = XiConv(
int(filters[1]),
int(filters[1]),
gamma=2,
kernel_size=3,
stride=2,
padding=1,
skip_tensor_in=False,
)
self.n6 = XiConv(
int(filters[1] + filters[2]),
int(filters[2]),
gamma=2,
kernel_size=3,
skip_tensor_in=False,
)


class DetectionHead(nn.Module):
"""Implements YOLOv8's detection head.

Expand All @@ -537,6 +615,7 @@ def __init__(self, nc=80, filters=(), heads=[True, True, True]):
super().__init__()
self.reg_max = 16
self.nc = nc
# filters = [f for f, h in zip(filters, heads) if h]
self.nl = len(filters)
self.no = nc + self.reg_max * 4
self.stride = torch.tensor([8.0, 16.0, 32.0], dtype=torch.float16)
Expand Down Expand Up @@ -615,14 +694,16 @@ class YOLOv8(nn.Module):
Number of classes to predict.
"""

def __init__(self, w, r, d, num_classes=80):
def __init__(self, w, r, d, num_classes=80, heads=[True, True, True]):
super().__init__()
self.net = Darknet(w, r, d)
self.fpn = Yolov8Neck(
filters=[int(256 * w), int(512 * w), int(512 * w * r)], d=d
filters=[int(256 * w), int(512 * w), int(512 * w * r)], heads=heads, d=d
)
self.head = DetectionHead(
num_classes, filters=(int(256 * w), int(512 * w), int(512 * w * r))
num_classes,
filters=(int(256 * w), int(512 * w), int(512 * w * r)),
heads=heads,
)

def forward(self, x):
Expand Down
1 change: 1 addition & 0 deletions recipes/object_detection/README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
## Object Detection using YOLO

**[16 Jan 2024]** Updated training code for better performance. Added ultralytics metrics calculation .<br />
**[16 Jan 2024]** Added optimized YOLO neck, using XiConv. Fixed compatibility with ultralytics weights.<br />
**[17 Dec 2023]** Add VOC dataset, selective head option, and instructions for dataset download.<br />
**[1 Dec 2023]** Fix DDP handling and computational graph.
Expand Down
2 changes: 1 addition & 1 deletion recipes/object_detection/cfg/data/VOC.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ mixup: 0.0 # (float) image mixup (probability)
copy_paste: 0.0 # (float) segment copy-paste (probability)

# Train/val/test sets as 1) dir: path/to/imgs, 2) file: path/to/imgs.txt, or 3) list: [path/to/imgs1, path/to/imgs2, ..]
path: ../datasets/VOC
path: datasets/VOC
train: # train images (relative to 'path') 16551 images
- images/train2012
- images/train2007
Expand Down
2 changes: 1 addition & 1 deletion recipes/object_detection/cfg/data/coco.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ copy_paste: 0.0 # (float) segment copy-paste (probability)


# Dataset location
path: /mnt/data/coco # dataset root dir
path: datasets/coco # dataset root dir
train: train2017.txt # train images (relative to 'path') 118287 images
val: val2017.txt # val images (relative to 'path') 5000 images
test: test-dev2017.txt # 20288 of 40670 images, submit to https://competitions.codalab.org/competitions/20794
Expand Down
2 changes: 1 addition & 1 deletion recipes/object_detection/cfg/data/coco8.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ copy_paste: 0.0 # (float) segment copy-paste (probability)


# Train/val/test sets as 1) dir: path/to/imgs, 2) file: path/to/imgs.txt, or 3) list: [path/to/imgs1, path/to/imgs2, ..]
path: /mnt/data/coco8 # dataset root dir
path: datasets/coco8 # dataset root dir
train: images/train # train images (relative to 'path') 4 images
val: images/val # val images (relative to 'path') 4 images
test: # test images (optional)
Expand Down
12 changes: 7 additions & 5 deletions recipes/object_detection/cfg/yolo_phinet.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,18 @@
- Matteo Beltrami, 2023
- Francesco Paissan, 2023
"""

# Data configuration
batch_size = 8
data_cfg = "cfg/data/coco.yaml"
data_dir = "data/coco"
epochs = 200
data_cfg = "cfg/data/VOC.yaml"
data_dir = "datasets/coco"
epochs = 350
num_classes = 80

# Model configuration
input_shape = [3, 640, 640]
alpha = 2.3
num_layers = 7
alpha = 1.1
num_layers = 8
beta = 0.75
t_zero = 5
divisor = 8
Expand Down
33 changes: 14 additions & 19 deletions recipes/object_detection/inference.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,25 +27,16 @@
preprocess,
)
from train import YOLO
from micromind.utils.yolo import load_config


class Inference(YOLO):
def __init__(self, hparams):
super().__init__(hparams=hparams, m_cfg={})

def forward(self, img):
"""Executes the detection network.

Arguments
---------
bacth : List[torch.Tensor]
Input to the detection network.

Returns
-------
Output of the detection network : torch.Tensor
"""
backbone = self.modules["backbone"](img)
def __init__(self, m_cfg, hparams):
super().__init__(m_cfg, hparams=hparams)

def forward(self, batch):
"""Runs the forward method by calling every module."""
backbone = self.modules["backbone"](batch)
neck_input = backbone[1]
neck_input.append(self.modules["sppf"](backbone[0]))
neck = self.modules["neck"](*neck_input)
Expand Down Expand Up @@ -73,6 +64,8 @@ def forward(self, img):
img_paths = [sys.argv[2]]
for img_path in img_paths:
image = torchvision.io.read_image(img_path)
if image.shape[0] == 4:
image = image[:3, :, :] # Mantieni solo i primi 3 canali (RGB)
out_paths = [
(
output_folder_path
Expand All @@ -85,7 +78,8 @@ def forward(self, img):

pre_processed_image = preprocess(image)

model = Inference(hparams)
m_cfg, data_cfg = load_config(hparams.data_cfg)
model = Inference(m_cfg, hparams=hparams)
# Load pretrained if passed.
if hparams.ckpt_pretrained != "":
model.load_modules(hparams.ckpt_pretrained)
Expand All @@ -97,11 +91,13 @@ def forward(self, img):

with torch.no_grad():
st = time.time()
predictions = model(pre_processed_image)
predictions = model.forward(pre_processed_image)
print(f"Inference took {int(round(((time.time() - st) * 1000)))}ms")
breakpoint()
post_predictions = postprocess(
preds=predictions[0], img=pre_processed_image, orig_imgs=image
)
breakpoint()

class_labels = [s.strip() for s in open(hparams.coco_names, "r").readlines()]
draw_bounding_boxes_and_save(
Expand All @@ -112,4 +108,3 @@ def forward(self, img):
)

# Exporting onnx model.
# model.export("model.onnx", "onnx", hparams.input_shape)
1 change: 1 addition & 0 deletions recipes/object_detection/prepare_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
- Matteo Beltrami, 2023
- Francesco Paissan, 2023
"""

from typing import Dict
import os

Expand Down
Loading
Loading