123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248 |
- #!/usr/bin/env python3
- # -*- coding:utf-8 -*-
- # Copyright (c) 2014-2021 Megvii Inc. All rights reserved.
-
- import torch
- import torch.distributed as dist
- import torch.nn as nn
-
- import os
- import random
-
- from .base_exp import BaseExp
-
-
- class Exp(BaseExp):
- def __init__(self):
- super().__init__()
-
- # ---------------- model config ---------------- #
- self.num_classes = 80
- self.depth = 1.00
- self.width = 1.00
-
- # ---------------- dataloader config ---------------- #
- # set worker to 4 for shorter dataloader init time
- self.data_num_workers = 4
- self.input_size = (640, 640)
- self.random_size = (14, 26)
- self.train_ann = "instances_train2017.json"
- self.val_ann = "instances_val2017.json"
-
- # --------------- transform config ----------------- #
- self.degrees = 10.0
- self.translate = 0.1
- self.scale = (0.1, 2)
- self.mscale = (0.8, 1.6)
- self.shear = 2.0
- self.perspective = 0.0
- self.enable_mixup = True
-
- # -------------- training config --------------------- #
- self.warmup_epochs = 5
- self.max_epoch = 300
- self.warmup_lr = 0
- self.basic_lr_per_img = 0.01 / 64.0
- self.scheduler = "yoloxwarmcos"
- self.no_aug_epochs = 15
- self.min_lr_ratio = 0.05
- self.ema = True
-
- self.weight_decay = 5e-4
- self.momentum = 0.9
- self.print_interval = 10
- self.eval_interval = 10
- self.exp_name = os.path.split(os.path.realpath(__file__))[1].split(".")[0]
-
- # ----------------- testing config ------------------ #
- self.test_size = (640, 640)
- self.test_conf = 0.001
- self.nmsthre = 0.65
-
- def get_model(self):
- from yolox.models import YOLOPAFPN, YOLOX, YOLOXHead
-
- def init_yolo(M):
- for m in M.modules():
- if isinstance(m, nn.BatchNorm2d):
- m.eps = 1e-3
- m.momentum = 0.03
-
- if getattr(self, "model", None) is None:
- in_channels = [256, 512, 1024]
- backbone = YOLOPAFPN(self.depth, self.width, in_channels=in_channels)
- head = YOLOXHead(self.num_classes, self.width, in_channels=in_channels)
- self.model = YOLOX(backbone, head)
-
- self.model.apply(init_yolo)
- self.model.head.initialize_biases(1e-2)
- return self.model
-
- def get_data_loader(self, batch_size, is_distributed, no_aug=False):
- from yolox.data import (
- COCODataset,
- DataLoader,
- InfiniteSampler,
- MosaicDetection,
- TrainTransform,
- YoloBatchSampler
- )
-
- dataset = COCODataset(
- data_dir=None,
- json_file=self.train_ann,
- img_size=self.input_size,
- preproc=TrainTransform(
- rgb_means=(0.485, 0.456, 0.406),
- std=(0.229, 0.224, 0.225),
- max_labels=50,
- ),
- )
-
- dataset = MosaicDetection(
- dataset,
- mosaic=not no_aug,
- img_size=self.input_size,
- preproc=TrainTransform(
- rgb_means=(0.485, 0.456, 0.406),
- std=(0.229, 0.224, 0.225),
- max_labels=120,
- ),
- degrees=self.degrees,
- translate=self.translate,
- scale=self.scale,
- shear=self.shear,
- perspective=self.perspective,
- enable_mixup=self.enable_mixup,
- )
-
- self.dataset = dataset
-
- if is_distributed:
- batch_size = batch_size // dist.get_world_size()
-
- sampler = InfiniteSampler(len(self.dataset), seed=self.seed if self.seed else 0)
-
- batch_sampler = YoloBatchSampler(
- sampler=sampler,
- batch_size=batch_size,
- drop_last=False,
- input_dimension=self.input_size,
- mosaic=not no_aug,
- )
-
- dataloader_kwargs = {"num_workers": self.data_num_workers, "pin_memory": True}
- dataloader_kwargs["batch_sampler"] = batch_sampler
- train_loader = DataLoader(self.dataset, **dataloader_kwargs)
-
- return train_loader
-
- def random_resize(self, data_loader, epoch, rank, is_distributed):
- tensor = torch.LongTensor(2).cuda()
-
- if rank == 0:
- size_factor = self.input_size[1] * 1.0 / self.input_size[0]
- size = random.randint(*self.random_size)
- size = (int(32 * size), 32 * int(size * size_factor))
- tensor[0] = size[0]
- tensor[1] = size[1]
-
- if is_distributed:
- dist.barrier()
- dist.broadcast(tensor, 0)
-
- input_size = data_loader.change_input_dim(
- multiple=(tensor[0].item(), tensor[1].item()), random_range=None
- )
- return input_size
-
- def get_optimizer(self, batch_size):
- if "optimizer" not in self.__dict__:
- if self.warmup_epochs > 0:
- lr = self.warmup_lr
- else:
- lr = self.basic_lr_per_img * batch_size
-
- pg0, pg1, pg2 = [], [], [] # optimizer parameter groups
-
- for k, v in self.model.named_modules():
- if hasattr(v, "bias") and isinstance(v.bias, nn.Parameter):
- pg2.append(v.bias) # biases
- if isinstance(v, nn.BatchNorm2d) or "bn" in k:
- pg0.append(v.weight) # no decay
- elif hasattr(v, "weight") and isinstance(v.weight, nn.Parameter):
- pg1.append(v.weight) # apply decay
-
- optimizer = torch.optim.SGD(
- pg0, lr=lr, momentum=self.momentum, nesterov=True
- )
- optimizer.add_param_group(
- {"params": pg1, "weight_decay": self.weight_decay}
- ) # add pg1 with weight_decay
- optimizer.add_param_group({"params": pg2})
- self.optimizer = optimizer
-
- return self.optimizer
-
- def get_lr_scheduler(self, lr, iters_per_epoch):
- from yolox.utils import LRScheduler
-
- scheduler = LRScheduler(
- self.scheduler,
- lr,
- iters_per_epoch,
- self.max_epoch,
- warmup_epochs=self.warmup_epochs,
- warmup_lr_start=self.warmup_lr,
- no_aug_epochs=self.no_aug_epochs,
- min_lr_ratio=self.min_lr_ratio,
- )
- return scheduler
-
- def get_eval_loader(self, batch_size, is_distributed, testdev=False):
- from yolox.data import COCODataset, ValTransform
-
- valdataset = COCODataset(
- data_dir=None,
- json_file=self.val_ann if not testdev else "image_info_test-dev2017.json",
- name="val2017" if not testdev else "test2017",
- img_size=self.test_size,
- preproc=ValTransform(
- rgb_means=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225)
- ),
- )
-
- if is_distributed:
- batch_size = batch_size // dist.get_world_size()
- sampler = torch.utils.data.distributed.DistributedSampler(
- valdataset, shuffle=False
- )
- else:
- sampler = torch.utils.data.SequentialSampler(valdataset)
-
- dataloader_kwargs = {
- "num_workers": self.data_num_workers,
- "pin_memory": True,
- "sampler": sampler,
- }
- dataloader_kwargs["batch_size"] = batch_size
- val_loader = torch.utils.data.DataLoader(valdataset, **dataloader_kwargs)
-
- return val_loader
-
- def get_evaluator(self, batch_size, is_distributed, testdev=False):
- from yolox.evaluators import COCOEvaluator
-
- val_loader = self.get_eval_loader(batch_size, is_distributed, testdev=testdev)
- evaluator = COCOEvaluator(
- dataloader=val_loader,
- img_size=self.test_size,
- confthre=self.test_conf,
- nmsthre=self.nmsthre,
- num_classes=self.num_classes,
- testdev=testdev,
- )
- return evaluator
-
- def eval(self, model, evaluator, is_distributed, half=False):
- return evaluator.evaluate(model, is_distributed, half)
|