{"cells":[{"metadata":{"trusted":true},"cell_type":"code","source":"from __future__ import division\nfrom terminaltables import AsciiTable\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n\nimport matplotlib.pyplot as plt\nimport matplotlib.patches as patches\n\nimport tqdm\nimport math\nimport glob\nimport random\nimport os\nimport sys\nimport time\nimport datetime\nimport argparse\nfrom PIL import Image\n\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom torch.autograd import Variable\nfrom torch.utils.data import Dataset\nfrom torch.utils.data import DataLoader\nfrom torchvision import datasets\nfrom torchvision import transforms\nfrom torch.autograd import Variable\nimport torch.optim as optim","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"epochs = 1\nbatch_size = 32\ngradient_accumulations = 2\nmodel_def = \"/kaggle/input/custom-files/yolov3-Wheat-tiny.cfg\"\ndata_config_str = \"/kaggle/input/custom-files/Wheat_online.data\"\n#pretrained_weights = \"/kaggle/input/custom-files/yolov3-tiny.weights\"\npretrained_weights = \"/kaggle/input/custom-files/yolov3_ckpt_final.pth\"\nimg_size = 416\noptimizer_str = \"Adam\"","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true,"_kg_hide-input":false},"cell_type":"code","source":"def pad_to_square(img, pad_value):\n    c, h, w = img.shape\n    dim_diff = np.abs(h - w)\n    # (upper / left) padding and (lower / right) padding\n    pad1, pad2 = dim_diff // 2, dim_diff - dim_diff // 2\n    # Determine padding\n    pad = (0, 0, pad1, pad2) if h <= w else (pad1, pad2, 0, 0)\n    # Add padding\n    img = F.pad(img, pad, \"constant\", value=pad_value)\n\n    return img, pad\n\n\ndef resize(image, size):\n    image = F.interpolate(image.unsqueeze(0), size=size, mode=\"nearest\").squeeze(0)\n    return image\n\n\ndef random_resize(images, min_size=288, max_size=448):\n    new_size = random.sample(list(range(min_size, max_size + 1, 32)), 1)[0]\n    images = F.interpolate(images, size=new_size, mode=\"nearest\")\n    return images\n\n\nclass ImageFolder(Dataset):\n    def __init__(self, folder_path, img_size=416):\n        self.files = sorted(glob.glob(\"%s/*.*\" % folder_path))\n        self.img_size = img_size\n\n    def __getitem__(self, index):\n        img_path = self.files[index % len(self.files)]\n        # Extract image as PyTorch tensor\n        img = transforms.ToTensor()(Image.open(img_path))\n        # Pad to square resolution\n        img, _ = pad_to_square(img, 0)\n        # Resize\n        img = resize(img, self.img_size)\n\n        return img_path, img\n\n    def __len__(self):\n        return len(self.files)\n\n\nclass ListDataset(Dataset):\n    def __init__(self, list_path, img_size=416, multiscale=True, normalized_labels=True):\n        with open(list_path, \"r\") as file:\n            self.img_files = file.readlines()\n\n        self.label_files = [\n            path.replace(\"images\", \"labels\").replace(\".png\", \".txt\").replace(\".jpg\", \".txt\")\n            for path in self.img_files\n        ]\n        self.img_size = img_size\n        self.max_objects = 100\n        self.multiscale = multiscale\n        self.normalized_labels = normalized_labels\n        self.min_size = self.img_size - 3 * 32\n        self.max_size = self.img_size + 3 * 32\n        self.batch_count = 0\n\n    def __getitem__(self, index):\n\n        # ---------\n        #  Image\n        # ---------\n\n        img_path = self.img_files[index % len(self.img_files)].rstrip()\n\n        # Extract image as PyTorch tensor\n        img = transforms.ToTensor()(Image.open(img_path).convert('RGB'))\n\n        # Handle images with less than three channels\n        if len(img.shape) != 3:\n            img = img.unsqueeze(0)\n            img = img.expand((3, img.shape[1:]))\n\n        _, h, w = img.shape\n        h_factor, w_factor = (h, w) if self.normalized_labels else (1, 1)\n        # Pad to square resolution\n        img, pad = pad_to_square(img, 0)\n        _, padded_h, padded_w = img.shape\n\n        # ---------\n        #  Label\n        # ---------\n\n        label_path = self.label_files[index % len(self.img_files)].rstrip()\n\n        targets = None\n        if os.path.exists(label_path):\n            boxes = torch.from_numpy(np.loadtxt(label_path).reshape(-1, 5))\n            # Extract coordinates for unpadded + unscaled image\n            x1 = w_factor * (boxes[:, 1] - boxes[:, 3] / 2)\n            y1 = h_factor * (boxes[:, 2] - boxes[:, 4] / 2)\n            x2 = w_factor * (boxes[:, 1] + boxes[:, 3] / 2)\n            y2 = h_factor * (boxes[:, 2] + boxes[:, 4] / 2)\n            # Adjust for added padding\n            x1 += pad[0]\n            y1 += pad[2]\n            x2 += pad[1]\n            y2 += pad[3]\n            # Returns (x, y, w, h)\n            boxes[:, 1] = ((x1 + x2) / 2) / padded_w\n            boxes[:, 2] = ((y1 + y2) / 2) / padded_h\n            boxes[:, 3] *= w_factor / padded_w\n            boxes[:, 4] *= h_factor / padded_h\n\n            targets = torch.zeros((len(boxes), 6))\n            targets[:, 1:] = boxes\n\n        return img_path, img, targets\n\n    def collate_fn(self, batch):\n        paths, imgs, targets = list(zip(*batch))\n        # Remove empty placeholder targets\n        targets = [boxes for boxes in targets if boxes is not None]\n        # Add sample index to targets\n        for i, boxes in enumerate(targets):\n            boxes[:, 0] = i\n        targets = torch.cat(targets, 0)\n        # Selects new image size every tenth batch\n        if self.multiscale and self.batch_count % 10 == 0:\n            self.img_size = random.choice(range(self.min_size, self.max_size + 1, 32))\n        # Resize images to input shape\n        imgs = torch.stack([resize(img, self.img_size) for img in imgs])\n        self.batch_count += 1\n        return paths, imgs, targets\n\n    def __len__(self):\n        return len(self.img_files)\ndef to_cpu(tensor):\n    return tensor.detach().cpu()\n\n\ndef load_classes(path):\n    \"\"\"\n    Loads class labels at 'path'\n    \"\"\"\n    fp = open(path, \"r\")\n    names = fp.read().split(\"\\n\")[:-1]\n    return names\n\n\ndef weights_init_normal(m):\n    classname = m.__class__.__name__\n    if classname.find(\"Conv\") != -1:\n        torch.nn.init.normal_(m.weight.data, 0.0, 0.02)\n    elif classname.find(\"BatchNorm2d\") != -1:\n        torch.nn.init.normal_(m.weight.data, 1.0, 0.02)\n        torch.nn.init.constant_(m.bias.data, 0.0)\n\n\ndef rescale_boxes(boxes, current_dim, original_shape):\n    \"\"\" Rescales bounding boxes to the original shape \"\"\"\n    orig_h, orig_w = original_shape\n    # The amount of padding that was added\n    pad_x = max(orig_h - orig_w, 0) * (current_dim / max(original_shape))\n    pad_y = max(orig_w - orig_h, 0) * (current_dim / max(original_shape))\n    # Image height and width after padding is removed\n    unpad_h = current_dim - pad_y\n    unpad_w = current_dim - pad_x\n    # Rescale bounding boxes to dimension of original image\n    boxes[:, 0] = ((boxes[:, 0] - pad_x // 2) / unpad_w) * orig_w\n    boxes[:, 1] = ((boxes[:, 1] - pad_y // 2) / unpad_h) * orig_h\n    boxes[:, 2] = ((boxes[:, 2] - pad_x // 2) / unpad_w) * orig_w\n    boxes[:, 3] = ((boxes[:, 3] - pad_y // 2) / unpad_h) * orig_h\n    return boxes\n\n\ndef xywh2xyxy(x):\n    y = x.new(x.shape)\n    y[..., 0] = x[..., 0] - x[..., 2] / 2\n    y[..., 1] = x[..., 1] - x[..., 3] / 2\n    y[..., 2] = x[..., 0] + x[..., 2] / 2\n    y[..., 3] = x[..., 1] + x[..., 3] / 2\n    return y\n\n\ndef ap_per_class(tp, conf, pred_cls, target_cls):\n    \"\"\" Compute the average precision, given the recall and precision curves.\n    Source: https://github.com/rafaelpadilla/Object-Detection-Metrics.\n    # Arguments\n        tp:    True positives (list).\n        conf:  Objectness value from 0-1 (list).\n        pred_cls: Predicted object classes (list).\n        target_cls: True object classes (list).\n    # Returns\n        The average precision as computed in py-faster-rcnn.\n    \"\"\"\n\n    # Sort by objectness\n    i = np.argsort(-conf)\n    tp, conf, pred_cls = tp[i], conf[i], pred_cls[i]\n\n    # Find unique classes\n    unique_classes = np.unique(target_cls)\n\n    # Create Precision-Recall curve and compute AP for each class\n    ap, p, r = [], [], []\n    for c in tqdm.tqdm(unique_classes, desc=\"Computing AP\"):\n        i = pred_cls == c\n        n_gt = (target_cls == c).sum()  # Number of ground truth objects\n        n_p = i.sum()  # Number of predicted objects\n\n        if n_p == 0 and n_gt == 0:\n            continue\n        elif n_p == 0 or n_gt == 0:\n            ap.append(0)\n            r.append(0)\n            p.append(0)\n        else:\n            # Accumulate FPs and TPs\n            fpc = (1 - tp[i]).cumsum()\n            tpc = (tp[i]).cumsum()\n\n            # Recall\n            recall_curve = tpc / (n_gt + 1e-16)\n            r.append(recall_curve[-1])\n\n            # Precision\n            precision_curve = tpc / (tpc + fpc)\n            p.append(precision_curve[-1])\n\n            # AP from recall-precision curve\n            ap.append(compute_ap(recall_curve, precision_curve))\n\n    # Compute F1 score (harmonic mean of precision and recall)\n    p, r, ap = np.array(p), np.array(r), np.array(ap)\n    f1 = 2 * p * r / (p + r + 1e-16)\n\n    return p, r, ap, f1, unique_classes.astype(\"int32\")\n\n\ndef compute_ap(recall, precision):\n    \"\"\" Compute the average precision, given the recall and precision curves.\n    Code originally from https://github.com/rbgirshick/py-faster-rcnn.\n\n    # Arguments\n        recall:    The recall curve (list).\n        precision: The precision curve (list).\n    # Returns\n        The average precision as computed in py-faster-rcnn.\n    \"\"\"\n    # correct AP calculation\n    # first append sentinel values at the end\n    mrec = np.concatenate(([0.0], recall, [1.0]))\n    mpre = np.concatenate(([0.0], precision, [0.0]))\n\n    # compute the precision envelope\n    for i in range(mpre.size - 1, 0, -1):\n        mpre[i - 1] = np.maximum(mpre[i - 1], mpre[i])\n\n    # to calculate area under PR curve, look for points\n    # where X axis (recall) changes value\n    i = np.where(mrec[1:] != mrec[:-1])[0]\n\n    # and sum (\\Delta recall) * prec\n    ap = np.sum((mrec[i + 1] - mrec[i]) * mpre[i + 1])\n    return ap\n\n\ndef get_batch_statistics(outputs, targets, iou_threshold):\n    \"\"\" Compute true positives, predicted scores and predicted labels per sample \"\"\"\n    batch_metrics = []\n    for sample_i in range(len(outputs)):\n\n        if outputs[sample_i] is None:\n            continue\n\n        output = outputs[sample_i]\n        pred_boxes = output[:, :4]\n        pred_scores = output[:, 4]\n        pred_labels = output[:, -1]\n\n        true_positives = np.zeros(pred_boxes.shape[0])\n\n        annotations = targets[targets[:, 0] == sample_i][:, 1:]\n        target_labels = annotations[:, 0] if len(annotations) else []\n        if len(annotations):\n            detected_boxes = []\n            target_boxes = annotations[:, 1:]\n\n            for pred_i, (pred_box, pred_label) in enumerate(zip(pred_boxes, pred_labels)):\n\n                # If targets are found break\n                if len(detected_boxes) == len(annotations):\n                    break\n\n                # Ignore if label is not one of the target labels\n                if pred_label not in target_labels:\n                    continue\n\n                iou, box_index = bbox_iou(pred_box.unsqueeze(0), target_boxes).max(0)\n                if iou >= iou_threshold and box_index not in detected_boxes:\n                    true_positives[pred_i] = 1\n                    detected_boxes += [box_index]\n        batch_metrics.append([true_positives, pred_scores, pred_labels])\n    return batch_metrics\n\n\ndef bbox_wh_iou(wh1, wh2):\n    wh2 = wh2.t()\n    w1, h1 = wh1[0], wh1[1]\n    w2, h2 = wh2[0], wh2[1]\n    inter_area = torch.min(w1, w2) * torch.min(h1, h2)\n    union_area = (w1 * h1 + 1e-16) + w2 * h2 - inter_area\n    return inter_area / union_area\n\n\ndef bbox_iou(box1, box2, x1y1x2y2=True):\n    \"\"\"\n    Returns the IoU of two bounding boxes\n    \"\"\"\n    if not x1y1x2y2:\n        # Transform from center and width to exact coordinates\n        b1_x1, b1_x2 = box1[:, 0] - box1[:, 2] / 2, box1[:, 0] + box1[:, 2] / 2\n        b1_y1, b1_y2 = box1[:, 1] - box1[:, 3] / 2, box1[:, 1] + box1[:, 3] / 2\n        b2_x1, b2_x2 = box2[:, 0] - box2[:, 2] / 2, box2[:, 0] + box2[:, 2] / 2\n        b2_y1, b2_y2 = box2[:, 1] - box2[:, 3] / 2, box2[:, 1] + box2[:, 3] / 2\n    else:\n        # Get the coordinates of bounding boxes\n        b1_x1, b1_y1, b1_x2, b1_y2 = box1[:, 0], box1[:, 1], box1[:, 2], box1[:, 3]\n        b2_x1, b2_y1, b2_x2, b2_y2 = box2[:, 0], box2[:, 1], box2[:, 2], box2[:, 3]\n\n    # get the corrdinates of the intersection rectangle\n    inter_rect_x1 = torch.max(b1_x1, b2_x1)\n    inter_rect_y1 = torch.max(b1_y1, b2_y1)\n    inter_rect_x2 = torch.min(b1_x2, b2_x2)\n    inter_rect_y2 = torch.min(b1_y2, b2_y2)\n    # Intersection area\n    inter_area = torch.clamp(inter_rect_x2 - inter_rect_x1 + 1, min=0) * torch.clamp(\n        inter_rect_y2 - inter_rect_y1 + 1, min=0\n    )\n    # Union Area\n    b1_area = (b1_x2 - b1_x1 + 1) * (b1_y2 - b1_y1 + 1)\n    b2_area = (b2_x2 - b2_x1 + 1) * (b2_y2 - b2_y1 + 1)\n\n    iou = inter_area / (b1_area + b2_area - inter_area + 1e-16)\n\n    return iou\n\n\ndef non_max_suppression(prediction, conf_thres=0.5, nms_thres=0.4):\n    \"\"\"\n    Removes detections with lower object confidence score than 'conf_thres' and performs\n    Non-Maximum Suppression to further filter detections.\n    Returns detections with shape:\n        (x1, y1, x2, y2, object_conf, class_score, class_pred)\n    \"\"\"\n\n    # From (center x, center y, width, height) to (x1, y1, x2, y2)\n    prediction[..., :4] = xywh2xyxy(prediction[..., :4])\n    output = [None for _ in range(len(prediction))]\n    for image_i, image_pred in enumerate(prediction):\n        # Filter out confidence scores below threshold\n        image_pred = image_pred[image_pred[:, 4] >= conf_thres]\n        # If none are remaining => process next image\n        if not image_pred.size(0):\n            continue\n        # Object confidence times class confidence\n        score = image_pred[:, 4] * image_pred[:, 5:].max(1)[0]\n        # Sort by it\n        image_pred = image_pred[(-score).argsort()]\n        class_confs, class_preds = image_pred[:, 5:].max(1, keepdim=True)\n        detections = torch.cat((image_pred[:, :5], class_confs.float(), class_preds.float()), 1)\n        # Perform non-maximum suppression\n        keep_boxes = []\n        while detections.size(0):\n            large_overlap = bbox_iou(detections[0, :4].unsqueeze(0), detections[:, :4]) > nms_thres\n            label_match = detections[0, -1] == detections[:, -1]\n            # Indices of boxes with lower confidence scores, large IOUs and matching labels\n            invalid = large_overlap & label_match\n            weights = detections[invalid, 4:5]\n            # Merge overlapping bboxes by order of confidence\n            detections[0, :4] = (weights * detections[invalid, :4]).sum(0) / weights.sum()\n            keep_boxes += [detections[0]]\n            detections = detections[~invalid]\n            #Handle NaN values:\n            nans = torch.isnan(detections)\n            detections[nans] = 0\n        if keep_boxes:\n            output[image_i] = torch.stack(keep_boxes)\n\n    return output\n\n\ndef build_targets(pred_boxes, pred_cls, target, anchors, ignore_thres):\n\n    ByteTensor = torch.cuda.BoolTensor if pred_boxes.is_cuda else torch.BoolTensor\n    FloatTensor = torch.cuda.FloatTensor if pred_boxes.is_cuda else torch.FloatTensor\n\n    nB = pred_boxes.size(0)\n    nA = pred_boxes.size(1)\n    nC = pred_cls.size(-1)\n    nG = pred_boxes.size(2)\n\n    # Output tensors\n    obj_mask = ByteTensor(nB, nA, nG, nG).fill_(False)\n    noobj_mask = ByteTensor(nB, nA, nG, nG).fill_(True)\n    class_mask = FloatTensor(nB, nA, nG, nG).fill_(0)\n    iou_scores = FloatTensor(nB, nA, nG, nG).fill_(0)\n    tx = FloatTensor(nB, nA, nG, nG).fill_(0)\n    ty = FloatTensor(nB, nA, nG, nG).fill_(0)\n    tw = FloatTensor(nB, nA, nG, nG).fill_(0)\n    th = FloatTensor(nB, nA, nG, nG).fill_(0)\n    tcls = FloatTensor(nB, nA, nG, nG, nC).fill_(0)\n\n    # Convert to position relative to box\n    target_boxes = target[:, 2:6] * nG\n    gxy = target_boxes[:, :2]\n    gwh = target_boxes[:, 2:]\n    # Get anchors with best iou\n    ious = torch.stack([bbox_wh_iou(anchor, gwh) for anchor in anchors])\n    best_ious, best_n = ious.max(0)\n    # Separate target values\n    b, target_labels = target[:, :2].long().t()\n    gx, gy = gxy.t()\n    gw, gh = gwh.t()\n    gi, gj = gxy.long().t()\n    # Set masks\n    obj_mask[b, best_n, gj, gi] = 1\n    noobj_mask[b, best_n, gj, gi] = 0\n\n    # Set noobj mask to zero where iou exceeds ignore threshold\n    for i, anchor_ious in enumerate(ious.t()):\n        noobj_mask[b[i], anchor_ious > ignore_thres, gj[i], gi[i]] = 0\n\n    # Coordinates\n    tx[b, best_n, gj, gi] = gx - gx.floor()\n    ty[b, best_n, gj, gi] = gy - gy.floor()\n    # Width and height\n    tw[b, best_n, gj, gi] = torch.log(gw / anchors[best_n][:, 0] + 1e-16)\n    th[b, best_n, gj, gi] = torch.log(gh / anchors[best_n][:, 1] + 1e-16)\n    # One-hot encoding of label\n    tcls[b, best_n, gj, gi, target_labels] = 1\n    # Compute label correctness and iou at best anchor\n    class_mask[b, best_n, gj, gi] = (pred_cls[b, best_n, gj, gi].argmax(-1) == target_labels).float()\n    iou_scores[b, best_n, gj, gi] = bbox_iou(pred_boxes[b, best_n, gj, gi], target_boxes, x1y1x2y2=False)\n\n    tconf = obj_mask.float()\n    return iou_scores, class_mask, obj_mask, noobj_mask, tx, ty, tw, th, tcls, tconf\n\ndef parse_model_config(path):\n    \"\"\"Parses the yolo-v3 layer configuration file and returns module definitions\"\"\"\n    file = open(path, 'r')\n    lines = file.read().split('\\n')\n    lines = [x for x in lines if x and not x.startswith('#')]\n    lines = [x.rstrip().lstrip() for x in lines] # get rid of fringe whitespaces\n    module_defs = []\n    for line in lines:\n        if line.startswith('['): # This marks the start of a new block\n            module_defs.append({})\n            module_defs[-1]['type'] = line[1:-1].rstrip()\n            if module_defs[-1]['type'] == 'convolutional':\n                module_defs[-1]['batch_normalize'] = 0\n        else:\n            key, value = line.split(\"=\")\n            value = value.strip()\n            module_defs[-1][key.rstrip()] = value.strip()\n\n    return module_defs\n\ndef parse_data_config(path):\n    \"\"\"Parses the data configuration file\"\"\"\n    options = dict()\n    options['gpus'] = '0,1,2,3'\n    options['num_workers'] = '10'\n    with open(path, 'r') as fp:\n        lines = fp.readlines()\n    for line in lines:\n        line = line.strip()\n        if line == '' or line.startswith('#'):\n            continue\n        key, value = line.split('=')\n        options[key.strip()] = value.strip()\n    return options\ndef evaluate(model, path, iou_thres, conf_thres, nms_thres, img_size, batch_size):\n    model.eval()\n\n    # Get dataloader\n    dataset = ListDataset(path, img_size=img_size, augment=False, multiscale=False)\n    dataloader = torch.utils.data.DataLoader(\n        dataset, batch_size=batch_size, shuffle=False, num_workers=1, collate_fn=dataset.collate_fn\n    )\n\n    Tensor = torch.cuda.FloatTensor if torch.cuda.is_available() else torch.FloatTensor\n\n    labels = []\n    sample_metrics = []  # List of tuples (TP, confs, pred)\n    for batch_i, (_, imgs, targets) in enumerate(tqdm.tqdm(dataloader, desc=\"Detecting objects\")):\n\n        # Extract labels\n        labels += targets[:, 1].tolist()\n        # Rescale target\n        targets[:, 2:] = xywh2xyxy(targets[:, 2:])\n        targets[:, 2:] *= img_size\n\n        imgs = Variable(imgs.type(Tensor), requires_grad=False)\n\n        with torch.no_grad():\n            outputs = model(imgs)\n            outputs = non_max_suppression(outputs, conf_thres=conf_thres, nms_thres=nms_thres)\n\n        sample_metrics += get_batch_statistics(outputs, targets, iou_threshold=iou_thres)\n\n    # Concatenate sample statistics\n    true_positives, pred_scores, pred_labels = [np.concatenate(x, 0) for x in list(zip(*sample_metrics))]\n    precision, recall, AP, f1, ap_class = ap_per_class(true_positives, pred_scores, pred_labels, labels)\n\n    return precision, recall, AP, f1, ap_class","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def create_modules(module_defs):\n    \"\"\"\n    Constructs module list of layer blocks from module configuration in module_defs\n    \"\"\"\n    hyperparams = module_defs.pop(0)\n    output_filters = [int(hyperparams[\"channels\"])]\n    module_list = nn.ModuleList()\n    for module_i, module_def in enumerate(module_defs):\n        modules = nn.Sequential()\n\n        if module_def[\"type\"] == \"convolutional\":\n            bn = int(module_def[\"batch_normalize\"])\n            filters = int(module_def[\"filters\"])\n            kernel_size = int(module_def[\"size\"])\n            pad = (kernel_size - 1) // 2\n            modules.add_module(\n                f\"conv_{module_i}\",\n                nn.Conv2d(\n                    in_channels=output_filters[-1],\n                    out_channels=filters,\n                    kernel_size=kernel_size,\n                    stride=int(module_def[\"stride\"]),\n                    padding=pad,\n                    bias=not bn,\n                ),\n            )\n            if bn:\n                modules.add_module(f\"batch_norm_{module_i}\", nn.BatchNorm2d(filters, momentum=0.9, eps=1e-5))\n            if module_def[\"activation\"] == \"leaky\":\n                modules.add_module(f\"leaky_{module_i}\", nn.LeakyReLU(0.1))\n\n        elif module_def[\"type\"] == \"maxpool\":\n            kernel_size = int(module_def[\"size\"])\n            stride = int(module_def[\"stride\"])\n            if kernel_size == 2 and stride == 1:\n                modules.add_module(f\"_debug_padding_{module_i}\", nn.ZeroPad2d((0, 1, 0, 1)))\n            maxpool = nn.MaxPool2d(kernel_size=kernel_size, stride=stride, padding=int((kernel_size - 1) // 2))\n            modules.add_module(f\"maxpool_{module_i}\", maxpool)\n\n        elif module_def[\"type\"] == \"upsample\":\n            upsample = Upsample(scale_factor=int(module_def[\"stride\"]), mode=\"nearest\")\n            modules.add_module(f\"upsample_{module_i}\", upsample)\n\n        elif module_def[\"type\"] == \"route\":\n            layers = [int(x) for x in module_def[\"layers\"].split(\",\")]\n            filters = sum([output_filters[1:][i] for i in layers])\n            modules.add_module(f\"route_{module_i}\", EmptyLayer())\n\n        elif module_def[\"type\"] == \"shortcut\":\n            filters = output_filters[1:][int(module_def[\"from\"])]\n            modules.add_module(f\"shortcut_{module_i}\", EmptyLayer())\n\n        elif module_def[\"type\"] == \"yolo\":\n            anchor_idxs = [int(x) for x in module_def[\"mask\"].split(\",\")]\n            # Extract anchors\n            anchors = [int(x) for x in module_def[\"anchors\"].split(\",\")]\n            anchors = [(anchors[i], anchors[i + 1]) for i in range(0, len(anchors), 2)]\n            anchors = [anchors[i] for i in anchor_idxs]\n            num_classes = int(module_def[\"classes\"])\n            img_size = int(hyperparams[\"height\"])\n            # Define detection layer\n            yolo_layer = YOLOLayer(anchors, num_classes, img_size)\n            modules.add_module(f\"yolo_{module_i}\", yolo_layer)\n        # Register module list and number of output filters\n        module_list.append(modules)\n        output_filters.append(filters)\n\n    return hyperparams, module_list\n\n\nclass Upsample(nn.Module):\n    \"\"\" nn.Upsample is deprecated \"\"\"\n\n    def __init__(self, scale_factor, mode=\"nearest\"):\n        super(Upsample, self).__init__()\n        self.scale_factor = scale_factor\n        self.mode = mode\n\n    def forward(self, x):\n        x = F.interpolate(x, scale_factor=self.scale_factor, mode=self.mode)\n        return x\n\n\nclass EmptyLayer(nn.Module):\n    \"\"\"Placeholder for 'route' and 'shortcut' layers\"\"\"\n\n    def __init__(self):\n        super(EmptyLayer, self).__init__()\n\n\nclass YOLOLayer(nn.Module):\n    \"\"\"Detection layer\"\"\"\n\n    def __init__(self, anchors, num_classes, img_dim=416):\n        super(YOLOLayer, self).__init__()\n        self.anchors = anchors\n        self.num_anchors = len(anchors)\n        self.num_classes = num_classes\n        self.ignore_thres = 0.5\n        self.mse_loss = nn.L1Loss()\n        self.bce_loss = nn.BCELoss()\n        self.obj_scale = 1\n        self.noobj_scale = 1\n        self.metrics = {}\n        self.img_dim = img_dim\n        self.grid_size = 0  # grid size\n\n    def compute_grid_offsets(self, grid_size, cuda=True):\n        self.grid_size = grid_size\n        g = self.grid_size\n        FloatTensor = torch.cuda.FloatTensor if cuda else torch.FloatTensor\n        self.stride = self.img_dim / self.grid_size\n        # Calculate offsets for each grid\n        self.grid_x = torch.arange(g).repeat(g, 1).view([1, 1, g, g]).type(FloatTensor)\n        self.grid_y = torch.arange(g).repeat(g, 1).t().view([1, 1, g, g]).type(FloatTensor)\n        self.scaled_anchors = FloatTensor([(a_w / self.stride, a_h / self.stride) for a_w, a_h in self.anchors])\n        self.anchor_w = self.scaled_anchors[:, 0:1].view((1, self.num_anchors, 1, 1))\n        self.anchor_h = self.scaled_anchors[:, 1:2].view((1, self.num_anchors, 1, 1))\n\n    def forward(self, x, targets=None, img_dim=None):\n\n        # Tensors for cuda support\n        FloatTensor = torch.cuda.FloatTensor if x.is_cuda else torch.FloatTensor\n        LongTensor = torch.cuda.LongTensor if x.is_cuda else torch.LongTensor\n        ByteTensor = torch.cuda.ByteTensor if x.is_cuda else torch.ByteTensor\n\n        self.img_dim = img_dim\n        num_samples = x.size(0)\n        grid_size = x.size(2)\n\n        prediction = (\n            x.view(num_samples, self.num_anchors, self.num_classes + 5, grid_size, grid_size)\n            .permute(0, 1, 3, 4, 2)\n            .contiguous()\n        )\n\n        # Get outputs\n        x = torch.sigmoid(prediction[..., 0])  # Center x\n        y = torch.sigmoid(prediction[..., 1])  # Center y\n        w = prediction[..., 2]  # Width\n        h = prediction[..., 3]  # Height\n        pred_conf = torch.sigmoid(prediction[..., 4])  # Conf\n        pred_cls = torch.sigmoid(prediction[..., 5:])  # Cls pred.\n\n        # If grid size does not match current we compute new offsets\n        if grid_size != self.grid_size:\n            self.compute_grid_offsets(grid_size, cuda=x.is_cuda)\n\n        # Add offset and scale with anchors\n        pred_boxes = FloatTensor(prediction[..., :4].shape)\n        pred_boxes[..., 0] = x.data + self.grid_x\n        pred_boxes[..., 1] = y.data + self.grid_y\n        pred_boxes[..., 2] = torch.exp(w.data) * self.anchor_w\n        pred_boxes[..., 3] = torch.exp(h.data) * self.anchor_h\n\n        output = torch.cat(\n            (\n                pred_boxes.view(num_samples, -1, 4) * self.stride,\n                pred_conf.view(num_samples, -1, 1),\n                pred_cls.view(num_samples, -1, self.num_classes),\n            ),\n            -1,\n        )\n\n        if targets is None:\n            return output, 0\n        else:\n            iou_scores, class_mask, obj_mask, noobj_mask, tx, ty, tw, th, tcls, tconf = build_targets(\n                pred_boxes=pred_boxes,\n                pred_cls=pred_cls,\n                target=targets,\n                anchors=self.scaled_anchors,\n                ignore_thres=self.ignore_thres,\n            )\n\n            # Loss : Mask outputs to ignore non-existing objects (except with conf. loss)\n            loss_x = self.mse_loss(x[obj_mask], tx[obj_mask])\n            loss_y = self.mse_loss(y[obj_mask], ty[obj_mask])\n            loss_w = self.mse_loss(w[obj_mask], tw[obj_mask])\n            loss_h = self.mse_loss(h[obj_mask], th[obj_mask])\n            loss_conf_obj = self.bce_loss(pred_conf[obj_mask], tconf[obj_mask])\n            loss_conf_noobj = self.bce_loss(pred_conf[noobj_mask], tconf[noobj_mask])\n            loss_conf = self.obj_scale * loss_conf_obj + self.noobj_scale * loss_conf_noobj\n            loss_cls = self.bce_loss(pred_cls[obj_mask], tcls[obj_mask])\n            total_loss = loss_x + loss_y + loss_w + loss_h + loss_conf_obj + loss_conf_noobj\n\n            # Metrics\n            cls_acc = 100 * class_mask[obj_mask].mean()\n            conf_obj = pred_conf[obj_mask].mean()\n            conf_noobj = pred_conf[noobj_mask].mean()\n            conf50 = (pred_conf > 0.5).float()\n            iou50 = (iou_scores > 0.5).float()\n            iou75 = (iou_scores > 0.75).float()\n            detected_mask = conf50 * class_mask * tconf\n            precision = torch.sum(iou50 * detected_mask) / (conf50.sum() + 1e-16)\n            recall50 = torch.sum(iou50 * detected_mask) / (obj_mask.sum() + 1e-16)\n            recall75 = torch.sum(iou75 * detected_mask) / (obj_mask.sum() + 1e-16)\n\n            self.metrics = {\n                \"loss\": to_cpu(total_loss).item(),\n                \"x\": to_cpu(loss_x).item(),\n                \"y\": to_cpu(loss_y).item(),\n                \"w\": to_cpu(loss_w).item(),\n                \"h\": to_cpu(loss_h).item(),\n                \"conf\": to_cpu(loss_conf).item(),\n                \"cls\": to_cpu(loss_cls).item(),\n                \"cls_acc\": to_cpu(cls_acc).item(),\n                \"recall50\": to_cpu(recall50).item(),\n                \"recall75\": to_cpu(recall75).item(),\n                \"precision\": to_cpu(precision).item(),\n                \"conf_obj\": to_cpu(conf_obj).item(),\n                \"conf_noobj\": to_cpu(conf_noobj).item(),\n                \"grid_size\": grid_size,\n            }\n\n            return output, total_loss\n\n\nclass Darknet(nn.Module):\n    \"\"\"YOLOv3 object detection model\"\"\"\n\n    def __init__(self, config_path, img_size=416):\n        super(Darknet, self).__init__()\n        self.module_defs = parse_model_config(config_path)\n        self.hyperparams, self.module_list = create_modules(self.module_defs)\n        self.yolo_layers = [layer[0] for layer in self.module_list if hasattr(layer[0], \"metrics\")]\n        self.img_size = img_size\n        self.seen = 0\n        self.header_info = np.array([0, 0, 0, self.seen, 0], dtype=np.int32)\n\n    def forward(self, x, targets=None):\n        img_dim = x.shape[2]\n        loss = 0\n        layer_outputs, yolo_outputs = [], []\n        for i, (module_def, module) in enumerate(zip(self.module_defs, self.module_list)):\n            if module_def[\"type\"] in [\"convolutional\", \"upsample\", \"maxpool\"]:\n                x = module(x)\n            elif module_def[\"type\"] == \"route\":\n                x = torch.cat([layer_outputs[int(layer_i)] for layer_i in module_def[\"layers\"].split(\",\")], 1)\n            elif module_def[\"type\"] == \"shortcut\":\n                layer_i = int(module_def[\"from\"])\n                x = layer_outputs[-1] + layer_outputs[layer_i]\n            elif module_def[\"type\"] == \"yolo\":\n                x, layer_loss = module[0](x, targets, img_dim)\n                loss += layer_loss\n                yolo_outputs.append(x)\n            layer_outputs.append(x)\n        yolo_outputs = to_cpu(torch.cat(yolo_outputs, 1))\n        return yolo_outputs if targets is None else (loss, yolo_outputs)\n\n    def load_darknet_weights(self, weights_path):\n        \"\"\"Parses and loads the weights stored in 'weights_path'\"\"\"\n\n        # Open the weights file\n        with open(weights_path, \"rb\") as f:\n            header = np.fromfile(f, dtype=np.int32, count=5)  # First five are header values\n            self.header_info = header  # Needed to write header when saving weights\n            self.seen = header[3]  # number of images seen during training\n            weights = np.fromfile(f, dtype=np.float32)  # The rest are weights\n\n        # Establish cutoff for loading backbone weights\n        cutoff = None\n        if \"darknet53.conv.74\" in weights_path:\n            cutoff = 75\n\n        ptr = 0\n        for i, (module_def, module) in enumerate(zip(self.module_defs, self.module_list)):\n            if i == cutoff:\n                break\n            if module_def[\"type\"] == \"convolutional\":\n                conv_layer = module[0]\n                if module_def[\"batch_normalize\"]:\n                    # Load BN bias, weights, running mean and running variance\n                    bn_layer = module[1]\n                    num_b = bn_layer.bias.numel()  # Number of biases\n                    # Bias\n                    bn_b = torch.from_numpy(weights[ptr : ptr + num_b]).view_as(bn_layer.bias)\n                    bn_layer.bias.data.copy_(bn_b)\n                    ptr += num_b\n                    # Weight\n                    bn_w = torch.from_numpy(weights[ptr : ptr + num_b]).view_as(bn_layer.weight)\n                    bn_layer.weight.data.copy_(bn_w)\n                    ptr += num_b\n                    # Running Mean\n                    bn_rm = torch.from_numpy(weights[ptr : ptr + num_b]).view_as(bn_layer.running_mean)\n                    bn_layer.running_mean.data.copy_(bn_rm)\n                    ptr += num_b\n                    # Running Var\n                    bn_rv = torch.from_numpy(weights[ptr : ptr + num_b]).view_as(bn_layer.running_var)\n                    bn_layer.running_var.data.copy_(bn_rv)\n                    ptr += num_b\n                else:\n                    # Load conv. bias\n                    num_b = conv_layer.bias.numel()\n                    conv_b = torch.from_numpy(weights[ptr : ptr + num_b]).view_as(conv_layer.bias)\n                    conv_layer.bias.data.copy_(conv_b)\n                    ptr += num_b\n                # Load conv. weights\n                num_w = conv_layer.weight.numel()\n                conv_w = torch.from_numpy(weights[ptr : ptr + num_w]).view_as(conv_layer.weight)\n                conv_layer.weight.data.copy_(conv_w)\n                ptr += num_w\n\n    def save_darknet_weights(self, path, cutoff=-1):\n        \"\"\"\n            @:param path    - path of the new weights file\n            @:param cutoff  - save layers between 0 and cutoff (cutoff = -1 -> all are saved)\n        \"\"\"\n        fp = open(path, \"wb\")\n        self.header_info[3] = self.seen\n        self.header_info.tofile(fp)\n\n        # Iterate through layers\n        for i, (module_def, module) in enumerate(zip(self.module_defs[:cutoff], self.module_list[:cutoff])):\n            if module_def[\"type\"] == \"convolutional\":\n                conv_layer = module[0]\n                # If batch norm, load bn first\n                if module_def[\"batch_normalize\"]:\n                    bn_layer = module[1]\n                    bn_layer.bias.data.cpu().numpy().tofile(fp)\n                    bn_layer.weight.data.cpu().numpy().tofile(fp)\n                    bn_layer.running_mean.data.cpu().numpy().tofile(fp)\n                    bn_layer.running_var.data.cpu().numpy().tofile(fp)\n                # Load conv bias\n                else:\n                    conv_layer.bias.data.cpu().numpy().tofile(fp)\n                # Load conv weights\n                conv_layer.weight.data.cpu().numpy().tofile(fp)\n\n        fp.close()\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n\ndata_config = parse_data_config(data_config_str)\ntrain_path = data_config[\"train\"]\nvalid_path = data_config[\"valid\"]\nclass_names = load_classes(data_config[\"names\"])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model = Darknet(model_def).to(device)\nmodel.apply(weights_init_normal)\n#model.load_darknet_weights(pretrained_weights)\nmodel.load_state_dict(torch.load(pretrained_weights)) #Use this if its a checkpoint","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#TODO - Fix train.txt and valid.txt to have correct image name (missing '/WheatDetection_data/')\n\"\"\"\ndataset = ListDataset(train_path, multiscale=True)\ndataloader = torch.utils.data.DataLoader(\n    dataset,\n    batch_size=batch_size,\n    shuffle=True,\n    num_workers=8,\n    pin_memory=True,\n    collate_fn=dataset.collate_fn,\n)\nlearning_rate = model.hyperparams['learning_rate']\nif optimizer_str == \"Adam\":\n    optimizer = torch.optim.Adam(model.parameters(),lr=float(learning_rate))\nelse:\n    optimizer = torch.optim.SGD(model.parameters(),lr=float(learning_rate))\n    \nfor epoch in range(epochs):\n        model.train()\n        start_time = time.time()\n        tot_loss = 0\n        for batch_i, (_, imgs, targets) in enumerate(dataloader):\n            batches_done = len(dataloader) * epoch + batch_i\n\n            imgs = Variable(imgs.to(device))\n            targets = Variable(targets.to(device), requires_grad=False)\n\n            loss, outputs = model(imgs, targets)\n            loss.backward()\n\n            if batches_done % gradient_accumulations:\n                # Accumulates gradient before each step\n                optimizer.step()\n                optimizer.zero_grad()\n\"\"\"","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model.eval()  # Set in evaluation mode\nimage_folder = \"/kaggle/input/global-wheat-detection/test/\"\nclass_path = \"/kaggle/input/custom-files/classes.names\"\n\ndataloader = DataLoader(\n    ImageFolder(image_folder, img_size=416),\n    batch_size=1,\n    shuffle=False,\n    num_workers=0,\n)\nclasses = load_classes(class_path)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"Tensor = torch.cuda.FloatTensor if torch.cuda.is_available() else torch.FloatTensor\nimgs = []  # Stores image paths\nimg_detections = []\nfor batch_i, (img_paths, input_imgs) in enumerate(dataloader):\n    # Configure input\n    input_imgs = Variable(input_imgs.type(Tensor))\n    # Get detections\n    with torch.no_grad():\n        detections = model(input_imgs)\n        detections = non_max_suppression(detections, 0.8, 0.4)\n    # Save image and detections\n    imgs.extend(img_paths)\n    img_detections.extend(detections)\n\nsubmission = []\nfor img_i, (path, detections) in enumerate(zip(imgs, img_detections)):\n        # Draw bounding boxes and labels of detections\n        prediction_string = []\n        if detections is not None:\n            detections = rescale_boxes(detections, 416, (1024,1024))\n            for x1, y1, x2, y2, conf, cls_conf, cls_pred in detections:\n                if x1 < 0:\n                    x1 = torch.FloatTensor([0.001])[0]\n                if y1 < 0:\n                    y1 = torch.FloatTensor([0.001])[0]\n                if x2 > 1024:\n                    x2 = torch.FloatTensor([1023.999])[0]\n                if y2 > 1024:\n                    y2 = torch.FloatTensor([1023.999])[0]\n                x = torch.round(x1)\n                y = torch.round(y1)\n                h = torch.round(y2-y1)\n                w = torch.round(x2-x1)\n                prediction_string.append(f\"{conf} {x} {y} {w} {h}\")\n        \n        prediction_string = \" \".join(prediction_string)\n        \n        filename = path.split(\"/\")[-1].split(\".\")[0]\n\n        submission.append([filename,prediction_string])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"out_of_order_df = pd.DataFrame(submission, columns=[\"image_id\",\"PredictionString\"])\nsubmission = pd.read_csv(f'/kaggle/input/global-wheat-detection/sample_submission.csv')\nroot_image = \"/kaggle/input/custom-files/test/test/\"\norder = [f\"{img}\" for img in submission.image_id]\nlist_order = list()\nfor name in order:\n    list_order.extend(out_of_order_df[out_of_order_df['image_id'] == name].values)\nin_order_df = pd.DataFrame(list_order,columns=['image_id', 'PredictionString'])\nin_order_df.to_csv('submission.csv', index=False)","execution_count":null,"outputs":[]}],"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":4,"nbformat_minor":4}