{"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 pandas as pd\nimport numpy as np\nimport cv2\nimport os\nimport re\nimport matplotlib.pyplot as plt\nfrom tqdm import tqdm_notebook as tqdm\n\nimport torch\nimport torch.nn as nn\nimport torchvision","metadata":{"execution":{"iopub.status.busy":"2022-03-20T07:05:43.683289Z","iopub.execute_input":"2022-03-20T07:05:43.683648Z","iopub.status.idle":"2022-03-20T07:05:45.105384Z","shell.execute_reply.started":"2022-03-20T07:05:43.683595Z","shell.execute_reply":"2022-03-20T07:05:45.104584Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Hyper-params\nMODEL_PATH = \"\"\ninput_size = 512\nIN_SCALE = 1024//input_size \nMODEL_SCALE = 4\nbatch_size = 2A\nmodel_name = \"resnet18\"\nTRAIN = True # True for training","metadata":{"execution":{"iopub.status.busy":"2022-03-20T07:05:45.107129Z","iopub.execute_input":"2022-03-20T07:05:45.107504Z","iopub.status.idle":"2022-03-20T07:05:45.114963Z","shell.execute_reply.started":"2022-03-20T07:05:45.107448Z","shell.execute_reply":"2022-03-20T07:05:45.114079Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Prepare labels","metadata":{}},{"cell_type":"code","source":"DIR_INPUT = '../input/global-wheat-detection/'\nDIR_TRAIN = f'{DIR_INPUT}/train'\nDIR_TEST = f'{DIR_INPUT}/test'\n\ntrain_df = pd.read_csv(f'{DIR_INPUT}/train.csv')\ntrain_df.shape","metadata":{"execution":{"iopub.status.busy":"2022-03-20T07:05:45.118298Z","iopub.execute_input":"2022-03-20T07:05:45.11869Z","iopub.status.idle":"2022-03-20T07:05:45.35673Z","shell.execute_reply.started":"2022-03-20T07:05:45.118545Z","shell.execute_reply":"2022-03-20T07:05:45.355984Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_df['x'] = -1\ntrain_df['y'] = -1\ntrain_df['w'] = -1\ntrain_df['h'] = -1\n\ndef expand_bbox(x):\n    r = np.array(re.findall(\"([0-9]+[.]?[0-9]*)\", x))\n    if len(r) == 0:\n        r = [-1, -1, -1, -1]\n    return r\n\ntrain_df[['x', 'y', 'w', 'h']] = np.stack(train_df['bbox'].apply(lambda x: expand_bbox(x)))\ntrain_df.drop(columns=['bbox'], inplace=True)\ntrain_df['x'] = train_df['x'].astype(np.float)\ntrain_df['y'] = train_df['y'].astype(np.float)\ntrain_df['w'] = train_df['w'].astype(np.float)\ntrain_df['h'] = train_df['h'].astype(np.float)","metadata":{"execution":{"iopub.status.busy":"2022-03-20T07:05:45.35817Z","iopub.execute_input":"2022-03-20T07:05:45.358506Z","iopub.status.idle":"2022-03-20T07:05:46.9957Z","shell.execute_reply.started":"2022-03-20T07:05:45.358459Z","shell.execute_reply":"2022-03-20T07:05:46.994933Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Split train-test\nfrom sklearn.model_selection import train_test_split\n# Split by unique image ids.\nimage_ids = train_df['image_id'].unique()\ntrain_id, test_id = train_test_split(image_ids, test_size=0.2, random_state=777)","metadata":{"execution":{"iopub.status.busy":"2022-03-20T07:05:46.998835Z","iopub.execute_input":"2022-03-20T07:05:46.999204Z","iopub.status.idle":"2022-03-20T07:05:47.679619Z","shell.execute_reply.started":"2022-03-20T07:05:46.999156Z","shell.execute_reply":"2022-03-20T07:05:47.678768Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Convert boxes to heatmap\nWHile the labels are given in COCO format, we need to convert them to heatmaps in order to train CenterNets.","metadata":{}},{"cell_type":"code","source":"# show image\nimg_id = train_id[0]\nimg = cv2.imread(os.path.join(DIR_INPUT,\"train\", img_id+\".jpg\"))\nimg = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\nplt.imshow(img)","metadata":{"execution":{"iopub.status.busy":"2022-03-20T07:05:47.682291Z","iopub.execute_input":"2022-03-20T07:05:47.682658Z","iopub.status.idle":"2022-03-20T07:05:47.95555Z","shell.execute_reply.started":"2022-03-20T07:05:47.682608Z","shell.execute_reply":"2022-03-20T07:05:47.95457Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# get targets\ntarget = train_df[train_df['image_id']==img_id]\n# convert targets to its center.\ntry:\n    center = np.array([target[\"x\"]+target[\"w\"]//2, target[\"y\"]+target[\"h\"]//2]).T\nexcept:\n    center = np.array([int(target[\"x\"]+target[\"w\"]//2), int(target[\"y\"]+target[\"h\"]//2)]).T.reshape(1,2)","metadata":{"execution":{"iopub.status.busy":"2022-03-20T07:05:47.956698Z","iopub.execute_input":"2022-03-20T07:05:47.957067Z","iopub.status.idle":"2022-03-20T07:05:48.001809Z","shell.execute_reply.started":"2022-03-20T07:05:47.957017Z","shell.execute_reply":"2022-03-20T07:05:48.00111Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Let's plot center points into the image.","metadata":{}},{"cell_type":"code","source":"# plot centers on image\nplt.figure(figsize=(14,14))\nplt.imshow(img)\nfor x in center:\n    plt.scatter(x[0], x[1], color='red', s=100)","metadata":{"execution":{"iopub.status.busy":"2022-03-20T07:05:48.003995Z","iopub.execute_input":"2022-03-20T07:05:48.004478Z","iopub.status.idle":"2022-03-20T07:05:48.978563Z","shell.execute_reply.started":"2022-03-20T07:05:48.004431Z","shell.execute_reply":"2022-03-20T07:05:48.977726Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Make heatmaps using the utility functions from the centernet repo\ndef draw_msra_gaussian(heatmap, center, sigma=2):\n  tmp_size = sigma * 6\n  mu_x = int(center[0] + 0.5)\n  mu_y = int(center[1] + 0.5)\n  w, h = heatmap.shape[0], heatmap.shape[1]\n  ul = [int(mu_x - tmp_size), int(mu_y - tmp_size)]\n  br = [int(mu_x + tmp_size + 1), int(mu_y + tmp_size + 1)]\n  if ul[0] >= h or ul[1] >= w or br[0] < 0 or br[1] < 0:\n    return heatmap\n  size = 2 * tmp_size + 1\n  x = np.arange(0, size, 1, np.float32)\n  y = x[:, np.newaxis]\n  x0 = y0 = size // 2\n  g = np.exp(- ((x - x0) ** 2 + (y - y0) ** 2) / (2 * sigma ** 2))\n  g_x = max(0, -ul[0]), min(br[0], h) - ul[0]\n  g_y = max(0, -ul[1]), min(br[1], w) - ul[1]\n  img_x = max(0, ul[0]), min(br[0], h)\n  img_y = max(0, ul[1]), min(br[1], w)\n  heatmap[img_y[0]:img_y[1], img_x[0]:img_x[1]] = np.maximum(\n    heatmap[img_y[0]:img_y[1], img_x[0]:img_x[1]],\n    g[g_y[0]:g_y[1], g_x[0]:g_x[1]])\n  return heatmap\ndef draw_dense_reg(regmap, heatmap, center, value, radius, is_offset=False):\n  diameter = 2 * radius + 1\n  gaussian = gaussian2D((diameter, diameter), sigma=diameter / 6)\n  value = np.array(value, dtype=np.float32).reshape(-1, 1, 1)\n  dim = value.shape[0]\n  reg = np.ones((dim, diameter*2+1, diameter*2+1), dtype=np.float32) * value\n  if is_offset and dim == 2:\n    delta = np.arange(diameter*2+1) - radius\n    reg[0] = reg[0] - delta.reshape(1, -1)\n    reg[1] = reg[1] - delta.reshape(-1, 1)\n  \n  x, y = int(center[0]), int(center[1])\n\n  height, width = heatmap.shape[0:2]\n    \n  left, right = min(x, radius), min(width - x, radius + 1)\n  top, bottom = min(y, radius), min(height - y, radius + 1)\n\n  masked_heatmap = heatmap[y - top:y + bottom, x - left:x + right]\n  masked_regmap = regmap[:, y - top:y + bottom, x - left:x + right]\n  masked_gaussian = gaussian[radius - top:radius + bottom,\n                             radius - left:radius + right]\n  masked_reg = reg[:, radius - top:radius + bottom,\n                      radius - left:radius + right]\n  if min(masked_gaussian.shape) > 0 and min(masked_heatmap.shape) > 0: # TODO debug\n    idx = (masked_gaussian >= masked_heatmap).reshape(\n      1, masked_gaussian.shape[0], masked_gaussian.shape[1])\n    masked_regmap = (1-idx) * masked_regmap + idx * masked_reg\n  regmap[:, y - top:y + bottom, x - left:x + right] = masked_regmap\n  return regmap\n\ndef gaussian2D(shape, sigma=1):\n    m, n = [(ss - 1.) / 2. for ss in shape]\n    y, x = np.ogrid[-m:m+1,-n:n+1]\n\n    h = np.exp(-(x * x + y * y) / (2 * sigma * sigma))\n    h[h < np.finfo(h.dtype).eps * h.max()] = 0\n    return h","metadata":{"execution":{"iopub.status.busy":"2022-03-20T07:05:48.979798Z","iopub.execute_input":"2022-03-20T07:05:48.980253Z","iopub.status.idle":"2022-03-20T07:05:49.018846Z","shell.execute_reply.started":"2022-03-20T07:05:48.980207Z","shell.execute_reply":"2022-03-20T07:05:49.017976Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Wrapped heatmap function\ndef make_hm_regr(target):\n    # make output heatmap for single class\n    hm = np.zeros([input_size//MODEL_SCALE, input_size//MODEL_SCALE])\n    # make regr heatmap \n    regr = np.zeros([2, input_size//MODEL_SCALE, input_size//MODEL_SCALE])\n    \n    if len(target) == 0:\n        return hm, regr\n    \n    try:\n        center = np.array([target[\"x\"]+target[\"w\"]//2, target[\"y\"]+target[\"h\"]//2, \n                       target[\"w\"], target[\"h\"]\n                      ]).T\n    except:\n        center = np.array([int(target[\"x\"]+target[\"w\"]//2), int(target[\"y\"]+target[\"h\"]//2), \n                       int(target[\"w\"]), int(target[\"h\"])\n                      ]).T.reshape(1,4)\n    \n    # make a center point\n    # try gaussian points.\n    for c in center:\n        hm = draw_msra_gaussian(hm, [int(c[0])//MODEL_SCALE//IN_SCALE, int(c[1])//MODEL_SCALE//IN_SCALE], \n                                sigma=np.clip(c[2]*c[3]//2000, 2, 4))    \n\n    # convert targets to its center.\n    regrs = center[:, 2:]/input_size/IN_SCALE\n\n    # plot regr values to mask\n    for r, c in zip(regrs, center):\n        for i in range(-2, 3):\n            for j in range(-2, 3):\n                try:\n                    regr[:, int(c[0])//MODEL_SCALE//IN_SCALE+i, \n                         int(c[1])//MODEL_SCALE//IN_SCALE+j] = r\n                except:\n                    pass\n    regr[0] = regr[0].T; regr[1] = regr[1].T;\n    return hm, regr","metadata":{"execution":{"iopub.status.busy":"2022-03-20T07:05:49.020037Z","iopub.execute_input":"2022-03-20T07:05:49.021929Z","iopub.status.idle":"2022-03-20T07:05:49.037837Z","shell.execute_reply.started":"2022-03-20T07:05:49.020289Z","shell.execute_reply":"2022-03-20T07:05:49.036919Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def pred2box(hm, regr, thresh=0.99):\n    # make binding box from heatmaps\n    # thresh: threshold for logits.\n        \n    # get center\n    pred = hm > thresh\n    pred_center = np.where(hm>thresh)\n    # get regressions\n    pred_r = regr[:,pred].T\n\n    # wrap as boxes\n    # [xmin, ymin, width, height]\n    # size as original image.\n    boxes = []\n    scores = hm[pred]\n    for i, b in enumerate(pred_r):\n        arr = np.array([pred_center[1][i]*MODEL_SCALE-b[0]*input_size//2, pred_center[0][i]*MODEL_SCALE-b[1]*input_size//2, \n                      int(b[0]*input_size), int(b[1]*input_size)])\n        arr = np.clip(arr, 0, input_size)\n        # filter \n        #if arr[0]<0 or arr[1]<0 or arr[0]>input_size or arr[1]>input_size:\n            #pass\n        boxes.append(arr)\n    return np.asarray(boxes), scores","metadata":{"execution":{"iopub.status.busy":"2022-03-20T07:05:49.039609Z","iopub.execute_input":"2022-03-20T07:05:49.040143Z","iopub.status.idle":"2022-03-20T07:05:49.050562Z","shell.execute_reply.started":"2022-03-20T07:05:49.039971Z","shell.execute_reply":"2022-03-20T07:05:49.049653Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# functions for plotting results\ndef showbox(img, hm, regr, thresh=0.9):\n    boxes, _ = pred2box(hm, regr, thresh=thresh)\n    print(\"preds:\",boxes.shape)\n    sample = img\n\n    for box in boxes:\n        # upper-left, lower-right\n        cv2.rectangle(sample,\n                      (int(box[0]), int(box[1]+box[3])),\n                      (int(box[0]+box[2]), int(box[1])),\n                      (220, 0, 0), 2)\n    return sample\n\ndef showgtbox(img, hm, regr, thresh=0.9):\n    boxes, _ = pred2box(hm, regr, thresh=thresh)\n    print(\"GT boxes:\", boxes.shape)\n    sample = img\n\n    for box in boxes:\n        cv2.rectangle(sample,\n                      (int(box[0]), int(box[1]+box[3])),\n                      (int(box[0]+box[2]), int(box[1])),\n                      (0, 220, 0), 2)\n    return sample","metadata":{"execution":{"iopub.status.busy":"2022-03-20T07:05:49.052286Z","iopub.execute_input":"2022-03-20T07:05:49.053095Z","iopub.status.idle":"2022-03-20T07:05:49.065651Z","shell.execute_reply.started":"2022-03-20T07:05:49.052791Z","shell.execute_reply":"2022-03-20T07:05:49.064956Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## compile data processing\nLet's first make sure that we can convert targets to heatmaps and bring it back to boxes.","metadata":{}},{"cell_type":"code","source":"# show image\n#img_id = train_df[\"image_id\"][]\nimg_id = train_id[0]\nimg = cv2.imread(os.path.join(DIR_INPUT,\"train\", img_id+\".jpg\"))\nimg = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\nimg = cv2.resize(img, (input_size, input_size))\nsample = img\n\n# get labels\ntarget = train_df[train_df['image_id']==img_id]\n\n# convert target to heatmaps\nhm, regr = make_hm_regr(target)\n\n# get boxes\nboxes, _ = pred2box(hm, regr)\n\nfig, ax = plt.subplots(1, 1, figsize=(16, 8))\nsample = showbox(sample, hm, regr, 0.99)\nplt.imshow(sample)\nplt.show()","metadata":{"scrolled":true,"execution":{"iopub.status.busy":"2022-03-20T07:05:49.067355Z","iopub.execute_input":"2022-03-20T07:05:49.067899Z","iopub.status.idle":"2022-03-20T07:05:49.439645Z","shell.execute_reply.started":"2022-03-20T07:05:49.067687Z","shell.execute_reply":"2022-03-20T07:05:49.438844Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Make dataset\nThen, let's make a dataset and a dataloader for training.","metadata":{}},{"cell_type":"code","source":"from torchvision import transforms\n\nclass Normalize(object):\n    def __init__(self):\n        self.mean=[0.485, 0.456, 0.406]\n        self.std=[0.229, 0.224, 0.225]\n        self.norm = transforms.Normalize(self.mean, self.std)\n    def __call__(self, image):\n        image = image.astype(np.float32)/255\n        axis = (0,1)\n        image -= self.mean\n        image /= self.std\n        return image\n    \n# pool duplicates\ndef pool(data):\n    stride = 3\n    for y in np.arange(1,data.shape[1]-1, stride):\n        for x in np.arange(1, data.shape[0]-1, stride):\n            a_2d = data[x-1:x+2, y-1:y+2]\n            max = np.asarray(np.unravel_index(np.argmax(a_2d), a_2d.shape))            \n            for c1 in range(3):\n                for c2 in range(3):\n                    #print(c1,c2)\n                    if not (c1== max[0] and c2 == max[1]):\n                        data[x+c1-1, y+c2-1] = -1\n    return data\n\nclass WheatDataset(torch.utils.data.Dataset):\n    def __init__(self, img_id, labels, transform=None):\n        self.img_id = img_id\n        self.labels = labels\n        if transform:\n            self.transform = transform\n        self.normalize = Normalize()\n        \n    def __len__(self):\n        return len(self.img_id)\n\n    def __getitem__(self, idx):\n        img = cv2.imread(os.path.join(DIR_INPUT,\"train\", self.img_id[idx]+\".jpg\"))\n        img = cv2.resize(img, (input_size, input_size))\n        img = self.normalize(img)\n        img = img.transpose([2,0,1])\n        target = self.labels[self.labels['image_id']==self.img_id[idx]]\n        hm, regr = make_hm_regr(target)\n        return img, hm, regr\n\n# Submission\nclass WheatDatasetTest(torch.utils.data.Dataset):\n    def __init__(self, image_dir, transform=None):\n        self.image_dir = image_dir\n        self.img_id = os.listdir(self.image_dir)\n        if transform:\n            self.transform = transform\n        self.normalize = Normalize()\n        \n    def __len__(self):\n        return len(self.img_id)\n\n    def __getitem__(self, idx):\n        img = cv2.imread(os.path.join(self.image_dir, self.img_id[idx]))\n        img = cv2.resize(img, (input_size, input_size))\n        img = self.normalize(img)\n        img = img.transpose([2,0,1])\n        return img, self.img_id[idx]","metadata":{"execution":{"iopub.status.busy":"2022-03-20T07:05:49.441218Z","iopub.execute_input":"2022-03-20T07:05:49.441777Z","iopub.status.idle":"2022-03-20T07:05:49.466794Z","shell.execute_reply.started":"2022-03-20T07:05:49.441717Z","shell.execute_reply":"2022-03-20T07:05:49.465972Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"traindataset = WheatDataset(train_id, train_df)\nvaldataset = WheatDataset(test_id, train_df)\ntestdataset = WheatDatasetTest('../input/global-wheat-detection/test')\n\n# Test dataset\nimg, hm, regr = traindataset[0]\nplt.imshow(img.transpose([1,2,0]))\nplt.show()\nimg.std()\nplt.imshow(hm)","metadata":{"execution":{"iopub.status.busy":"2022-03-20T07:05:49.46855Z","iopub.execute_input":"2022-03-20T07:05:49.468849Z","iopub.status.idle":"2022-03-20T07:05:49.80917Z","shell.execute_reply.started":"2022-03-20T07:05:49.468801Z","shell.execute_reply":"2022-03-20T07:05:49.808327Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Pack to dataloaders\ntrain_loader = torch.utils.data.DataLoader(traindataset,batch_size=batch_size,shuffle=True, num_workers=0)\nval_loader = torch.utils.data.DataLoader(valdataset,batch_size=batch_size,shuffle=True, num_workers=0)\ntest_loader = torch.utils.data.DataLoader(testdataset,batch_size=batch_size,shuffle=False, num_workers=0)","metadata":{"execution":{"iopub.status.busy":"2022-03-20T07:05:49.810551Z","iopub.execute_input":"2022-03-20T07:05:49.811099Z","iopub.status.idle":"2022-03-20T07:05:49.817926Z","shell.execute_reply.started":"2022-03-20T07:05:49.811046Z","shell.execute_reply":"2022-03-20T07:05:49.817194Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Define Centernet model\nThe model is based on [PKU centernets](https://www.kaggle.com/hocop1/centernet-baseline/data).","metadata":{}},{"cell_type":"code","source":"class double_conv(nn.Module):\n    '''(conv => BN => ReLU) * 2'''\n    def __init__(self, in_ch, out_ch):\n        super(double_conv, self).__init__()\n        self.conv = nn.Sequential(\n            nn.Conv2d(in_ch, out_ch, 3, padding=1),\n            nn.BatchNorm2d(out_ch),\n            nn.ReLU(inplace=True),\n            nn.Conv2d(out_ch, out_ch, 3, padding=1),\n            nn.BatchNorm2d(out_ch),\n            nn.ReLU(inplace=True)\n        )\n\n    def forward(self, x):\n        x = self.conv(x)\n        return x\n\nclass up(nn.Module):\n    def __init__(self, in_ch, out_ch, bilinear=True):\n        super(up, self).__init__()\n        if bilinear:\n            self.up = nn.Upsample(scale_factor=2, mode='bilinear', align_corners=True)\n        else:\n            self.up = nn.ConvTranspose2d(in_ch//2, in_ch//2, 2, stride=2)\n        self.conv = double_conv(in_ch, out_ch)\n        \n    def forward(self, x1, x2=None):\n        x1 = self.up(x1)\n        if x2 is not None:\n            x = torch.cat([x2, x1], dim=1)\n            # input is CHW\n            diffY = x2.size()[2] - x1.size()[2]\n            diffX = x2.size()[3] - x1.size()[3]\n\n            x1 = F.pad(x1, (diffX // 2, diffX - diffX//2,\n                            diffY // 2, diffY - diffY//2))\n        else:\n            x = x1\n        x = self.conv(x)\n        return x\n\nclass centernet(nn.Module):\n    def __init__(self, n_classes=1, model_name=\"resnet18\"):\n        super(centernet, self).__init__()\n        # create backbone.\n        basemodel = torchvision.models.resnet18(pretrained=False) # turn this on for training\n        basemodel = nn.Sequential(*list(basemodel.children())[:-2])\n        # set basemodel\n        self.base_model = basemodel\n        \n        if model_name == \"resnet34\" or model_name==\"resnet18\":\n            num_ch = 512\n        else:\n            num_ch = 2048\n        \n        self.up1 = up(num_ch, 512)\n        self.up2 = up(512, 256)\n        self.up3 = up(256, 256)\n        # output classification\n        self.outc = nn.Conv2d(256, n_classes, 1)\n        # output residue\n        self.outr = nn.Conv2d(256, 2, 1)\n        \n    def forward(self, x):\n        batch_size = x.shape[0]\n        \n        x = self.base_model(x)\n        \n        # Add positional info        \n        x = self.up1(x)\n        x = self.up2(x)\n        x = self.up3(x)\n        outc = self.outc(x)\n        outr = self.outr(x)\n        return outc, outr","metadata":{"execution":{"iopub.status.busy":"2022-03-20T07:05:49.819832Z","iopub.execute_input":"2022-03-20T07:05:49.820335Z","iopub.status.idle":"2022-03-20T07:05:49.84536Z","shell.execute_reply.started":"2022-03-20T07:05:49.820209Z","shell.execute_reply":"2022-03-20T07:05:49.844497Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model = centernet()\n# Check if it runs correctly\nmodel(torch.rand(1,3,512,512))[0].size()","metadata":{"execution":{"iopub.status.busy":"2022-03-20T07:05:49.846812Z","iopub.execute_input":"2022-03-20T07:05:49.847583Z","iopub.status.idle":"2022-03-20T07:05:51.367816Z","shell.execute_reply.started":"2022-03-20T07:05:49.847527Z","shell.execute_reply":"2022-03-20T07:05:51.366845Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Define optimizers","metadata":{}},{"cell_type":"code","source":"# Gets the GPU if there is one, otherwise the cpu\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nmodel.to(device)\n\n# Optimizer\nimport torch.optim as optim\noptimizer = optim.Adam(model.parameters(), lr=1e-4)","metadata":{"execution":{"iopub.status.busy":"2022-03-20T07:05:51.369172Z","iopub.execute_input":"2022-03-20T07:05:51.369562Z","iopub.status.idle":"2022-03-20T07:05:54.48321Z","shell.execute_reply.started":"2022-03-20T07:05:51.369506Z","shell.execute_reply":"2022-03-20T07:05:54.48239Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Define Loss","metadata":{}},{"cell_type":"markdown","source":"Về hàm loss của CenterNet \n![image.png](attachment:000b72eb-93de-42ad-9823-76b36f2156ca.png)","metadata":{},"attachments":{"000b72eb-93de-42ad-9823-76b36f2156ca.png":{"image/png":"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"}}},{"cell_type":"markdown","source":"","metadata":{}},{"cell_type":"code","source":"# From centernet repo\ndef neg_loss(pred, gt):\n  ''' Modified focal loss. Exactly the same as CornerNet.\n      Runs faster and costs a little bit more memory\n    Arguments:\n      pred (batch x c x h x w)\n      gt_regr (batch x c x h x w)\n  '''\n  pred = pred.unsqueeze(1).float()\n  gt = gt.unsqueeze(1).float()\n\n  pos_inds = gt.eq(1).float()\n  neg_inds = gt.lt(1).float()\n  neg_weights = torch.pow(1 - gt, 4)\n\n  loss = 0\n\n  pos_loss = torch.log(pred + 1e-12) * torch.pow(1 - pred, 3) * pos_inds\n  neg_loss = torch.log(1 - pred + 1e-12) * torch.pow(pred, 3) * neg_weights * neg_inds\n\n  num_pos  = pos_inds.float().sum()\n  pos_loss = pos_loss.sum()\n  neg_loss = neg_loss.sum()\n\n  if num_pos == 0:\n    loss = loss - neg_loss\n  else:\n    loss = loss - (pos_loss + neg_loss) / num_pos\n  return loss\n\ndef _reg_loss(regr, gt_regr, mask):\n  ''' L1 regression loss\n    Arguments:\n      regr (batch x max_objects x dim)\n      gt_regr (batch x max_objects x dim)\n      mask (batch x max_objects)\n  '''\n  num = mask.float().sum()\n  #print(gt_regr.size())\n  mask = mask.sum(1).unsqueeze(1).expand_as(gt_regr)\n  #print(mask.size())\n\n  regr = regr * mask\n  gt_regr = gt_regr * mask\n    \n  regr_loss = nn.functional.smooth_l1_loss(regr, gt_regr, size_average=False)\n  regr_loss = regr_loss / (num + 1e-4)\n  return regr_loss\n  \ndef centerloss(prediction, mask, regr,weight=0.4, size_average=True):\n    # Binary mask loss\n    pred_mask = torch.sigmoid(prediction[:, 0])\n    mask_loss = neg_loss(pred_mask, mask)\n    \n    # Regression L1 loss\n    pred_regr = prediction[:, 1:]\n    regr_loss = (torch.abs(pred_regr - regr).sum(1) * mask).sum(1).sum(1) / mask.sum(1).sum(1)\n    regr_loss = regr_loss.mean(0)\n  \n    # Sum\n    loss = mask_loss +regr_loss\n    if not size_average:\n        loss *= prediction.shape[0]\n    return loss ,mask_loss , regr_loss","metadata":{"execution":{"iopub.status.busy":"2022-03-20T07:05:54.486579Z","iopub.execute_input":"2022-03-20T07:05:54.486872Z","iopub.status.idle":"2022-03-20T07:05:54.502715Z","shell.execute_reply.started":"2022-03-20T07:05:54.486814Z","shell.execute_reply":"2022-03-20T07:05:54.501902Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Train function","metadata":{}},{"cell_type":"code","source":"def train(epoch):\n    model.train()\n    print('epochs {}/{} '.format(epoch+1,epochs))\n    running_loss = 0.0\n    running_mask = 0.0\n    running_regr = 0.0\n    t = tqdm(train_loader)\n    rd = np.random.rand()\n    \n    for idx, (img, hm, regr) in enumerate(t):       \n        # send to gpu\n        img = img.to(device)\n        hm_gt = hm.to(device)\n        regr_gt = regr.to(device)\n        # set opt\n        optimizer.zero_grad()\n        \n        # run model\n        hm, regr = model(img)\n        preds = torch.cat((hm, regr), 1)\n            \n        loss, mask_loss, regr_loss = centerloss(preds, hm_gt, regr_gt)\n        # misc\n        running_loss += loss\n        running_mask += mask_loss\n        running_regr += regr_loss\n        \n        loss.backward()\n        optimizer.step()\n        \n        t.set_description(f't (l={running_loss/(idx+1):.3f})(m={running_mask/(idx+1):.4f})(r={running_regr/(idx+1):.4f})')\n        \n    #scheduler.step()\n    print('train loss : {:.4f}'.format(running_loss/len(train_loader)))\n    print('maskloss : {:.4f}'.format(running_mask/(len(train_loader))))\n    print('regrloss : {:.4f}'.format(running_regr/(len(train_loader))))\n    \n    # save logs\n    log_epoch = {'epoch': epoch+1, 'lr': optimizer.state_dict()['param_groups'][0]['lr'],\n                    'loss': running_loss/len(train_loader), \"mask\": running_mask/(len(train_loader)), \n                 \"regr\": running_regr/(len(train_loader))}\n    logs.append(log_epoch)","metadata":{"execution":{"iopub.status.busy":"2022-03-20T07:05:54.504085Z","iopub.execute_input":"2022-03-20T07:05:54.504639Z","iopub.status.idle":"2022-03-20T07:05:54.519493Z","shell.execute_reply.started":"2022-03-20T07:05:54.504586Z","shell.execute_reply":"2022-03-20T07:05:54.518722Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Train model\nTo save time, only trained for 10 epochs","metadata":{}},{"cell_type":"code","source":"#epochs=100\nepochs = 10\nlogs = []\nlogs_eval = []\n\nif TRAIN:\n    for epoch in range(epochs):\n        train(epoch)\nelse:\n    model.load_state_dict(torch.load(MODEL_PATH))","metadata":{"execution":{"iopub.status.busy":"2022-03-20T07:05:54.521934Z","iopub.execute_input":"2022-03-20T07:05:54.522477Z","iopub.status.idle":"2022-03-20T07:44:53.933769Z","shell.execute_reply.started":"2022-03-20T07:05:54.522405Z","shell.execute_reply":"2022-03-20T07:44:53.932704Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Show predictions","metadata":{}},{"cell_type":"code","source":"for id in range(10):\n    img, hm_gt, regr_gt = valdataset[id]\n    img = torch.from_numpy(img)\n    with torch.no_grad():\n        hm, regr = model(img.to(device).float().unsqueeze(0))\n\n    \n    hm = hm.cpu().numpy().squeeze(0).squeeze(0)\n    regr = regr.cpu().numpy().squeeze(0)\n\n    # show image\n    img_id = test_id[id]\n    img = cv2.imread(os.path.join(DIR_INPUT,\"train\", img_id+\".jpg\"))\n    img = cv2.resize(cv2.cvtColor(img, cv2.COLOR_BGR2RGB), (input_size, input_size))\n\n    # get boxes\n    hm = torch.sigmoid(torch.from_numpy(hm)).numpy()\n    hm = pool(hm)\n    plt.imshow(hm>0.6)\n    plt.show()\n    sample = showbox(img, hm, regr, 0.6)\n    \n    # show gt\n    sample = showgtbox(sample, hm_gt, regr_gt, 0.99)\n    fig, ax = plt.subplots(1, 1, figsize=(16, 8))\n    plt.imshow(sample)\n    plt.show()","metadata":{"execution":{"iopub.status.busy":"2022-03-20T07:44:53.935353Z","iopub.execute_input":"2022-03-20T07:44:53.935945Z","iopub.status.idle":"2022-03-20T07:45:00.208852Z","shell.execute_reply.started":"2022-03-20T07:44:53.935887Z","shell.execute_reply":"2022-03-20T07:45:00.20799Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# For submissions","metadata":{}},{"cell_type":"code","source":"def format_prediction_string(boxes, scores):\n    pred_strings = []\n    for s, b in zip(scores, boxes.astype(int)):\n        # xmin, ymin, w, h\n        pred_strings.append(f'{s:.4f} {b[0]*IN_SCALE} {b[1]*IN_SCALE} {b[2]*IN_SCALE} {b[3]*IN_SCALE}')\n\n    return \" \".join(pred_strings)","metadata":{"execution":{"iopub.status.busy":"2022-03-20T07:45:00.210302Z","iopub.execute_input":"2022-03-20T07:45:00.210842Z","iopub.status.idle":"2022-03-20T07:45:00.217117Z","shell.execute_reply.started":"2022-03-20T07:45:00.210795Z","shell.execute_reply":"2022-03-20T07:45:00.216275Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"thresh = 0.6\nresults = []\n\nfor images, image_ids in tqdm(test_loader):\n\n    images = images.to(device)\n    with torch.no_grad():\n        hms, regrs = model(images)\n\n    for hm, regr, image_id in zip(hms, regrs, image_ids):\n        # process predictions\n        hm = hm.cpu().numpy().squeeze(0)\n        regr = regr.cpu().numpy()\n        hm = torch.sigmoid(torch.from_numpy(hm)).numpy()\n        hm = pool(hm)\n\n        boxes, scores = pred2box(hm, regr, thresh)\n\n        preds_sorted_idx = np.argsort(scores)[::-1]\n        boxes_sorted = boxes[preds_sorted_idx]\n        scores_sorted = scores[preds_sorted_idx]\n        \n        result = {\n            'image_id': image_id[:-4],\n            'PredictionString': format_prediction_string(boxes, scores)\n        }\n\n        results.append(result)","metadata":{"execution":{"iopub.status.busy":"2022-03-20T07:45:00.218664Z","iopub.execute_input":"2022-03-20T07:45:00.219338Z","iopub.status.idle":"2022-03-20T07:45:01.44807Z","shell.execute_reply.started":"2022-03-20T07:45:00.219286Z","shell.execute_reply":"2022-03-20T07:45:01.447195Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_df = pd.DataFrame(results, columns=['image_id', 'PredictionString'])\ntest_df.head()","metadata":{"execution":{"iopub.status.busy":"2022-03-20T07:45:01.449508Z","iopub.execute_input":"2022-03-20T07:45:01.450106Z","iopub.status.idle":"2022-03-20T07:45:01.465658Z","shell.execute_reply.started":"2022-03-20T07:45:01.450055Z","shell.execute_reply":"2022-03-20T07:45:01.464757Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_df.to_csv('submission.csv', index=False)","metadata":{"execution":{"iopub.status.busy":"2022-03-20T07:45:01.46726Z","iopub.execute_input":"2022-03-20T07:45:01.467758Z","iopub.status.idle":"2022-03-20T07:45:01.679652Z","shell.execute_reply.started":"2022-03-20T07:45:01.467567Z","shell.execute_reply":"2022-03-20T07:45:01.678955Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"That's it!","metadata":{}}]}