{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"> # 👨‍💻Sartourius Cell Instance Segmentation with UNet👨‍💻\n\n                                       🤖Training Version🤖\n","metadata":{}},{"cell_type":"code","source":"!pip install wandb\nimport wandb\n# Wandb Login\nwandb.login()\n# wandb config\n#WANDB_CONFIG = {'competition': 'Sartorius', '_wandb_kernel': 'neuracort', 'entity':\"kohyun1207\"}\nrun = wandb.init(project='sartorius-unet')","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#Libarary\nimport pandas as pd\nimport numpy as np\nimport os\nfrom tqdm import tqdm\nimport cv2\nimport matplotlib.pyplot as plt\nfrom PIL import Image\nimport random\nimport skimage.morphology\nimport warnings\nwarnings.filterwarnings(action='ignore')\n\n#Torch\nimport torch\nimport torch.nn as nn\nfrom torch.utils.data import Dataset, DataLoader\nimport torchvision\nimport torchvision.transforms as T\nimport torch.nn.functional as F\n\n#Initializing All seed\nseed = 42\nnp.random.seed(seed)\nrandom.seed(seed)\ntorch.manual_seed(seed)\ntorch.cuda.manual_seed(seed)\n# When running on the CuDNN backend, two further options must be set\ntorch.backends.cudnn.deterministic = True\ntorch.backends.cudnn.benchmark = False\n# Set a fixed value for the hash seed\nos.environ['PYTHONHASHSEED'] = str(seed)\nprint('> SEEDING DONE')\n\n#Groupping Annotation by 'ID'\ntrain_info = pd.read_csv('../input/sartorius-cell-instance-segmentation/train.csv')\ndf = train_info.groupby('id').agg(list).reset_index()\nfor col in df.columns[2:]:\n    df[col] = df[col].apply(lambda x: np.unique(x)[0] if len(np.unique(x)) == 1 else np.unique(x))\ndf.head(5)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"> # Utils Functions🔨\n\n1. Decoder : function to decode to 2d array (height, width) from 1d array\n\n2. Visulization : Check the each image druing training (input, pred, actual)","metadata":{}},{"cell_type":"code","source":"#Utils\ndef visualization(photo, predict, actual):\n    idx = np.random.randint(0, len(df))\n    photo = np.transpose(photo[0].cpu().numpy(), (1,2,0))\n    predict = np.transpose(predict[0].cpu().numpy(), (1,2,0))\n    actual = np.transpose(actual[0].cpu().numpy(), (1,2,0))\n    plt.figure(figsize = (10,10))\n    plt.subplot(1,3,1)\n    plt.title('Photo (X)')\n    plt.imshow(photo)\n    plt.tight_layout()\n    plt.tick_params(left = False, bottom = False, labelleft = False, labelbottom = False)\n    plt.subplot(1,3,2)\n    plt.title('Prdict(y_hat)')\n    plt.imshow(predict)\n    plt.tight_layout()\n    plt.tick_params(left = False, bottom = False, labelleft = False, labelbottom = False)\n    plt.subplot(1,3,3)\n    plt.title('Actual(yt)')\n    plt.imshow(actual)\n    plt.tight_layout()\n    plt.tick_params(left = False, bottom = False, labelleft = False, labelbottom = False)\n    wandb.log({\"chart\": plt})\n    #plt.show()\n\ndef rle_decode(mask_rle, shape, color=1):\n    '''\n    mask_rle: run-length as string formated (start length)\n    shape: (height,width) of array to return \n    Returns numpy array, 1 - mask, 0 - background\n\n    '''\n    s = mask_rle.split()\n    starts, lengths = [np.asarray(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] = color\n    return img.reshape(shape)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"> # IOU Score\n\nCheck IOU Score btw prediction ↔️ actual to figure out the best Thresh hold","metadata":{}},{"cell_type":"markdown","source":"![](https://www.pyimagesearch.com/wp-content/uploads/2016/09/iou_examples.png)","metadata":{}},{"cell_type":"code","source":"#https://www.kaggle.com/theoviel/competition-metric-map-iou\ndef compute_iou(labels, y_pred):\n    \"\"\"\n    Computes the IoU for instance labels and predictions.\n\n    Args:\n        labels (np array): Labels.\n        y_pred (np array): predictions\n\n    Returns:\n        np array: IoU matrix, of size true_objects x pred_objects.\n    \"\"\"\n\n    true_objects = len(np.unique(labels))\n    pred_objects = len(np.unique(y_pred))\n\n    # Compute intersection between all objects\n    intersection = np.histogram2d(\n        labels.flatten(), y_pred.flatten(), bins=(true_objects, pred_objects)\n    )[0]\n\n    # Compute areas (needed for finding the union between all objects)\n    area_true = np.histogram(labels, bins=true_objects)[0]\n    area_pred = np.histogram(y_pred, bins=pred_objects)[0]\n    area_true = np.expand_dims(area_true, -1)\n    area_pred = np.expand_dims(area_pred, 0)\n\n    # Compute union\n    union = area_true + area_pred - intersection\n    iou = intersection / union\n    \n    return iou[1:, 1:]  # exclude background\n\ndef precision_at(threshold, iou):\n    \"\"\"\n    Computes the precision at a given threshold.\n\n    Args:\n        threshold (float): Threshold.\n        iou (np array [n_truths x n_preds]): IoU matrix.\n\n    Returns:\n        int: Number of true positives,\n        int: Number of false positives,\n        int: Number of false negatives.\n    \"\"\"\n    matches = iou > threshold\n    true_positives = np.sum(matches, axis=1) >= 1  # Correct objects\n    false_negatives = np.sum(matches, axis=1) == 0  # Missed objects\n    false_positives = np.sum(matches, axis=0) == 0  # Extra objects\n    tp, fp, fn = (\n        np.sum(true_positives),\n        np.sum(false_positives),\n        np.sum(false_negatives),\n    )\n    return tp, fp, fn\n\ndef iou_map(truths, preds, verbose=0):\n    \"\"\"\n    Computes the metric for the competition.\n    Masks contain the segmented pixels where each object has one value associated,\n    and 0 is the background.\n\n    Args:\n        truths (list of masks): Ground truths.\n        preds (list of masks): Predictions.\n        verbose (int, optional): Whether to print infos. Defaults to 0.\n\n    Returns:\n        float: mAP.\n    \"\"\"\n    ious = [compute_iou(truth, pred) for truth, pred in zip(truths, preds)]\n    \n    print(ious[0].shape)\n\n    if verbose:\n        print(\"Thresh\\tTP\\tFP\\tFN\\tPrec.\")\n\n    prec = []\n    for t in np.arange(0.5, 1.0, 0.05):\n        tps, fps, fns = 0, 0, 0\n        for iou in ious:\n            tp, fp, fn = precision_at(t, iou)\n            tps += tp\n            fps += fp\n            fns += fn\n\n        p = tps / (tps + fps + fns)\n        prec.append(p)\n\n        if verbose:\n            print(\"{:1.3f}\\t{}\\t{}\\t{}\\t{:1.3f}\".format(t, tps, fps, fns, p))\n\n    if verbose:\n        print(\"AP\\t-\\t-\\t-\\t{:1.3f}\".format(np.mean(prec)))\n\n    return np.mean(prec)\n\n#iou_map([masks] * 5, [masks] * 5, verbose=1)  # This should score 1","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"> # **Load Dataset**\n","metadata":{}},{"cell_type":"code","source":"class cumstom_nomalize(object):\n    def __call__(self, inputs):\n        mean = 0.5 \n        std = 0.5\n        inputs = ((inputs * mean) / std)\n        return inputs\n\n\ntrain_transforms = T.Compose([T.Resize((224,224)),\n                             T.RandomHorizontalFlip(p = 0.5),\n                             T.RandomVerticalFlip(p = 0.5),\n                             T.ToTensor(),\n                             cumstom_nomalize()])\n\nvalid_transforms = T.Compose([T.Resize((224, 224)), T.ToTensor(), cumstom_nomalize()])","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class CustomDataset(Dataset):\n    def __init__(self, df ,transforms = None, seed = None):\n        self.df = df\n        self.transforms = transforms\n        self.seed = seed\n\n    def __len__(self):\n        return len(self.df)\n\n    def __getitem__(self, idx):\n        path = '../input/sartorius-cell-instance-segmentation/train/{}.png'.format(self.df['id'].iloc[idx])\n        inputs = Image.open(path).convert('RGB')\n        labels = self.df['annotation'].iloc[idx]\n        mask = np.zeros((520, 704))\n        for label in labels:\n                mask += rle_decode(label, shape=(520, 704))\n        mask = mask.clip(0, 1)\n        mask = Image.fromarray(np.float32(mask))\n        \n        if self.transforms:\n            torch.manual_seed(self.seed)\n            inputs = self.transforms(inputs)\n        if self.transforms:\n            torch.manual_seed(self.seed)\n            mask = self.transforms(mask)\n\n        return inputs, mask","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"> # **UNet architecture**","metadata":{}},{"cell_type":"code","source":"##Network (Unet)\nimport torch.nn as nn\n\nclass UNet(nn.Module):\n    def __init__(self):\n        super(UNet, self).__init__()\n        \n        def CBR(in_channels, out_channels, kernel_size = 3, stride = 1, padding = 1, bias = True):\n            layers = []\n            layers += [nn.Conv2d(in_channels= in_channels, out_channels =out_channels,\n                                 kernel_size = kernel_size, stride = stride , padding = padding, bias = bias)]\n            layers += [nn.BatchNorm2d(num_features = out_channels)]\n            layers += [nn.ReLU()]\n            \n            return nn.Sequential(*layers)\n        \n        self.step1_1 = CBR(in_channels = 3, out_channels = 64)\n        self.step1_2 = CBR(in_channels = 64, out_channels = 64)\n        self.pool1 = nn.MaxPool2d(2)\n        self.step2_1 = CBR(in_channels = 64, out_channels = 128)\n        self.step2_2 = CBR(in_channels = 128, out_channels = 128)\n        self.pool2 = nn.MaxPool2d(2)\n        self.step3_1 = CBR(in_channels = 128, out_channels = 256)\n        self.step3_2 = CBR(in_channels = 256, out_channels = 256)\n        self.pool3 = nn.MaxPool2d(2)\n        self.step4_1 = CBR(in_channels = 256, out_channels = 512)\n        self.step4_2 = CBR(in_channels = 512, out_channels = 512)\n        self.pool4 = nn.MaxPool2d(2)\n        self.step5_1 = CBR(in_channels = 512, out_channels = 1024)\n        \n        self.rstep5_1 = CBR(in_channels = 1024, out_channels = 512)\n        self.unpool4 = nn.ConvTranspose2d(in_channels = 512, out_channels = 512, kernel_size = 2, stride = 2, padding = 0, bias = True)\n        self.rstep4_2 = CBR(in_channels = 512 * 2 , out_channels = 512)\n        self.rstep4_1 = CBR(in_channels = 512, out_channels = 256)\n        self.unpool3 = nn.ConvTranspose2d(in_channels = 256, out_channels = 256, kernel_size = 2, stride = 2, padding = 0, bias = True)\n        self.rstep3_2 = CBR(in_channels = 256 * 2, out_channels = 256)\n        self.rstep3_1 = CBR(in_channels = 256, out_channels = 128)\n        self.unpool2 = nn.ConvTranspose2d(in_channels = 128, out_channels = 128, kernel_size = 2, stride = 2, padding = 0, bias = True)\n        self.rstep2_2 = CBR(in_channels = 128 * 2, out_channels = 128)\n        self.rstep2_1 = CBR(in_channels = 128, out_channels = 64)\n        self.unpool1 = nn.ConvTranspose2d(in_channels = 64, out_channels = 64, kernel_size = 2, stride = 2, padding = 0, bias = True)\n        self.rstep1_2 = CBR(in_channels = 64 * 2, out_channels = 64)\n        self.rstep1_1 = CBR(in_channels = 64, out_channels = 64)\n        \n        self.fc = nn.Conv2d(in_channels = 64, out_channels = 1, kernel_size = 1, stride = 1, padding = 0, bias = True)\n        \n    def forward(self, x):\n        step1_1 = self.step1_1(x)\n        step1_2 = self.step1_2(step1_1)\n        pool1 = self.pool1(step1_2)\n        \n        step2_1 = self.step2_1(pool1)\n        step2_2 = self.step2_2(step2_1)\n        pool2 = self.pool2(step2_2)\n        \n        step3_1 = self.step3_1(pool2)\n        step3_2 = self.step3_2(step3_1)\n        pool3 = self.pool3(step3_2)\n        \n        step4_1 = self.step4_1(pool3)\n        step4_2 = self.step4_2(step4_1)\n        pool4 = self.pool4(step4_2)\n        \n        step5_1 = self.step5_1(pool4)\n        \n        rstep5_1 = self.rstep5_1(step5_1)\n        \n        unpool4 = self.unpool4(rstep5_1)\n        cat4 = torch.cat((unpool4, step4_2), dim = 1)\n        rstep4_2 = self.rstep4_2(cat4)\n        rstep4_1 = self.rstep4_1(rstep4_2)\n        \n        unpool3 = self.unpool3(rstep4_1)\n        cat3 = torch.cat((step3_2, unpool3), dim = 1)\n        rstep3_2 = self.rstep3_2(cat3)\n        rstep3_1 = self.rstep3_1(rstep3_2)\n        \n        unpool2 = self.unpool2(rstep3_1)\n        cat2 = torch.cat((step2_2, unpool2), dim = 1)\n        rstep2_2 = self.rstep2_2(cat2)\n        rstep2_1 = self.rstep2_1(rstep2_2)\n        \n        unpool1 = self.unpool1(rstep2_1)\n        cat1 = torch.cat((step1_2, unpool1), dim = 1)\n        rstep1_2 = self.rstep1_2(cat1)\n        rstep1_1 = self.rstep1_1(rstep1_2)\n        output = self.fc(rstep1_1)\n        \n        return output","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"> # Training\n\n**Basic option ....**\n1. lr = 0.001\n2. early_stopping = 10\n3. batch_size = 32\n4. Loss Function = BCEWithLogitsloss\n5. Optimizer = Adam","metadata":{}},{"cell_type":"code","source":"def dice_loss(input, target):\n    input = torch.sigmoid(input)\n    smooth = 1.0\n    iflat = input.view(-1)\n    tflat = target.view(-1)\n    intersection = (iflat * tflat).sum()\n    return ((2.0 * intersection + smooth) / (iflat.sum() + tflat.sum() + smooth))","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class FocalLoss(nn.Module):\n    def __init__(self, gamma):\n        super().__init__()\n        self.gamma = gamma\n\n    def forward(self, input, target):\n        if not (target.size() == input.size()):\n            raise ValueError(\"Target size ({}) must be the same as input size ({})\"\n                             .format(target.size(), input.size()))\n        max_val = (-input).clamp(min=0)\n        loss = input - input * target + max_val + \\\n            ((-max_val).exp() + (-input - max_val).exp()).log()\n        invprobs = F.logsigmoid(-input * (target * 2.0 - 1.0))\n        loss = (invprobs * self.gamma).exp() * loss\n        return loss.mean()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class MixedLoss(nn.Module):\n    def __init__(self, alpha, gamma):\n        super().__init__()\n        self.alpha = alpha\n        self.focal = FocalLoss(gamma)\n\n    def forward(self, input, target):\n        loss = self.alpha*self.focal(input, target) - torch.log(dice_loss(input, target))\n        return loss.mean()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#Training Configure\n#basic option\ndevice = 'cuda' if torch.cuda.is_available() else 'cpu'\n#set seed\nimport random\nrandom.seed(777)\ntorch.manual_seed(777)\nif device == 'cuda':\n    torch.cuda.manual_seed_all(777)\n\n#Defineing UNet\n\n\nmodel = UNet()\njacard_p = 2\nlr = 1e-3\nsetting_patience = 20\nn_epoch = 250\nbatch_size = 16\n#optimizer & loss functions\ncriterion = MixedLoss(10.0, 2.0)\noptimizer = torch.optim.SGD(params = model.parameters(), lr = lr)\n#Data Split\nfrom sklearn.model_selection import train_test_split\nX_train, X_test = train_test_split(df, test_size = 0.33, random_state = 777, shuffle = False)\nprint(X_train.shape, X_test.shape)\n#Data defining\ntrain_dataset = CustomDataset(df = X_train, transforms = train_transforms, seed = 777)\nvalid_dataset = CustomDataset(df = X_test, transforms = valid_transforms, seed = 777) \ntrain_dataloader = DataLoader(train_dataset, batch_size = batch_size, shuffle = True)\nvalid_dataloader = DataLoader(valid_dataset, batch_size = batch_size, shuffle = True)\ntrain_total_batch = len(train_dataloader)\nvalid_total_batch = len(valid_dataloader)\ntrn_loss_list = []\ntest_loss_list = []","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#Training & Evaluation\ntorch.cuda.empty_cache()\nbest_loss = 100\ntotal_patience = 0\nfor epoch in range(n_epoch):\n    model.train()    \n    model.cuda()\n    optimizer.zero_grad()\n    trn_avg_loss = 0\n    trn_avg_iou = 0\n    test_avg_loss = 0\n    with tqdm(train_dataloader, unit = 'batch') as train_bar:\n        for image, label in train_bar:  \n            image = image.float().cuda()\n            label = label.float().cuda()\n            trn_output = model(image)\n            iou_score = iou_map([np.transpose(trn_output[0].cpu().detach().numpy(), (1,2,0))], [np.transpose(label[0].cpu().detach().numpy(), (1,2,0))])\n            loss = criterion(label,trn_output)\n            loss.backward()\n            optimizer.step()\n            trn_avg_loss += loss / train_total_batch\n            trn_avg_iou += iou_score / train_total_batch\n            train_bar.set_postfix(epoch = epoch+1, loss = loss.item(), iou_score = trn_avg_iou)\n    model.eval()\n    with torch.no_grad():\n        with tqdm(valid_dataloader, unit = 'batch') as test_bar:\n            for image, label in test_bar:  \n                image = image.float().cuda()\n                label = label.float().cuda()\n                test_output = model(image)\n                loss = criterion(label, test_output)\n                test_avg_loss += loss / valid_total_batch\n                test_bar.set_postfix(epoch = epoch+1, loss = loss.item())\n    wandb.log({'Epoch' : epoch, \"Validation loss\": test_avg_loss, \"Train loss\": trn_avg_loss})\n    trn_loss_list.append(trn_avg_loss)\n    test_loss_list.append(test_avg_loss)\n    if total_patience == setting_patience:\n        wandb.finish()\n        break\n    else:\n        if best_loss > test_avg_loss:\n            total_patience = 0\n            last_loss = best_loss\n            best_loss = test_avg_loss\n            print('Model Improving')\n            print('Epoch : {}, Loss : {:.10f} ------> {:.10f}, differ : ({:.10f}) model save.....'.format(epoch+1, last_loss, best_loss, last_loss-best_loss))\n            visualization(photo = image, predict = test_output, actual = label)\n            torch.save(model.state_dict(), './checkpoint.pt')\n        else:\n            print('early stop counter : {}/{}'.format(total_patience+1, setting_patience))\n            total_patience += 1","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}