Spaces:
Runtime error
Runtime error
| #!/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) | |