{"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":"# !pip install tifffile","metadata":{"execution":{"iopub.status.busy":"2022-08-21T13:56:59.612150Z","iopub.execute_input":"2022-08-21T13:56:59.613608Z","iopub.status.idle":"2022-08-21T13:57:11.312369Z","shell.execute_reply.started":"2022-08-21T13:56:59.613450Z","shell.execute_reply":"2022-08-21T13:57:11.310366Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport os\nimport cv2, random\nimport matplotlib.pyplot as plt\nimport json\nfrom tqdm import tqdm\nfrom tifffile import imread\nfrom glob import glob\n\nimport torch\nimport torch.nn as nn\nimport torch.optim as optim\nimport torch.nn.functional as F\nfrom torch.utils.data import Dataset, DataLoader\n\nimport torchvision\nimport albumentations as A\nfrom albumentations.pytorch.transforms import ToTensorV2\n\nfrom sklearn.model_selection import train_test_split\n\nDEVICE = 'cuda' if torch.cuda.is_available() else 'cpu'","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2022-08-21T13:57:11.314779Z","iopub.execute_input":"2022-08-21T13:57:11.315676Z","iopub.status.idle":"2022-08-21T13:57:13.074603Z","shell.execute_reply.started":"2022-08-21T13:57:11.315635Z","shell.execute_reply":"2022-08-21T13:57:13.073252Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Load Data","metadata":{}},{"cell_type":"code","source":"root = '../input/hubmap-organ-segmentation'\n\ntrain_df =  pd.read_csv(os.path.join(root, 'train.csv'))\ntest_df =  pd.read_csv(os.path.join(root, 'test.csv'))\nsubmission = pd.read_csv(os.path.join(root, 'sample_submission.csv'))","metadata":{"execution":{"iopub.status.busy":"2022-08-21T13:57:13.078449Z","iopub.execute_input":"2022-08-21T13:57:13.079425Z","iopub.status.idle":"2022-08-21T13:57:13.242771Z","shell.execute_reply.started":"2022-08-21T13:57:13.079392Z","shell.execute_reply":"2022-08-21T13:57:13.241380Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_df","metadata":{"execution":{"iopub.status.busy":"2022-08-21T13:57:13.247766Z","iopub.execute_input":"2022-08-21T13:57:13.248241Z","iopub.status.idle":"2022-08-21T13:57:13.285646Z","shell.execute_reply.started":"2022-08-21T13:57:13.248207Z","shell.execute_reply":"2022-08-21T13:57:13.283958Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_df","metadata":{"execution":{"iopub.status.busy":"2022-08-21T13:57:13.288039Z","iopub.execute_input":"2022-08-21T13:57:13.288561Z","iopub.status.idle":"2022-08-21T13:57:13.303263Z","shell.execute_reply.started":"2022-08-21T13:57:13.288502Z","shell.execute_reply":"2022-08-21T13:57:13.301662Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"submission","metadata":{"execution":{"iopub.status.busy":"2022-08-21T13:57:13.305789Z","iopub.execute_input":"2022-08-21T13:57:13.306861Z","iopub.status.idle":"2022-08-21T13:57:13.322408Z","shell.execute_reply.started":"2022-08-21T13:57:13.306819Z","shell.execute_reply":"2022-08-21T13:57:13.320111Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Check Data","metadata":{}},{"cell_type":"code","source":"def rle_to_mask(rle, shape):\n    s = rle.split()\n    starts, lengths = [np.array(x, dtype = int) for x in (s[0:][::2], s[1:][::2])]\n    starts -= 1\n    ends = starts + lengths\n    img = np.zeros(shape[0] * shape[1], dtype = np.float32)\n    for lo, hi in zip(starts, ends):\n        img[lo:hi] = 1\n    return img.reshape(shape).T","metadata":{"execution":{"iopub.status.busy":"2022-08-21T13:57:13.323745Z","iopub.execute_input":"2022-08-21T13:57:13.324100Z","iopub.status.idle":"2022-08-21T13:57:13.334806Z","shell.execute_reply.started":"2022-08-21T13:57:13.324052Z","shell.execute_reply":"2022-08-21T13:57:13.333421Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"no_imgs = 5\nfor i in range(no_imgs):\n    idx = random.randint(0, len(train_df) - 1)\n    img_id = train_df['id'][idx]\n    img_path = os.path.join(root, f'train_images/{img_id}.tiff')\n    shape = (train_df['img_width'][idx], train_df['img_height'][idx])\n    plt.imshow(cv2.imread(img_path), )\n    plt.imshow(rle_to_mask(train_df['rle'][idx], shape), alpha = 0.5)\n    plt.show()","metadata":{"execution":{"iopub.status.busy":"2022-08-21T13:57:13.336959Z","iopub.execute_input":"2022-08-21T13:57:13.338609Z","iopub.status.idle":"2022-08-21T13:57:23.839362Z","shell.execute_reply.started":"2022-08-21T13:57:13.338567Z","shell.execute_reply":"2022-08-21T13:57:23.837856Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Compare RLE vs JSON Annotations","metadata":{}},{"cell_type":"code","source":"idx = random.randint(0, len(train_df) - 1)\nimg_id = train_df['id'][idx]\nimg_path = os.path.join(root, f'train_images/{img_id}.tiff')\nannot_path = os.path.join(root, f'train_annotations/{img_id}.json')\nshape = (train_df['img_width'][idx], train_df['img_height'][idx])\npolygon = json.load(open(annot_path))\n\npoly_img = np.zeros(shape)\nfor i in range(len(polygon)):\n    poly_img = cv2.fillPoly(poly_img, pts = [np.array(polygon[i])], color = (255,255,255))\n\nplt.title('RLE')\nplt.imshow(imread(img_path))\nplt.imshow(rle_to_mask(train_df['rle'][idx], shape), alpha = 0.5)\nplt.show()\n\nplt.title('Polygon')\nplt.imshow(imread(img_path))\nplt.imshow(poly_img, alpha = 0.5)\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2022-08-21T13:57:23.841195Z","iopub.execute_input":"2022-08-21T13:57:23.842722Z","iopub.status.idle":"2022-08-21T13:57:28.473398Z","shell.execute_reply.started":"2022-08-21T13:57:23.842678Z","shell.execute_reply":"2022-08-21T13:57:28.472043Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"imread(img_path).shape","metadata":{"execution":{"iopub.status.busy":"2022-08-21T13:57:28.475597Z","iopub.execute_input":"2022-08-21T13:57:28.476458Z","iopub.status.idle":"2022-08-21T13:57:28.497884Z","shell.execute_reply.started":"2022-08-21T13:57:28.476409Z","shell.execute_reply":"2022-08-21T13:57:28.496686Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Split Data","metadata":{}},{"cell_type":"code","source":"train_data, val_data = train_test_split(train_df, test_size = 0.2, random_state = 42, stratify = train_df['organ'])\ntrain_data = train_data.reset_index(drop = True)\nval_data = val_data.reset_index(drop = True)\nlen(train_data), len(val_data)","metadata":{"execution":{"iopub.status.busy":"2022-08-21T13:57:28.499470Z","iopub.execute_input":"2022-08-21T13:57:28.500293Z","iopub.status.idle":"2022-08-21T13:57:28.517936Z","shell.execute_reply.started":"2022-08-21T13:57:28.500248Z","shell.execute_reply":"2022-08-21T13:57:28.516446Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Data Preparation","metadata":{}},{"cell_type":"code","source":"resize_transform = A.Compose([\n    A.Resize(512, 512),\n    A.Normalize(),\n    ToTensorV2(),\n])","metadata":{"execution":{"iopub.status.busy":"2022-08-21T13:57:28.521627Z","iopub.execute_input":"2022-08-21T13:57:28.522111Z","iopub.status.idle":"2022-08-21T13:57:28.528980Z","shell.execute_reply.started":"2022-08-21T13:57:28.522055Z","shell.execute_reply":"2022-08-21T13:57:28.527612Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class HuBMAP_data(Dataset):\n    def __init__(self, df, is_train = True, transform = None):\n        super().__init__()\n        self.df = df\n        self.is_train = is_train\n        self.transform = transform\n        \n    def __len__(self):\n        return len(self.df)\n    \n    def __getitem__(self, idx):\n        img_id = self.df['id'][idx]\n        shape = (self.df['img_width'][idx], self.df['img_height'][idx])\n\n        if self.is_train:\n            img_path = os.path.join(root, f'train_images/{img_id}.tiff')\n            mask = rle_to_mask(self.df['rle'][idx], shape)\n        else:\n            img_path = os.path.join(root, f'test_images/{img_id}.tiff')\n        \n        img = imread(img_path)\n        \n        if self.transform is not None:\n            if self.is_train:\n                transformed = self.transform(image = img, mask = mask)\n                img = transformed['image']\n                mask = transformed['mask']\n            else:\n                transformed = self.transform(image = img)\n                img = transformed['image']\n                \n        if self.is_train:\n            return img, mask\n        else:\n            return shape, img","metadata":{"execution":{"iopub.status.busy":"2022-08-21T14:31:58.526606Z","iopub.execute_input":"2022-08-21T14:31:58.527360Z","iopub.status.idle":"2022-08-21T14:31:58.543863Z","shell.execute_reply.started":"2022-08-21T14:31:58.527303Z","shell.execute_reply":"2022-08-21T14:31:58.542213Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_dataset = HuBMAP_data(train_data, transform = resize_transform)\nval_dataset = HuBMAP_data(val_data, transform = resize_transform)\ntest_dataset = HuBMAP_data(test_df, is_train = False, transform = resize_transform)","metadata":{"execution":{"iopub.status.busy":"2022-08-21T14:32:01.988192Z","iopub.execute_input":"2022-08-21T14:32:01.988639Z","iopub.status.idle":"2022-08-21T14:32:01.997256Z","shell.execute_reply.started":"2022-08-21T14:32:01.988605Z","shell.execute_reply":"2022-08-21T14:32:01.995462Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# U-Net Modeling\n\n<img src = 'https://lmb.informatik.uni-freiburg.de/people/ronneber/u-net/u-net-architecture.png'>","metadata":{}},{"cell_type":"code","source":"class double_conv(nn.Module):\n    def __init__(self, in_channel, out_channel):\n        super(double_conv, self).__init__()\n        self.conv = nn.Sequential(\n            nn.Conv2d(in_channel, out_channel, kernel_size = 3, padding = 'same'),\n            nn.BatchNorm2d(out_channel),\n            nn.ReLU(),\n            nn.Conv2d(out_channel, out_channel, kernel_size = 3, padding = 'same'),\n            nn.BatchNorm2d(out_channel),\n            nn.ReLU(),\n        )\n        \n    def forward(self, inputs):\n        out = self.conv(inputs)\n        return out\n    \nclass down_conv(nn.Module):\n    def __init__(self, in_channel, out_channel):\n        super(down_conv, self).__init__()\n        self.conv = nn.Sequential(nn.MaxPool2d(2), double_conv(in_channel, out_channel))\n        \n    def forward(self, inputs):\n        out = self.conv(inputs)\n        \n#         print(f'input size = {inputs.size()}, output size = {out.size()}')\n        \n        return out\n        \nclass up_concat(nn.Module):\n    def __init__(self, in_channel):\n        super(up_concat, self).__init__()\n        self.up = nn.ConvTranspose2d(in_channel, in_channel // 2, 2, 2)\n        \n    def forward(self, x, bypass):\n        x = self.up(x)\n        \n        diff_X = (bypass.size()[2] - x.size()[2]) // 2\n        diff_Y = (bypass.size()[3] - x.size()[3]) // 2\n        bypass = F.pad(bypass, (-diff_X, -diff_X, -diff_Y, -diff_Y))\n        \n#         print(f'X size = {x.size()}, Bypass size = {bypass.size()}')\n        \n        out = torch.cat([x, bypass], dim = 1)\n        return out\n        \nclass up_conv(nn.Module):\n    def __init__(self, in_channel):\n        super(up_conv, self).__init__()\n        self.up_concat = up_concat(in_channel)\n        self.double_conv = double_conv(in_channel, in_channel // 2)\n        \n    def forward(self, x, bypass):\n        x = self.up_concat(x, bypass)\n        out = self.double_conv(x)\n        return out\n        \nclass UNet(nn.Module):\n    def __init__(self, in_channel, out_class):\n        super(UNet, self).__init__()\n        self.inconv = double_conv(in_channel, 64)\n        self.down_conv1 = down_conv(64, 128)\n        self.down_conv2 = down_conv(128, 256)\n        self.down_conv3 = down_conv(256, 512)\n        self.down_conv4 = down_conv(512, 1024)\n        \n        self.up1 = up_conv(1024)\n        self.up2 = up_conv(512)\n        self.up3 = up_conv(256)\n        self.up4 = up_conv(128)\n        self.outconv = nn.Conv2d(64, out_class, 1, 1)\n        \n    def forward(self, inputs):\n        x1 = self.inconv(inputs)\n        x2 = self.down_conv1(x1)\n        x3 = self.down_conv2(x2)\n        x4 = self.down_conv3(x3)\n        x5 = self.down_conv4(x4)\n        \n        x = self.up1(x5, x4)\n        x = self.up2(x, x3)\n        x = self.up3(x, x2)\n        x = self.up4(x, x1)\n        out = self.outconv(x)\n        \n        return torch.sigmoid(out)","metadata":{"execution":{"iopub.status.busy":"2022-08-21T13:57:28.567737Z","iopub.execute_input":"2022-08-21T13:57:28.568261Z","iopub.status.idle":"2022-08-21T13:57:28.598909Z","shell.execute_reply.started":"2022-08-21T13:57:28.568220Z","shell.execute_reply":"2022-08-21T13:57:28.597290Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Dice Loss\n<img src = \"https://i.stack.imgur.com/OsH4y.png\">","metadata":{}},{"cell_type":"code","source":"class DiceLoss(nn.Module):\n    def __init__(self):\n        super(DiceLoss, self).__init__()\n        \n    def forward(self, x, y, smooth = 1):\n        x = torch.sigmoid(x)\n        \n#         print(f'img size = {x.size()}, mask size = {y.size()}')\n        \n        x = x.view(-1)\n        y = y.view(-1)\n        \n        intersection = (x * y).sum()\n        dice = (2 * intersection + smooth) / (x.sum() + y.sum() + smooth)\n        \n        return 1 - dice","metadata":{"execution":{"iopub.status.busy":"2022-08-21T13:57:28.601025Z","iopub.execute_input":"2022-08-21T13:57:28.601867Z","iopub.status.idle":"2022-08-21T13:57:28.616440Z","shell.execute_reply.started":"2022-08-21T13:57:28.601822Z","shell.execute_reply":"2022-08-21T13:57:28.614747Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Metrics","metadata":{}},{"cell_type":"code","source":"# def DiceCoef():\n    \n# def IoU():\n    ","metadata":{"execution":{"iopub.status.busy":"2022-08-21T13:57:28.619011Z","iopub.execute_input":"2022-08-21T13:57:28.619636Z","iopub.status.idle":"2022-08-21T13:57:28.627844Z","shell.execute_reply.started":"2022-08-21T13:57:28.619588Z","shell.execute_reply":"2022-08-21T13:57:28.625560Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Train Models","metadata":{}},{"cell_type":"code","source":"N_EPOCH = 40\nBATCH_SIZE = 8\nLR = 0.001","metadata":{"execution":{"iopub.status.busy":"2022-08-21T13:57:28.630585Z","iopub.execute_input":"2022-08-21T13:57:28.631380Z","iopub.status.idle":"2022-08-21T13:57:28.643193Z","shell.execute_reply.started":"2022-08-21T13:57:28.631337Z","shell.execute_reply":"2022-08-21T13:57:28.639837Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_loader = DataLoader(train_dataset, batch_size = BATCH_SIZE, shuffle = True)\nval_loader = DataLoader(val_dataset, batch_size = BATCH_SIZE, shuffle = False)","metadata":{"execution":{"iopub.status.busy":"2022-08-21T13:57:28.645846Z","iopub.execute_input":"2022-08-21T13:57:28.646276Z","iopub.status.idle":"2022-08-21T13:57:28.656097Z","shell.execute_reply.started":"2022-08-21T13:57:28.646246Z","shell.execute_reply":"2022-08-21T13:57:28.654202Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model = UNet(3, 1).to(DEVICE)\nloss_fn = DiceLoss().to(DEVICE)\noptimizer = optim.Adam(model.parameters(), lr = LR ,weight_decay = 1e-5)\ntotal_batch = len(train_loader)","metadata":{"execution":{"iopub.status.busy":"2022-08-21T13:57:28.662083Z","iopub.execute_input":"2022-08-21T13:57:28.665681Z","iopub.status.idle":"2022-08-21T13:57:31.317430Z","shell.execute_reply.started":"2022-08-21T13:57:28.665573Z","shell.execute_reply":"2022-08-21T13:57:31.315788Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"best_loss = 99999999999\n\nweights_path = 'best.h5'\n\nfor epoch in range(N_EPOCH):\n    avg_cost = 0\n    pbar = tqdm(train_loader)\n    model.train()\n    for (img, mask) in pbar:\n        img = img.to(DEVICE)\n        mask = mask.to(DEVICE)\n\n        optimizer.zero_grad()\n        hypothesis = model(img)\n\n        cost = loss_fn(hypothesis, mask)\n        cost.backward()\n        optimizer.step()\n        \n        avg_cost += cost / total_batch\n    \n    model.eval()\n    with torch.no_grad():\n        val_loss = 0\n        for (img, mask) in val_loader:\n            img = img.to(DEVICE)\n            mask = mask.to(DEVICE)\n            \n            hypothesis = model(img)\n            batch_cost = loss_fn(hypothesis, mask)\n            val_loss += batch_cost / total_batch\n            \n    if val_loss < best_loss:\n        print(f'val_loss improved {best_loss:.4f} to {val_loss:.4f}')\n        best_loss = val_loss\n        torch.save(model.state_dict(), weights_path)\n\n    print(f'[Epoch {epoch + 1} / {N_EPOCH}] train_loss = {avg_cost:.4f}, val_loss = {val_loss:.4f}')","metadata":{"execution":{"iopub.status.busy":"2022-08-21T14:02:28.468473Z","iopub.execute_input":"2022-08-21T14:02:28.468897Z","iopub.status.idle":"2022-08-21T14:26:44.249168Z","shell.execute_reply.started":"2022-08-21T14:02:28.468850Z","shell.execute_reply":"2022-08-21T14:26:44.247568Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model.load_state_dict(torch.load(weights_path))","metadata":{"execution":{"iopub.status.busy":"2022-08-21T14:28:21.057625Z","iopub.execute_input":"2022-08-21T14:28:21.058113Z","iopub.status.idle":"2022-08-21T14:28:21.246979Z","shell.execute_reply.started":"2022-08-21T14:28:21.058078Z","shell.execute_reply":"2022-08-21T14:28:21.245306Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def mask_to_rle(mask):\n    '''\n    img: numpy array, 1 - mask, 0 - background\n    Returns run length as string formated\n    This simplified method requires first and last pixel to be zero\n    '''\n    pixels = mask.T.flatten()\n    \n    # This simplified method requires first and last pixel to be zero\n    pixels[0] = 0\n    pixels[-1] = 0\n    runs = np.where(pixels[1:] != pixels[:-1])[0] + 2\n    runs[1::2] -= runs[::2]\n    \n    return ' '.join(str(x) for x in runs)","metadata":{"execution":{"iopub.status.busy":"2022-08-21T14:26:44.252680Z","iopub.execute_input":"2022-08-21T14:26:44.253980Z","iopub.status.idle":"2022-08-21T14:26:44.262351Z","shell.execute_reply.started":"2022-08-21T14:26:44.253933Z","shell.execute_reply":"2022-08-21T14:26:44.260670Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_loader = DataLoader(test_dataset, batch_size = BATCH_SIZE, shuffle = False)\nthreshold = 0.5\n\nmodel.eval()\nwith torch.no_grad():\n    preds = []\n    for (width, height), img in test_loader:\n        width = width.numpy()[0]\n        height = height.numpy()[0]\n        \n        img = img.to(DEVICE)\n        \n        \n        pred = model(img).cpu().numpy()\n        pred = (pred.transpose((0,2,3,1)) > threshold).astype(int)\n\n        for mask in pred:\n            m = cv2.resize(mask, (width, height), interpolation=cv2.INTER_NEAREST)\n            rle = mask_to_rle(m)\n            preds.append(rle)","metadata":{"execution":{"iopub.status.busy":"2022-08-21T14:36:18.512540Z","iopub.execute_input":"2022-08-21T14:36:18.512939Z","iopub.status.idle":"2022-08-21T14:36:18.622309Z","shell.execute_reply.started":"2022-08-21T14:36:18.512902Z","shell.execute_reply":"2022-08-21T14:36:18.620962Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plt.imshow(rle_to_mask(preds[0], (2023, 2023)))","metadata":{"execution":{"iopub.status.busy":"2022-08-21T14:37:18.701470Z","iopub.execute_input":"2022-08-21T14:37:18.701927Z","iopub.status.idle":"2022-08-21T14:37:19.292651Z","shell.execute_reply.started":"2022-08-21T14:37:18.701860Z","shell.execute_reply":"2022-08-21T14:37:19.291106Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"submission['rle'] = preds\nsubmission_path = 'submission.csv'\n\nsubmission.to_csv(submission_path, index = False)\n\npd.read_csv(submission_path)","metadata":{"execution":{"iopub.status.busy":"2022-08-21T14:38:44.557911Z","iopub.execute_input":"2022-08-21T14:38:44.559122Z","iopub.status.idle":"2022-08-21T14:38:44.582489Z","shell.execute_reply.started":"2022-08-21T14:38:44.559036Z","shell.execute_reply":"2022-08-21T14:38:44.581164Z"},"trusted":true},"execution_count":null,"outputs":[]}]}