{"cells":[{"metadata":{},"cell_type":"markdown","source":"# * # Ship detection by PyTorch segmentation****","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"** Overview **\n> \n> in this notebook we use pytorch segmentation library  :\n> https://github.com/qubvel/segmentation_models.pytorch \n> \n> proved awsome pretrain networks of segmentation , we check two networks Unet and FPN and comparing two of us to get better results . \n> both networks have backbone of resnet50 and training a 20 ephoces .\n> we used only 1000 examples , for train and validation beacuse the training take a lot time , maybe we get same results with less data !\n> ","execution_count":null},{"metadata":{"trusted":true,"scrolled":false},"cell_type":"code","source":"#requirements \n!pip install segmentation_models_pytorch","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"scrolled":false},"cell_type":"code","source":"import numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\nimport os\nimport matplotlib.pyplot as plt\nimport torch\nimport torchvision\nimport torch.nn as nn\nimport torch.nn.functional as F\nimport segmentation_models_pytorch as smp\nfrom sklearn.model_selection import train_test_split\nfrom PIL import Image\nimport time\nfrom skimage.morphology import binary_opening, disk, label","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"scrolled":false},"cell_type":"code","source":"#create dataset\nclass ShipDatabaseSegmation(torch.utils.data.Dataset):\n    def __init__(self,in_df,root_path,transforms=None):\n        imagesIds = in_df['ImageId'].tolist()\n        self.image_ids =  list(set(imagesIds))\n        self.in_df = in_df\n        self.root_path = root_path\n        self.transforms = transforms\n        \n    def __len__(self):\n        return len(self.image_ids)\n    \n    def __getitem__(self,idx):\n        ImageId = self.image_ids[idx]\n        img = Image.open(self.root_path + \"/\"+ ImageId)\n        img_masks = self.in_df.loc[self.in_df['ImageId'] == ImageId, 'EncodedPixels'].tolist()\n        all_masks = np.zeros((768, 768))\n        for mask in img_masks:\n            all_masks += rle_decode(mask)\n        \n        #all_masks = np.expand_dims(all_masks,axis=0)\n        if self.transforms is not None:\n            img = self.transforms(img)\n            all_masks = self.transforms(all_masks)\n\n        return img,all_masks","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"scrolled":true},"cell_type":"code","source":"def multi_rle_encode(img, **kwargs):\n    '''\n    Encode connected regions as separated masks\n    '''\n    labels = label(img)\n    if img.ndim > 2:\n        return [rle_encode(np.sum(labels==k, axis=2), **kwargs) for k in np.unique(labels[labels>0])]\n    else:\n        return [rle_encode(labels==k, **kwargs) for k in np.unique(labels[labels>0])]\n\n# ref: https://www.kaggle.com/paulorzp/run-length-encode-and-decode\ndef rle_encode(img, min_max_threshold=1e-3, max_mean_threshold=None):\n    '''\n    img: numpy array, 1 - mask, 0 - background\n    Returns run length as string formated\n    '''\n    if np.max(img) < min_max_threshold:\n        return '' ## no need to encode if it's all zeros\n    if max_mean_threshold and np.mean(img) > max_mean_threshold:\n        return '' ## ignore overfilled mask\n    pixels = img.T.flatten()\n    pixels = np.concatenate([[0], pixels, [0]])\n    runs = np.where(pixels[1:] != pixels[:-1])[0] + 1\n    runs[1::2] -= runs[::2]\n    return ' '.join(str(x) for x in runs)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"scrolled":false},"cell_type":"code","source":"# ref: https://www.kaggle.com/paulorzp/run-length-encode-and-decode\ndef rle_encode(img, min_max_threshold=1e-3, max_mean_threshold=None):\n    '''\n    img: numpy array, 1 - mask, 0 - background\n    Returns run length as string formated\n    '''\n    if np.max(img) < min_max_threshold:\n        return '' ## no need to encode if it's all zeros\n    if max_mean_threshold and np.mean(img) > max_mean_threshold:\n        return '' ## ignore overfilled mask\n    pixels = img.T.flatten()\n    pixels = np.concatenate([[0], pixels, [0]])\n    runs = np.where(pixels[1:] != pixels[:-1])[0] + 1\n    runs[1::2] -= runs[::2]\n    return ' '.join(str(x) for x in runs)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"scrolled":false},"cell_type":"code","source":"# ref: https://www.kaggle.com/paulorzp/run-length-encode-and-decode\ndef rle_decode(mask_rle, shape=(768, 768)):\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],1], dtype=np.uint8)\n    for lo, hi in zip(starts, ends):\n        img[lo:hi] = 1\n    return img.reshape(shape).T  # Needed to align to RLE direction","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"scrolled":false},"cell_type":"code","source":"# helper function for data visualization\ndef visualize(**images):\n    \"\"\"PLot images in one row.\"\"\"\n    n = len(images)\n    plt.figure(figsize=(16, 5))\n    for i, (name, image) in enumerate(images.items()):\n        plt.subplot(1, n, i + 1)\n        plt.xticks([])\n        plt.yticks([])\n        plt.title(' '.join(name.split('_')).title())\n        plt.imshow(image)\n    plt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Preproccessing the data","execution_count":null},{"metadata":{"trusted":true,"scrolled":false},"cell_type":"code","source":"submission = pd.read_csv('../input/airbus-ship-detection/train_ship_segmentations_v2.csv')\nsubmission = submission.dropna()\nsubmission.head()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Split train and valid","execution_count":null},{"metadata":{"trusted":true,"scrolled":false},"cell_type":"code","source":"unique_img_ids = submission.groupby('ImageId').size().reset_index(name='counts')\ntrain_ids, valid_ids = train_test_split(unique_img_ids, \n                 test_size = 0.05, \n                 stratify = unique_img_ids['counts'],\n                 random_state=42\n                )\ntrain_df = pd.merge(submission, train_ids)\nvalid_df = pd.merge(submission, valid_ids)\nprint(train_df.shape[0], 'training masks')\nprint(valid_df.shape[0], 'validation masks')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"scrolled":false},"cell_type":"code","source":"# #take only 1000 ( maybe with less examples we can get same results)\ntrain_df = train_df[:1000]\nvalid_df = valid_df[:1000]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"scrolled":false},"cell_type":"code","source":"transforms = torchvision.transforms.Compose([\n    torchvision.transforms.ToTensor() \n])\nship_dataset_train = ShipDatabaseSegmation(train_df,\"../input/airbus-ship-detection/train_v2\",transforms=transforms)\nship_dataset_valid = ShipDatabaseSegmation(valid_df,\"../input/airbus-ship-detection/train_v2\",transforms=transforms)\n\ntrain_loader = torch.utils.data.DataLoader(ship_dataset_train, batch_size=5, shuffle=True, num_workers=8)\nvalid_loader = torch.utils.data.DataLoader(ship_dataset_valid, batch_size=1, shuffle=False, num_workers=4)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# **How our data look likes**","execution_count":null},{"metadata":{"trusted":true,"scrolled":true},"cell_type":"code","source":"examples = iter(valid_loader)\nfor i in range(10):\n    img,mask = next(examples)\n    visualize(\n    image=img.squeeze().permute(1,2,0),\n    mask = mask.squeeze(),\n    )","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Train section","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"overview about train section","execution_count":null},{"metadata":{"trusted":true,"scrolled":false},"cell_type":"code","source":"loss = nn.BCEWithLogitsLoss()\nloss.__name__ = \"bceWithLogitLoss\"\n\ndevice = \"cuda\"\nmetrics = [\n    smp.utils.metrics.IoU(threshold=0.5),\n]\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"scrolled":false},"cell_type":"code","source":"def train(model,number_epoches,model_name,device='cuda',lr=0.0001):\n    optimizer = torch.optim.Adam([ \n    dict(params=model.parameters(), lr=lr),])\n\n    # create epoch runners \n    # it is a simple loop of iterating over dataloader`s samples\n    train_epoch = smp.utils.train.TrainEpoch(\n        model, \n        loss=loss, \n        metrics=metrics, \n        optimizer=optimizer,\n        device=device,\n        verbose=True,\n    )\n\n    valid_epoch = smp.utils.train.ValidEpoch(\n        model, \n        loss=loss, \n        metrics=metrics, \n        device=device,\n        verbose=True,\n    )\n    print(\"Start train model : \",model_name)\n    \n    max_score = 0\n    for i in range(0, number_epoches):\n        print('\\nEpoch: {}'.format(i))\n        train_logs = train_epoch.run(train_loader)\n        valid_logs = valid_epoch.run(valid_loader)\n\n        # do something (save model, change lr, etc.)\n        if max_score < valid_logs['iou_score']:\n            max_score = valid_logs['iou_score']\n            torch.save(model, './'+model_name+'_best_model.pth')\n            print('Model saved!')\n\n        if i == 25:\n            optimizer.param_groups[0]['lr'] = 1e-5\n            print('Decrease decoder learning rate to 1e-5!')\n        if i % 5 == 0:\n            torch.save(model, './'+model_name+'_epoch_'+str(i)+'_'+str(time.time())+'.pth')\n            print(\"save model !\")\n    \n    ","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"scrolled":true},"cell_type":"code","source":"encoder = 'resnet50'\nencoder_weights ='imagenet'\nnumber_epoches = 20\ndevice = 'cuda'\nlr = 0.0001\nUnet = smp.Unet(encoder,encoder_weights=encoder_weights)\nFPN = smp.FPN(encoder,encoder_weights=encoder_weights)\n\n\n# for model_name,model in zip([\"Unet\",\"FPN\"],[Unet,FPN]):\n#     train(model,number_epoches,model_name,device=device,lr=lr) # train take 1 hour and half each model so in commands for submit\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Testing and comparing models ","execution_count":null},{"metadata":{"trusted":true,"scrolled":false},"cell_type":"code","source":"FPN = torch.load(\"../input/ship-detection-unet-fpn/FPN_best_model.pth\")\nUnet = torch.load(\"../input/ship-detection-unet-fpn/Unet_best_model.pth\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"scrolled":true},"cell_type":"code","source":"test_epoch_FPN = smp.utils.train.ValidEpoch(\n        FPN, \n        loss=loss, \n        metrics=metrics, \n        device=device,\n        verbose=True,\n    )\ntest_epoch_Unet = smp.utils.train.ValidEpoch(\n        Unet, \n        loss=loss, \n        metrics=metrics, \n        device=device,\n        verbose=True,\n    )","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**Check iou score between models**","execution_count":null},{"metadata":{"trusted":true,"scrolled":true},"cell_type":"code","source":"valid_logs = test_epoch_FPN.run(valid_loader) #  on data validation\ntrain_logs = test_epoch_FPN.run(train_loader) #  on data train","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"scrolled":true},"cell_type":"code","source":"valid_logs = test_epoch_Unet.run(valid_loader) # on data validation\ntrain_logs = test_epoch_Unet.run(train_loader) # on data train","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"> both models have  +- same iou score but Unet have a little better result so we will take him for test.","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"# Results model Unet visualize","execution_count":null},{"metadata":{"trusted":true,"scrolled":true},"cell_type":"code","source":"valid_loader = torch.utils.data.DataLoader(ship_dataset_valid, batch_size=1, shuffle=False, num_workers=4)\ntest = iter(valid_loader)\n\nfor i in range(30):\n    img,mask = next(test)\n    pred = Unet(img.cuda())\n    pred = pred.detach().cpu().double()\n    visualize(\n    image=img.squeeze().permute(1,2,0),\n    mask = mask.squeeze(),\n    Predict=pred.squeeze(),\n    )","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"raw","source":"","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"# Submission ","execution_count":null},{"metadata":{"trusted":true,"scrolled":false},"cell_type":"code","source":"def pred_image_from_path_encode(root_path,image_names,model,device):\n    out_pred_rows = []\n    for img_name in image_names:\n        c_img = Image.open(root_path+\"/\"+img_name)\n        covnertTensor = torchvision.transforms.transforms.ToTensor()\n        c_img = covnertTensor(c_img)\n        c_img = c_img.unsqueeze(0)\n        if device == 'cuda':\n            c_img = c_img.cuda()\n        cur_seg = model(c_img)\n        cur_seg = cur_seg.squeeze(0).squeeze(0).detach().cpu().numpy()\n        cur_seg[cur_seg < 0.5] = 0 \n        cur_seg[cur_seg >= 0.5] = 1\n        cur_rles = multi_rle_encode(cur_seg,max_mean_threshold=1.0)\n        if len(cur_rles)>0:\n            for c_rle in cur_rles:\n                out_pred_rows += [{'ImageId': img_name, 'EncodedPixels': c_rle}]\n        else:\n            out_pred_rows += [{'ImageId': img_name, 'EncodedPixels': None}]\n    \n    return out_pred_rows\n    \n\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"scrolled":false},"cell_type":"code","source":"test_image_dir = \"../input/airbus-ship-detection/test_v2\"\nimages_names = np.array(os.listdir(test_image_dir))\nprint(images_names)\nprint(len(images_names), 'test images found')\nout_pred_rows = pred_image_from_path_encode(test_image_dir,images_names[:200],Unet,device)\nsub = pd.DataFrame(out_pred_rows)\nsub.columns = ['ImageId', 'EncodedPixels']\nsub = sub[sub.EncodedPixels.notnull()]\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"scrolled":true},"cell_type":"code","source":"sub.head(5)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"scrolled":false},"cell_type":"code","source":"sub.to_csv('submission.csv', index=False)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"scrolled":false},"cell_type":"code","source":"# validation submission.csv","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"scrolled":false},"cell_type":"code","source":"submission = pd.read_csv('./submission.csv')\nsubmission = submission.dropna()\nsubmission.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"scrolled":false},"cell_type":"code","source":"\n\ndataset_test = ShipDatabaseSegmation(submission,\"../input/airbus-ship-detection/test_v2\",transforms=transforms)\ntest_loader = torch.utils.data.DataLoader(dataset_test, batch_size=1, shuffle=True, num_workers=8)\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"scrolled":false},"cell_type":"code","source":"examples = iter(test_loader)\n\nfor img,mask in examples:\n    visualize(\n    image=img.squeeze().permute(1,2,0),\n    predict = mask.squeeze(),\n    )","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"scrolled":false},"cell_type":"code","source":"","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}