{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import timm\nfrom pprint import pprint\npprint(timm.list_models('efficientnet*'))","metadata":{"execution":{"iopub.status.busy":"2023-08-02T09:48:33.296033Z","iopub.execute_input":"2023-08-02T09:48:33.296529Z","iopub.status.idle":"2023-08-02T09:48:33.305026Z","shell.execute_reply.started":"2023-08-02T09:48:33.296492Z","shell.execute_reply":"2023-08-02T09:48:33.303560Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import torch\nimport os\nfrom datetime import datetime\nimport time\nimport random\nimport cv2\nimport pandas as pd\nimport numpy as np\nimport albumentations as A\nimport matplotlib.pyplot as plt\nfrom albumentations.pytorch.transforms import ToTensorV2\nfrom sklearn.model_selection import StratifiedKFold\nfrom torch.utils.data import Dataset,DataLoader\nfrom torch.utils.data.sampler import SequentialSampler, RandomSampler\nfrom glob import glob","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"SEED = 42\n\ndef seed_everything(seed):\n    random.seed(seed)\n    os.environ['PYTHONHASHSEED'] = str(seed)\n    np.random.seed(seed)\n    torch.manual_seed(seed)\n    torch.cuda.manual_seed(seed)\n    torch.backends.cudnn.deterministic = True\n    torch.backends.cudnn.benchmark = True\n\nseed_everything(SEED)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"TrainConfig = {}","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Train Data","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19"}},{"cell_type":"code","source":"%%time\n\nskf = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)\n\ndf_folds = marking[['image_id']].copy()\ndf_folds.loc[:, 'bbox_count'] = 1\ndf_folds = df_folds.groupby('image_id').count()\ndf_folds.loc[:, 'source'] = marking[['image_id', 'source']].groupby('image_id').min()['source']\ndf_folds.loc[:, 'stratify_group'] = np.char.add(\n    df_folds['source'].values.astype(str),\n    df_folds['bbox_count'].apply(lambda x: f'_{x // 15}').values.astype(str)\n)\ndf_folds.loc[:, 'fold'] = 0\n\nfor fold_number, (train_index, val_index) in enumerate(skf.split(X=df_folds.index, y=df_folds['stratify_group'])):\n    df_folds.loc[df_folds.iloc[val_index].index, 'fold'] = fold_number","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Albumentations\ndef get_train_transform():\n    return A.Compose([\n        A.Flip(0.5),\n        ToTensorV2(p=1.0)\n    ], bbox_params={'format': 'pascal_voc', 'label_fields': ['labels']})\n\ndef get_valid_transform():\n    return A.Compose([\n        ToTensorV2(p=1.0)\n    ], bbox_params={'format': 'pascal_voc', 'label_fields': ['labels']})","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"TRAIN_ROOT_PATH = '../input/global-wheat-detection/train'\n\nclass WheatDataset(Dataset):\n\n    def __init__(self, marking, image_ids, transforms=None, test=False):\n        super().__init__()\n\n        self.image_ids = image_ids\n        self.marking = marking\n        self.transforms = transforms\n        self.test = test\n\n    def __getitem__(self, index: int):\n        image_id = self.image_ids[index]\n        \n        if self.test or random.random() > 0.5:\n            image, boxes = self.load_image_and_boxes(index)\n        else:\n            image, boxes = self.load_cutmix_image_and_boxes(index)\n\n        # there is only one class\n        labels = torch.ones((boxes.shape[0],), dtype=torch.int64)\n        \n        target = {}\n        target['boxes'] = boxes\n        target['labels'] = labels\n        target['image_id'] = torch.tensor([index])\n\n        if self.transforms:\n            for i in range(10):\n                sample = self.transforms(**{\n                    'image': image,\n                    'bboxes': target['boxes'],\n                    'labels': labels\n                })\n                if len(sample['bboxes']) > 0:\n                    image = sample['image']\n                    target['boxes'] = torch.stack(tuple(map(torch.tensor, zip(*sample['bboxes'])))).permute(1, 0)\n                    target['boxes'][:,[0,1,2,3]] = target['boxes'][:,[1,0,3,2]]  #yxyx: be warning\n                    break\n\n        return image, target, image_id\n\n    def __len__(self) -> int:\n        return self.image_ids.shape[0]\n\n    def load_image_and_boxes(self, index):\n        image_id = self.image_ids[index]\n        image = cv2.imread(f'{TRAIN_ROOT_PATH}/{image_id}.jpg', cv2.IMREAD_COLOR)\n        image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB).astype(np.float32)\n        image /= 255.0\n        records = self.marking[self.marking['image_id'] == image_id]\n        boxes = records[['x', 'y', 'w', 'h']].values\n        boxes[:, 2] = boxes[:, 0] + boxes[:, 2]\n        boxes[:, 3] = boxes[:, 1] + boxes[:, 3]\n        return image, boxes\n\n    def load_cutmix_image_and_boxes(self, index, imsize=1024):\n        \"\"\" \n        This implementation of cutmix author:  https://www.kaggle.com/nvnnghia \n        Refactoring and adaptation: https://www.kaggle.com/shonenkov\n        \"\"\"\n        w, h = imsize, imsize\n        s = imsize // 2\n    \n        xc, yc = [int(random.uniform(imsize * 0.25, imsize * 0.75)) for _ in range(2)]  # center x, y\n        indexes = [index] + [random.randint(0, self.image_ids.shape[0] - 1) for _ in range(3)]\n\n        result_image = np.full((imsize, imsize, 3), 1, dtype=np.float32)\n        result_boxes = []\n\n        for i, index in enumerate(indexes):\n            image, boxes = self.load_image_and_boxes(index)\n            if i == 0:\n                x1a, y1a, x2a, y2a = max(xc - w, 0), max(yc - h, 0), xc, yc  # xmin, ymin, xmax, ymax (large image)\n                x1b, y1b, x2b, y2b = w - (x2a - x1a), h - (y2a - y1a), w, h  # xmin, ymin, xmax, ymax (small image)\n            elif i == 1:  # top right\n                x1a, y1a, x2a, y2a = xc, max(yc - h, 0), min(xc + w, s * 2), yc\n                x1b, y1b, x2b, y2b = 0, h - (y2a - y1a), min(w, x2a - x1a), h\n            elif i == 2:  # bottom left\n                x1a, y1a, x2a, y2a = max(xc - w, 0), yc, xc, min(s * 2, yc + h)\n                x1b, y1b, x2b, y2b = w - (x2a - x1a), 0, max(xc, w), min(y2a - y1a, h)\n            elif i == 3:  # bottom right\n                x1a, y1a, x2a, y2a = xc, yc, min(xc + w, s * 2), min(s * 2, yc + h)\n                x1b, y1b, x2b, y2b = 0, 0, min(w, x2a - x1a), min(y2a - y1a, h)\n            result_image[y1a:y2a, x1a:x2a] = image[y1b:y2b, x1b:x2b]\n            padw = x1a - x1b\n            padh = y1a - y1b\n\n            boxes[:, 0] += padw\n            boxes[:, 1] += padh\n            boxes[:, 2] += padw\n            boxes[:, 3] += padh\n\n            result_boxes.append(boxes)\n\n        result_boxes = np.concatenate(result_boxes, 0)\n        np.clip(result_boxes[:, 0:], 0, 2 * s, out=result_boxes[:, 0:])\n        result_boxes = result_boxes.astype(np.int32)\n        result_boxes = result_boxes[np.where((result_boxes[:,2]-result_boxes[:,0])*(result_boxes[:,3]-result_boxes[:,1]) > 0)]\n        return result_image, result_boxes","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"fold_number = 0\n\ntrain_dataset = WheatDataset(image_ids=df_folds[df_folds['fold'] != fold_number].index.values, marking=marking, transforms=get_train_transforms(), test=False)\nvalidation_dataset = WheatDataset(image_ids=df_folds[df_folds['fold'] == fold_number].index.values, marking=marking, transforms=get_valid_transforms(), test=True)\n\ndef collate_fn(batch):\n    return tuple(zip(*batch))\n\ntrain_loader = torch.utils.data.DataLoader(\n    train_dataset,\n    batch_size=TrainGlobalConfig.batch_size,\n    sampler=RandomSampler(train_dataset),\n    pin_memory=False,\n    drop_last=True,\n    num_workers=TrainGlobalConfig.num_workers,\n    collate_fn=collate_fn,\n)\nval_loader = torch.utils.data.DataLoader(\n    validation_dataset, \n    batch_size=TrainGlobalConfig.batch_size,\n    num_workers=TrainGlobalConfig.num_workers,\n    shuffle=False,\n    sampler=SequentialSampler(validation_dataset),\n    pin_memory=False,\n    collate_fn=collate_fn,\n)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Train ","metadata":{}},{"cell_type":"code","source":"class Averager:\n    def __init__(self):\n        self.sum = 0\n        self.iterations = 0\n    \n    def send(self, value):\n        self.sum += value\n        self.iterations += 1\n        \n    def value(self):\n        if self.iterations == 0:\n            return 0\n        else:\n            return 1.0 * self.sum / self.iterations\n    \n    def reset(self):\n        self.sum = 0\n        self.iterations = 0","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"device = torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu')","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class Fitter:\n    def __init__(self, model, device, config):\n        self.config = config\n        self.epoch = 0\n        \n        self.base_dir = f'./{config['folder']}'\n        if not os.path.exists(self.base_dir):\n            os.makedirs(self.base_dir)\n        self.log_path = f'{self.base_dir}/log.txt'\n        \n        self.best_loss = 10**5\n        \n        self.model = model\n        self.device = device\n        \n        paramters = list(self.model.named_paramters())\n        no_decay = ['bias', 'LayerNorm.bias', 'LayerNorm.weight']\n        optimizer_grouped_parameters = [\n            {'params': [p for n, p in param_optimizer if not any(nd in n for nd in no_decay)], 'weight_decay': 0.001},\n            {'params': [p for n, p in param_optimizer if any(nd in n for nd in no_decay)], 'weight_decay': 0.0}\n        ] \n        self.optimizer = torch.optim.AdamW(optimizer_grouped_parameters, lr=config.lr)\n        self.scheduler = config[\"SchedulerClass\"](self.optimizer, **config.scheduler_params)\n        self.log(f'Fitter prepared. Device is {self.device}')\n    \n    def fit(self, train_loader, validation_loader):\n        loss_hist = Averager()\n        for e in range(self.config['n_epochs']):\n            loss_hist.reset()\n            if self.config['verbose']:\n                lr = self.optimizer.param_groups[0]['lr']\n                timestamp = datetime.utcnow().isformat()\n                self.log(f'\\n{timestamp}\\nLR: {lr}')\n            # Train\n            t = time.time()\n            summary_loss = self.train_one_epoch(train_loader)\n            self.log(f'[RESULT]: Train. Epoch: {self.epoch}, summary_loss: {summary_loss.avg:.5f}, time: {(time.time() - t):.5f}')\n            self.save(f'{self.base_dir}/last-checkpoint.bin')\n            # Val\n            t = time.time()\n            summary_loss = self.validation(validation_loader)\n            self.log(f'[RESULT]: Val. Epoch: {self.epoch}, summary_loss: {summary_loss.avg:.5f}, time: {(time.time() - t):.5f}')\n            # save best 3 checkpoint\n            if summary_loss.avg < self.best_summary_loss:\n                self.best_summary_loss = summary_loss.avg\n                self.model.eval()\n                self.save(f'{self.best_dir}/best-checkpoint-{str(self.epoch).zfill(3)}epoch.bin')\n                for path in sorted(glob(f'{self.base_dir}/best-checkpoint-*epoch.bin'))[:-3]:\n                    os.remove(path)\n            self.epoch += 1\n    def train_one_epoch(self, train_loader):\n        self.model.train()\n        summary_loss = AverageMeter()\n        t = time.time()\n        for step, (images, targets, image_ids) in enumerate(train_loader):\n            if self.config['verbose']:\n                if step % self.config['verbose_step'] == 0:\n                    print(\n                        f'Train Step {step}/{len(train_loader)},' + \\\n                        f'summray_loss {summary_loss.avg:.5f}},' + \\\n                        f'time: {(time.time() - t):.5f}', end='\\r'\n                    )\n            images = torch.stack(images)\n            images = images.to(self.device).float()\n            batch_size = images.shape[0]\n            boxes = [traget['boxes'].to(self.device).float() for target in targets]\n            labels = [traget['labels'].to(self.device).float() for label in labels]\n            self.optimizer.zero_grad()\n            loss, _, _ = self.model(images, boxes, labels)\n            loss.backward()\n            summary_loss.update(loss.detach().item(), batch_size)\n            self.optimizer.step()\n            if self.config.step_scheduler():\n                self.scheduler.step()\n        return summary_loss\n    def val(self, validation_loader):\n        self.model.val()\n        summary_loss = AverageMeter()\n        t = time.time()\n        for step, (images, labels, image_ids) in enumerate(validation_loader):\n            with torch.no_grad():\n                images = torch.stacks(images)\n                images = images.to(self.device)\n                batch_size = images.shape[0]\n                boxes = [target['boxes'].to(self.device).float() for target in targets]\n                labels = [target['labels'].to(self.device).float() for target in targets]\n                loss, _, _ = self.model(images, boxes, labels)\n                summary_loss.update(loss.detach().item(), batch_size)\n        if self.config['validation_scheduler']:\n            self.scheduler.step(metric=summary_loss.avg)\n    def save(self, path):\n        self.model.eval()\n        torch.save(\n            {\n                'model_state_dict': self.model.model.state_dict(),\n                'optimizer_state_dict': self.optimizer.state_dict(),\n                'scheduler_state_dict': self.scheduler.state_dict(),\n                'best_summary_loss': self.best_summary_loss,\n                'epoch': self.epoch,\n            }, path)\n    def load(self, path):\n        checkpoint = torch.load(path)\n        self.model.model.load_state_dict(checkpoint['model_state_dict'])\n        self.optimizer.load_state_dict(checkpoint['optimizer_state_dict'])\n        self.scheduler.load_state_dict(checkpoint['scheduler_state_dict'])\n        self.best_summary_loss = checkpoint['best_summary_loss']\n        self.epoch = checkpoint['epoch'] + 1\n    def log(self, message):\n        if self.verbose:\n            print(message)\n        with open(self.log_path, 'a+') as logger:\n            logger.write(f'{message}\\n')","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"\nfrom effdet import get_efficientdet_config, EfficientDet, DetBenchTrain\nfrom effdet.efficientdet import HeadNet\ndef get_net():\n    config = get_efficientdet_config('tf_efficientdet_d5')\n    net = EfficientDet(config, pretrained_backbone=False)\n    checkpoint = torch.load('../input/efficientdet/efficientdet_d5-ef44aea8.pth')\n    net.load_state_dict(checkpoint)\n    config.num_class = 1\n    config.image_size = 512\n    net.class_net = HeadNet(config, num_outputs=config.num_classes, norm_kwargs = dict(eps=.001, momentum=.01))\n    return DetBenchTrain(net, config)\n","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def training():\n    net = get_net()\n    device = torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu')\n    net.to(device)\n    fitter = Fitter(model=net, device=device, config=TrainGlobalConfig)\n    fitter.fit(train_loader, val_loader)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"training()","metadata":{},"execution_count":null,"outputs":[]}]}