{"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":"markdown","source":"# YoLoV4 Demo\n**This is a demo sample for yolov4(https://github.com/WongKinYiu/PyTorch_YOLOv4).**\n\n**Before using this demo, you may need to prepare your environment following commands below:**  \n**1. put source code into your input;**  \n**2. pip install all the required packages under source code path**  \n```\npip install -r requirements.txt\n```\n\n**Please note, in this demo I use yoloV4-pacsp model structure since this structure does not require mish funcion(<font color=green>in kaggle environment, you may need to install third-party pacakge for using mish funtion, which is proved unavailabel in my environment</font>)**\n\n**For using your dataset, you need to modify related cfg file. <font color=green>Take yolov4-pacsp.cfg as an example</font>, your class number is 3, so you need to <font color=green>1) change class number in yolo layers into 3</font> and <font color=green>2)change filters (the one just before the yolo layer) into 24 = (class number + 5) * 3</font>.**\n\n**Also I do not use any augment methods in this demo, so you may need to modify somthing functions(e.g.load_mosaic and load_image) by yourself.**","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19"}},{"cell_type":"code","source":"import argparse\nimport math\nimport os,pdb,sys\nimport random\nimport time\nimport matplotlib.pyplot as plt\nfrom pathlib import Path\nif '/kaggle/input/yolo-detection' not in sys.path:\n    sys.path.append('/kaggle/input/yolo-detection')\n\nimport numpy as np\nimport torch.distributed as dist\nimport torch.nn.functional as F\nimport torch.optim as optim\nimport torch.optim.lr_scheduler as lr_scheduler\nimport torch.utils.data\nimport yaml\nfrom torch.cuda import amp\nfrom torch.nn.parallel import DistributedDataParallel as DDP\nfrom torch.utils.tensorboard import SummaryWriter\nfrom tqdm import tqdm\n\nimport test  # import test.py to get mAP after each epoch\nfrom models.models import *\nfrom utils.datasets import create_dataloader\nfrom utils.general import (\n    check_img_size, torch_distributed_zero_first, labels_to_class_weights, plot_labels, check_anchors,\n    labels_to_image_weights, compute_loss, plot_images, fitness, strip_optimizer, plot_results,\n    get_latest_run, check_git_status, check_file, increment_dir, print_mutation, plot_evolution)\nfrom utils.google_utils import attempt_download\nfrom utils.torch_utils import init_seeds, ModelEMA, select_device, intersect_dicts","metadata":{"execution":{"iopub.status.busy":"2021-06-06T06:32:47.047880Z","iopub.execute_input":"2021-06-06T06:32:47.048347Z","iopub.status.idle":"2021-06-06T06:32:50.221475Z","shell.execute_reply.started":"2021-06-06T06:32:47.048200Z","shell.execute_reply":"2021-06-06T06:32:50.220532Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 1.Prepare Config","metadata":{}},{"cell_type":"code","source":"class Option():\n    def __init__(self, file_dir):\n        super(Option, self).__init__()\n        self.local_rank = 0\n        self.data = os.path.join(file_dir, 'data/coco.yaml')\n        self.cfg = os.path.join(file_dir, 'cfg/yolov4-pacsp.cfg')\n        self.hyp = os.path.join(file_dir, 'data/hyp.scratch.yaml')\n        self.img_size = [640, 640]\n        self.world_size = 1\n        self.global_rank = -1\n        self.epochs = 2\n        self.batch_size = 16\n        self.total_batch_size = 16","metadata":{"execution":{"iopub.status.busy":"2021-06-06T06:32:54.945091Z","iopub.execute_input":"2021-06-06T06:32:54.945470Z","iopub.status.idle":"2021-06-06T06:32:54.953244Z","shell.execute_reply.started":"2021-06-06T06:32:54.945437Z","shell.execute_reply":"2021-06-06T06:32:54.951809Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"source_code_dir = '/kaggle/input/yolo-detection'\nopt = Option(source_code_dir)\ndevice = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')\ncuda = device.type != 'cpu'\nepochs, batch_size, total_batch_size, rank = \\\n        opt.epochs, opt.batch_size, opt.total_batch_size, opt.global_rank\n\nwith open(opt.hyp) as f:\n    hyp = yaml.load(f, Loader=yaml.FullLoader)  # load hyps\nwith open(opt.data) as f:\n    data_dict = yaml.load(f, Loader=yaml.FullLoader)  # model dict\n    \ntrain_image_dir = '/kaggle/input/golfdetection/train_images/train_images'\ntrain_label_dir = '/kaggle/input/golfdetection/train_labels/train_labels'\nnc = 3","metadata":{"execution":{"iopub.status.busy":"2021-06-06T06:32:58.165246Z","iopub.execute_input":"2021-06-06T06:32:58.165650Z","iopub.status.idle":"2021-06-06T06:32:58.279571Z","shell.execute_reply.started":"2021-06-06T06:32:58.165616Z","shell.execute_reply":"2021-06-06T06:32:58.278590Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 2.Prepare Dataloader","metadata":{}},{"cell_type":"code","source":"from utils.datasets import *\nclass LoadImagesAndLabelsBoolart(Dataset):  # for training/testing\n    def __init__(self, img_dir, label_dir, img_size=640, batch_size=16, augment=False, hyp=None, rect=False, image_weights=False,cache_images=False, single_cls=False, stride=32, pad=0.0):\n        self.img_files = []\n        img_files = os.listdir(img_dir)\n        for img_file in img_files:\n            self.img_files.append(os.path.join(img_dir, img_file))\n            \n        n = len(self.img_files)\n        bi = np.floor(np.arange(n) / batch_size).astype(np.int)  # batch index\n        nb = bi[-1] + 1  # number of batches\n\n        self.n = n  # number of images\n        self.batch = bi  # batch index of image\n        self.img_size = img_size\n        self.augment = augment\n        self.hyp = hyp\n        self.image_weights = image_weights\n        self.rect = False if image_weights else rect\n        self.mosaic = self.augment and not self.rect  # load 4 images at a time into a mosaic (only during training)\n        self.mosaic_border = [-img_size // 2, -img_size // 2]\n        self.stride = stride\n\n        # Define labels\n        self.label_files = [x.replace('images', 'labels').replace('image', 'label').replace(os.path.splitext(x)[-1], '.txt') for x in\n                            self.img_files]\n\n        # Check cache\n        cache_path = str(Path(self.label_files[0]).parent) + '.cache'  # cached labels\n        if os.path.isfile(cache_path):\n            cache = torch.load(cache_path)  # load\n            if cache['hash'] != get_hash(self.label_files + self.img_files):  # dataset changed\n                cache = self.cache_labels(cache_path)  # re-cache\n        else:\n            cache = self.cache_labels(cache_path)  # cache\n\n        # Get labels\n        labels, shapes = zip(*[cache[x] for x in self.img_files])\n        self.shapes = np.array(shapes, dtype=np.float64)\n        self.labels = list(labels)\n\n        # Rectangular Training  https://github.com/ultralytics/yolov3/issues/232\n        if self.rect:\n            # Sort by aspect ratio\n            s = self.shapes  # wh\n            ar = s[:, 1] / s[:, 0]  # aspect ratio\n            irect = ar.argsort()\n            self.img_files = [self.img_files[i] for i in irect]\n            self.label_files = [self.label_files[i] for i in irect]\n            self.labels = [self.labels[i] for i in irect]\n            self.shapes = s[irect]  # wh\n            ar = ar[irect]\n\n            # Set training image shapes\n            shapes = [[1, 1]] * nb\n            for i in range(nb):\n                ari = ar[bi == i]\n                mini, maxi = ari.min(), ari.max()\n                if maxi < 1:\n                    shapes[i] = [maxi, 1]\n                elif mini > 1:\n                    shapes[i] = [1, 1 / mini]\n\n            self.batch_shapes = np.ceil(np.array(shapes) * img_size / stride + pad).astype(np.int) * stride\n\n        # Cache labels\n        create_datasubset, extract_bounding_boxes, labels_loaded = False, False, False\n        nm, nf, ne, ns, nd = 0, 0, 0, 0, 0  # number missing, found, empty, datasubset, duplicate\n        pbar = tqdm(self.label_files)\n        for i, file in enumerate(pbar):\n            l = self.labels[i]  # label\n            if l.shape[0]:\n                assert l.shape[1] == 5, '> 5 label columns: %s' % file\n                assert (l >= 0).all(), 'negative labels: %s' % file\n                assert (l[:, 1:] <= 1).all(), 'non-normalized or out of bounds coordinate labels: %s' % file\n                if np.unique(l, axis=0).shape[0] < l.shape[0]:  # duplicate rows\n                    nd += 1  # print('WARNING: duplicate rows in %s' % self.label_files[i])  # duplicate rows\n                if single_cls:\n                    l[:, 0] = 0  # force dataset into single-class mode\n                self.labels[i] = l\n                nf += 1  # file found\n\n                # Create subdataset (a smaller dataset)\n                if create_datasubset and ns < 1E4:\n                    if ns == 0:\n                        create_folder(path='./datasubset')\n                        os.makedirs('./datasubset/images')\n                    exclude_classes = 43\n                    if exclude_classes not in l[:, 0]:\n                        ns += 1\n                        # shutil.copy(src=self.img_files[i], dst='./datasubset/images/')  # copy image\n                        with open('./datasubset/images.txt', 'a') as f:\n                            f.write(self.img_files[i] + '\\n')\n\n                # Extract object detection boxes for a second stage classifier\n                if extract_bounding_boxes:\n                    p = Path(self.img_files[i])\n                    img = cv2.imread(str(p))\n                    h, w = img.shape[:2]\n                    for j, x in enumerate(l):\n                        f = '%s%sclassifier%s%g_%g_%s' % (p.parent.parent, os.sep, os.sep, x[0], j, p.name)\n                        if not os.path.exists(Path(f).parent):\n                            os.makedirs(Path(f).parent)  # make new output folder\n\n                        b = x[1:] * [w, h, w, h]  # box\n                        b[2:] = b[2:].max()  # rectangle to square\n                        b[2:] = b[2:] * 1.3 + 30  # pad\n                        b = xywh2xyxy(b.reshape(-1, 4)).ravel().astype(np.int)\n\n                        b[[0, 2]] = np.clip(b[[0, 2]], 0, w)  # clip boxes outside of image\n                        b[[1, 3]] = np.clip(b[[1, 3]], 0, h)\n                        assert cv2.imwrite(f, img[b[1]:b[3], b[0]:b[2]]), 'Failure extracting classifier boxes'\n            else:\n                ne += 1  # print('empty labels for image %s' % self.img_files[i])  # file empty\n                # os.system(\"rm '%s' '%s'\" % (self.img_files[i], self.label_files[i]))  # remove\n\n            pbar.desc = 'Scanning labels %s (%g found, %g missing, %g empty, %g duplicate, for %g images)' % (\n                cache_path, nf, nm, ne, nd, n)\n        if nf == 0:\n            s = 'WARNING: No labels found in %s. See %s' % (os.path.dirname(file) + os.sep, help_url)\n            print(s)\n            assert not augment, '%s. Can not train without labels.' % s\n\n        # Cache images into memory for faster training (WARNING: large datasets may exceed system RAM)\n        self.imgs = [None] * n\n        if cache_images:\n            gb = 0  # Gigabytes of cached images\n            pbar = tqdm(range(len(self.img_files)), desc='Caching images')\n            self.img_hw0, self.img_hw = [None] * n, [None] * n\n            for i in pbar:  # max 10k images\n                self.imgs[i], self.img_hw0[i], self.img_hw[i] = load_image(self, i)  # img, hw_original, hw_resized\n                gb += self.imgs[i].nbytes\n                pbar.desc = 'Caching images (%.1fGB)' % (gb / 1E9)\n\n    def cache_labels(self, path='labels.cache'):\n        # Cache dataset labels, check images and read shapes\n        x = {}  # dict\n        pbar = tqdm(zip(self.img_files, self.label_files), desc='Scanning images', total=len(self.img_files))\n        for (img, label) in pbar:\n            try:\n                l = []\n                image = Image.open(img)\n                image.verify()  # PIL verify\n                # _ = io.imread(img)  # skimage verify (from skimage import io)\n                shape = exif_size(image)  # image size\n                assert (shape[0] > 9) & (shape[1] > 9), 'image size <10 pixels'\n                if os.path.isfile(label):\n                    with open(label, 'r') as f:\n                        l = np.array([x.split() for x in f.read().splitlines()], dtype=np.float32)  # labels\n                if len(l) == 0:\n                    l = np.zeros((0, 5), dtype=np.float32)\n                x[img] = [l, shape]\n            except Exception as e:\n                x[img] = None\n                print('WARNING: %s: %s' % (img, e))\n\n        x['hash'] = get_hash(self.label_files + self.img_files)\n        return x\n\n    def __len__(self):\n        return len(self.img_files)\n\n    def __getitem__(self, index):\n        if self.image_weights:\n            index = self.indices[index]\n        \n        hyp = self.hyp\n        if self.mosaic:\n            # Load mosaic\n            img, labels = load_mosaic(self, index)\n            shapes = None\n\n            # MixUp https://arxiv.org/pdf/1710.09412.pdf\n            if random.random() < hyp['mixup']:\n                img2, labels2 = load_mosaic(self, random.randint(0, len(self.labels) - 1))\n                r = np.random.beta(8.0, 8.0)  # mixup ratio, alpha=beta=8.0\n                img = (img * r + img2 * (1 - r)).astype(np.uint8)\n                labels = np.concatenate((labels, labels2), 0)\n\n        else:\n            # Load image\n            img, (h0, w0), (h, w) = load_image(self, index)\n\n            # Letterbox\n            shape = self.batch_shapes[self.batch[index]] if self.rect else self.img_size  # final letterboxed shape\n            img, ratio, pad = letterbox(img, shape, auto=False, scaleup=self.augment)\n            shapes = (h0, w0), ((h / h0, w / w0), pad)  # for COCO mAP rescaling\n\n            # Load labels\n            labels = []\n#             x = self.labels[index]\n            \n            # delete wrong labels\n            x = []\n            x_temp = self.labels[index]\n            for idx, ele in enumerate(x_temp):\n                if ele[3] == 0 or ele[4] == 0: continue\n                x.append(ele)\n            x = np.array(x)\n                    \n            if x.size > 0:\n                # Normalized xywh to pixel xyxy format\n                labels = x.copy()\n                labels[:, 1] = ratio[0] * w * (x[:, 1] - x[:, 3] / 2) + pad[0]  # pad width\n                labels[:, 2] = ratio[1] * h * (x[:, 2] - x[:, 4] / 2) + pad[1]  # pad height\n                labels[:, 3] = ratio[0] * w * (x[:, 1] + x[:, 3] / 2) + pad[0]\n                labels[:, 4] = ratio[1] * h * (x[:, 2] + x[:, 4] / 2) + pad[1]\n\n        if self.augment:\n            # Augment imagespace\n            if not self.mosaic:\n                img, labels = random_perspective(img, labels,\n                                                 degrees=hyp['degrees'],\n                                                 translate=hyp['translate'],\n                                                 scale=hyp['scale'],\n                                                 shear=hyp['shear'],\n                                                 perspective=hyp['perspective'])\n\n            # Augment colorspace\n            augment_hsv(img, hgain=hyp['hsv_h'], sgain=hyp['hsv_s'], vgain=hyp['hsv_v'])\n\n        nL = len(labels)  # number of labels\n        if nL:\n            labels[:, 1:5] = xyxy2xywh(labels[:, 1:5])  # convert xyxy to xywh\n            labels[:, [2, 4]] /= img.shape[0]  # normalized height 0-1\n            labels[:, [1, 3]] /= img.shape[1]  # normalized width 0-1\n        \n        if self.augment:\n            # flip up-down\n            if random.random() < hyp['flipud']:\n                img = np.flipud(img)\n                if nL:\n                    labels[:, 2] = 1 - labels[:, 2]\n\n            # flip left-right\n            if random.random() < hyp['fliplr']:\n                img = np.fliplr(img)\n                if nL:\n                    labels[:, 1] = 1 - labels[:, 1]\n\n        labels_out = torch.zeros((nL, 6))\n        if nL:\n            labels_out[:, 1:] = torch.from_numpy(labels)\n        \n        # Convert\n        img = img[:, :, ::-1].transpose(2, 0, 1)  # BGR to RGB, to 3x416x416\n        img = np.ascontiguousarray(img)\n        \n        return torch.from_numpy(img), labels_out, self.img_files[index], shapes\n\n    @staticmethod\n    def collate_fn(batch):\n        img, label, path, shapes = zip(*batch)  # transposed\n        for i, l in enumerate(label):\n            l[:, 0] = i  # add target image index for build_targets()\n        return torch.stack(img, 0), torch.cat(label, 0), path, shapes","metadata":{"execution":{"iopub.status.busy":"2021-06-06T06:33:01.861883Z","iopub.execute_input":"2021-06-06T06:33:01.862254Z","iopub.status.idle":"2021-06-06T06:33:01.927395Z","shell.execute_reply.started":"2021-06-06T06:33:01.862223Z","shell.execute_reply":"2021-06-06T06:33:01.926183Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Image sizes\ngs = 32 # grid size (max stride)\nimgsz, imgsz_test = [check_img_size(x, gs) for x in opt.img_size]  # verify imgsz are gs-multiples\n\ndataset = LoadImagesAndLabelsBoolart(train_image_dir, train_label_dir, imgsz, batch_size,augment=False,  # augment images\n                                      hyp=hyp,  # augmentation hyperparameters\n                                      rect=False,  # rectangular training\n                                      cache_images=False,\n                                      single_cls=False,\n                                      stride=int(gs))","metadata":{"execution":{"iopub.status.busy":"2021-06-06T06:33:07.311196Z","iopub.execute_input":"2021-06-06T06:33:07.311573Z","iopub.status.idle":"2021-06-06T06:37:05.474554Z","shell.execute_reply.started":"2021-06-06T06:33:07.311540Z","shell.execute_reply":"2021-06-06T06:37:05.473552Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"batch_size = min(batch_size, len(dataset))\nnw = 4\ndataloader = torch.utils.data.DataLoader(dataset,\n                                             batch_size=batch_size,\n                                             num_workers=nw,\n                                             pin_memory=True,\n                                             collate_fn=LoadImagesAndLabelsBoolart.collate_fn)\nmlc = np.concatenate(dataset.labels, 0)[:, 0].max()\nnb = len(dataloader)","metadata":{"execution":{"iopub.status.busy":"2021-06-06T06:37:12.533403Z","iopub.execute_input":"2021-06-06T06:37:12.533819Z","iopub.status.idle":"2021-06-06T06:37:12.550216Z","shell.execute_reply.started":"2021-06-06T06:37:12.533785Z","shell.execute_reply":"2021-06-06T06:37:12.548995Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 3.Prepare Model and Optimizer","metadata":{}},{"cell_type":"code","source":"# Create Model\nmodel = Darknet(opt.cfg).to(device) \nema = ModelEMA(model) if rank in [-1, 0] else None\n\n# Optimizer\nnbs = 64  # nominal batch size\naccumulate = max(round(nbs / total_batch_size), 1)  # accumulate loss before optimizing\nhyp['weight_decay'] *= total_batch_size * accumulate / nbs  # scale weight_decay\n\npg0, pg1, pg2 = [], [], []  # optimizer parameter groups\nfor k, v in dict(model.named_parameters()).items():\n    if '.bias' in k:\n        pg2.append(v)  # biases\n    elif 'Conv2d.weight' in k:\n        pg1.append(v)  # apply weight_decay\n    else:\n        pg0.append(v)  # all else\n\noptimizer = optim.SGD(pg0, lr=hyp['lr0'], momentum=hyp['momentum'], nesterov=True)\noptimizer.add_param_group({'params': pg1, 'weight_decay': hyp['weight_decay']})  # add pg1 with weight_decay\noptimizer.add_param_group({'params': pg2})  # add pg2 (biases)\nprint('Optimizer groups: %g .bias, %g conv.weight, %g other' % (len(pg2), len(pg1), len(pg0)))\ndel pg0, pg1, pg2\n\n# Scheduler\nlf = lambda x: (((1 + math.cos(x * math.pi / epochs)) / 2) ** 1.0) * 0.8 + 0.2  # cosine\nscheduler = lr_scheduler.LambdaLR(optimizer, lr_lambda=lf)","metadata":{"execution":{"iopub.status.busy":"2021-06-06T06:37:24.839996Z","iopub.execute_input":"2021-06-06T06:37:24.840413Z","iopub.status.idle":"2021-06-06T06:37:30.152498Z","shell.execute_reply.started":"2021-06-06T06:37:24.840381Z","shell.execute_reply":"2021-06-06T06:37:30.151137Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"start_epoch, best_fitness = 0, 0.0\n# Model parameters\nhyp['cls'] *= nc / 80.  # scale coco-tuned hyp['cls'] to current dataset\nmodel.nc = nc  # attach number of classes to model\nmodel.hyp = hyp  # attach hyperparameters to model\nmodel.gr = 1.0  # giou loss ratio (obj_loss = 1.0 or giou)\nmodel.class_weights = labels_to_class_weights(dataset.labels, nc).to(device)  # attach class weights\nscheduler.last_epoch = start_epoch - 1  # do not move\nscaler = amp.GradScaler(enabled=cuda)","metadata":{"execution":{"iopub.status.busy":"2021-06-06T06:37:33.846949Z","iopub.execute_input":"2021-06-06T06:37:33.847324Z","iopub.status.idle":"2021-06-06T06:37:33.863145Z","shell.execute_reply.started":"2021-06-06T06:37:33.847292Z","shell.execute_reply":"2021-06-06T06:37:33.862039Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 4.Training","metadata":{}},{"cell_type":"code","source":"results = (0, 0, 0, 0, 0, 0, 0)\nfor epoch in range(start_epoch, epochs):  # epoch ------------------------------------------------------------------\n    model.train()\n    mloss = torch.zeros(4, device=device)  # mean losses\n    pbar = enumerate(dataloader)\n    optimizer.zero_grad()\n    print(('\\n' + '%10s' * 8) % ('Epoch', 'gpu_mem', 'GIoU', 'obj', 'cls', 'total', 'targets', 'img_size'))\n    pbar = tqdm(pbar, total=nb)  # progress bar\n        \n    for i, (imgs, targets, paths, _) in pbar:\n        ni = i + nb * epoch\n        imgs = imgs.to(device, non_blocking=True).float() / 255.0\n        \n        with amp.autocast(enabled=cuda):\n#             pdb.set_trace()\n            pred = model(imgs)\n            loss, loss_items = compute_loss(pred, targets.to(device), model)  # scaled by batch_size\n            if rank != -1:\n                loss *= opt.world_size\n        scaler.scale(loss).backward()\n        \n        # Optimize\n        if ni % accumulate == 0:\n            scaler.step(optimizer)  # optimizer.step\n            scaler.update()\n            optimizer.zero_grad()\n            if ema is not None:\n                ema.update(model)\n        \n        # Print\n        if rank in [-1, 0]:\n            mloss = (mloss * i + loss_items) / (i + 1)  # update mean losses\n            mem = '%.3gG' % (torch.cuda.memory_reserved() / 1E9 if torch.cuda.is_available() else 0)  # (GB)\n            s = ('%10s' * 2 + '%10.4g' * 6) % (\n                '%g/%g' % (epoch, epochs - 1), mem, *mloss, targets.shape[0], imgs.shape[-1])\n            pbar.set_description(s)\n            \n    scheduler.step()\n    \n    # Update best mAP\n    fi = fitness(np.array(results).reshape(1, -1))  # fitness_i = weighted combination of [P, R, mAP, F1]\n    if fi > best_fitness:\n        best_fitness = fi\n\n    # Save model\n    save_dir = '/kaggle/working/yolo4_checkpoint'\n    if not os.path.exists(save_dir):\n        os.makedirs(save_dir)\n    save_file = os.path.join(save_dir, str(epoch) + '_yolo.pt')\n    best_file = os.path.join(save_dir, 'best_yolo.pt')\n\n    ckpt = {'epoch': epoch,\n            'best_fitness': best_fitness,\n            'model': ema.ema.module.state_dict() if hasattr(ema, 'module') else ema.ema.state_dict()}\n\n    # Save last, best and delete\n    torch.save(ckpt, save_file)\n    if (best_fitness == fi):\n        torch.save(ckpt, best_file)\n    del ckpt","metadata":{"execution":{"iopub.status.busy":"2021-06-06T06:37:37.602369Z","iopub.execute_input":"2021-06-06T06:37:37.602868Z","iopub.status.idle":"2021-06-06T07:08:14.947213Z","shell.execute_reply.started":"2021-06-06T06:37:37.602827Z","shell.execute_reply":"2021-06-06T07:08:14.945655Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 5.Evaluation","metadata":{}},{"cell_type":"code","source":"from utils.general import (\n    coco80_to_coco91_class, check_file, check_img_size, compute_loss, non_max_suppression,\n    scale_coords, xyxy2xywh, clip_coords, plot_images, xywh2xyxy, box_iou, output_to_target, ap_per_class)\n\n# dataset\ntest_dataset = LoadImagesAndLabelsBoolart(train_image_dir, train_label_dir, imgsz, batch_size, augment=False,  # augment images\n                                      hyp=None,  # augmentation hyperparameters\n                                      rect=True,  # rectangular training\n                                      cache_images=False,\n                                      single_cls=False,\n                                      pad=0.5,\n                                      stride=int(32))","metadata":{"execution":{"iopub.status.busy":"2021-06-06T07:08:25.606251Z","iopub.execute_input":"2021-06-06T07:08:25.606656Z","iopub.status.idle":"2021-06-06T07:08:53.159911Z","shell.execute_reply.started":"2021-06-06T07:08:25.606623Z","shell.execute_reply":"2021-06-06T07:08:53.158949Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"'''\nTest one sample of dataset, the result will be saved in /kaggle/working/yolo4_checkpoint\n'''\ndef test(model, test_dataset):\n    conf_thres=0.02\n    iou_thres=0.6\n    iouv = torch.linspace(0.5, 0.95, 10).to(device)  # iou vector for mAP@0.5:0.95\n    niou = iouv.numel()\n    s = ('%20s' + '%12s' * 6) % ('Class', 'Images', 'Targets', 'P', 'R', 'mAP@.5', 'mAP@.5:.95')\n    batch_size = 1\n    nw = 1\n    test_dataloader = torch.utils.data.DataLoader(test_dataset,\n                                                 batch_size=batch_size,\n                                                 num_workers=nw,\n                                                 pin_memory=True,\n                                                 collate_fn=LoadImagesAndLabelsBoolart.collate_fn)\n\n    # model \n    half = device.type != 'cpu'  # half precision only supported on CUDA\n    if half:\n        model.half()\n    model.eval()\n\n    # evaluation\n    jdict, stats, ap, ap_class = [], [], [], []\n    for batch_i, (img, targets, paths, shapes) in enumerate(tqdm(test_dataloader, desc=s)):\n        img = img.to(device, non_blocking=True)\n        img = img.half() if half else img.float()  # uint8 to fp16/32\n        img /= 255.0  # 0 - 255 to 0.0 - 1.0\n        targets = targets.to(device)\n        nb, _, height, width = img.shape  # batch size, channels, height, width\n        whwh = torch.Tensor([width, height, width, height]).to(device)\n\n        with torch.no_grad():\n            inf_out, train_out = model(img, augment=False)  # inference and training outputs \n            output = non_max_suppression(inf_out, conf_thres=conf_thres, iou_thres=iou_thres, merge=False)\n            \n            \n            \n            max_subplots = 16\n            max_size=640\n            for si, pred in enumerate(output):\n                labels = targets[targets[:, 0] == si, 1:]\n                nl = len(labels)\n                tcls = labels[:, 0].tolist() if nl else []  # target class\n\n                if pred is None:\n                    if nl:\n                        stats.append((torch.zeros(0, niou, dtype=torch.bool), torch.Tensor(), torch.Tensor(), tcls))\n                    continue\n\n                # Clip boxes to image bounds\n                clip_coords(pred, (height, width))\n                \n                image_id = Path(paths[si]).stem\n                box = pred[:, :4].clone()  # xyxy\n                scale_coords(img[si].shape[1:], box, shapes[si][0], shapes[si][1])  # to original shape\n                box = xyxy2xywh(box)  # xywh\n                box[:, :2] -= box[:, 2:] / 2  # xy center to top-left corner\n                correct = torch.zeros(pred.shape[0], niou, dtype=torch.bool, device=device)\n                \n                names = ['1', '2', '3']\n                f = Path(save_dir) / ('test_batch%g_gt.jpg' % batch_i)  # filename\n                plot_images(img, targets, paths, str(f), names)  # ground truth\n                f = Path(save_dir) / ('test_batch%g_pred.jpg' % batch_i)\n                plot_images(img, output_to_target(output, width, height), paths, str(f), names)  # predictions\n                break\n        break             \ntest(model, test_dataset)","metadata":{"execution":{"iopub.status.busy":"2021-06-06T07:08:55.828424Z","iopub.execute_input":"2021-06-06T07:08:55.828823Z","iopub.status.idle":"2021-06-06T07:08:56.174870Z","shell.execute_reply.started":"2021-06-06T07:08:55.828780Z","shell.execute_reply":"2021-06-06T07:08:56.173376Z"},"trusted":true},"execution_count":null,"outputs":[]}]}