{"cells":[{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"_kg_hide-output":true,"scrolled":false},"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load in \n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\nimport cv2\nimport math\nimport random\nimport gc; gc.enable() # memory is tight\nimport matplotlib.pyplot as plt\n%matplotlib inline\nfrom skimage.data import imread\nfrom skimage.morphology import label\nfrom pathlib import Path\nfrom math import ceil\nfrom functools import partial\nfrom sklearn.model_selection import train_test_split\n\nimport keras.backend as K\nfrom keras import Model\nfrom keras import models, layers\nfrom keras.preprocessing.image import ImageDataGenerator\nfrom keras.applications.xception import Xception\nfrom keras.layers import LeakyReLU, Add, ZeroPadding2D, Conv2DTranspose\nfrom keras.layers import Conv2D, Concatenate, concatenate, MaxPooling2D, Dropout, BatchNormalization\nfrom keras.callbacks import ModelCheckpoint, LearningRateScheduler, EarlyStopping, ReduceLROnPlateau\nfrom keras.optimizers import Adam\nfrom keras.losses import binary_crossentropy\n\n# Input data files are available in the \"../input/\" directory.\n# For example, running this (by clicking run or pressing Shift+Enter) will list the files in the input directory\n\nimport os\nprint(os.listdir(\"../input\"))\n\n# Any results you write to the current directory are saved as output.","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"787db7339700ff867e6f94dd1636ec8d82eb2a40"},"cell_type":"code","source":"print(os.listdir(\"..\"))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"6ed128aca2580bfb10f8f48912888dab75110e1c"},"cell_type":"code","source":"INPUT_PATH = \"../input\"\nBATCH_SIZE = 8\nIMG_SCALING = (2, 2)\nEDGE_CROP = 16\nAUGMENT_BRIGHTNESS = False\nGAUSSIAN_NOISE = 0.1\nUPSAMPLE_MODE = 'DECONV'\nMAX_TRAIN_STEPS = 200\nNET_SCALING = None\nDATA_PATH = INPUT_PATH\nTRAIN = os.path.join(DATA_PATH, \"train_v2\")\nMASKS = os.path.join(DATA_PATH, \"train_ship_segmentations_v2.csv\")\nTEST = os.path.join(DATA_PATH, \"test_v2\")\nTEST_MASKS = os.path.join(DATA_PATH, \"sample_submission_v2.csv\")","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"6be8f40f319fe28d3c31e8d0e1e2b5da1a2c8391"},"cell_type":"markdown","source":"## Some utility functions"},{"metadata":{"trusted":true,"_uuid":"4cf2c43bfcd31772ddf1fb9bc31d775df00eed48"},"cell_type":"code","source":"def multi_rle_encode(img):\n    labels = label(img[:, :, 0])\n    return [rle_encode(labels==k) for k in np.unique(labels[labels>0])]\n\ndef get_filename(image_id, image_type):\n    check_dir = False\n    if \"Train\" == image_type:\n        data_path = TRAIN\n    elif \"Test\" in image_type:\n        data_path = TEST\n    else:\n        raise Exception(\"Image type '%s' is not recognized\" % image_type)\n\n    if check_dir and not os.path.exists(data_path):\n        os.makedirs(data_path)\n\n    return os.path.join(data_path, \"{}\".format(image_id))\n\ndef get_image_data(image_id, image_type, **kwargs):\n    img = _get_image_data_opencv(image_id, image_type, **kwargs)\n    img = img.astype('uint8')\n    return img\n\ndef _get_image_data_opencv(image_id, image_type, **kwargs):\n    fname = get_filename(image_id, image_type)\n    img = cv2.imread(fname)\n    assert img is not None, \"Failed to read image : %s, %s\" % (image_id, image_type)\n    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n    return img\n\n# ref: https://www.kaggle.com/paulorzp/run-length-encode-and-decode\ndef rle_encode(img):\n    '''\n    img: numpy array, 1 - mask, 0 - background\n    Returns run length as string formated\n    '''\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)\n\n# 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], 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\n\n# https://github.com/ternaus/TernausNet/blob/master/Example.ipynb\ndef mask_overlay(image, mask):\n    \"\"\"\n    Helper function to visualize mask\n    \"\"\"\n    mask = mask.astype(np.uint8)\n    weighted_sum = cv2.addWeighted(mask, 0.75, image, 0.5, 0.)\n    img = image.copy()\n    ind = mask[:, :, 1] > 0    \n    img[ind] = weighted_sum[ind]    \n    return img\n\ndef masks_as_image(in_mask_list):\n    # Take the individual ship masks and create a single mask array for all ships\n    all_masks = np.zeros((768, 768), dtype = np.int16)\n    #if isinstance(in_mask_list, list):\n    for mask in in_mask_list:\n        if isinstance(mask, str):\n            all_masks += rle_decode(mask)\n    return np.expand_dims(all_masks, -1)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"5c637577ab3a8ac8ffd67fe8bfcf8a43defc214d"},"cell_type":"markdown","source":"## Look at a sample of the training images."},{"metadata":{"_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","trusted":true,"scrolled":false},"cell_type":"code","source":"masks = pd.read_csv(MASKS)\nfile_names = os.listdir(TRAIN)\nprint(\"number of records in train_ship_segmentations_v2: {}\".format(len(masks)))\nprint(\"Train files :\",len(file_names))\nprint(\"number of train images: {}\".format(len(masks.ImageId.unique())))\nmasks.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"scrolled":false,"_uuid":"619b9864f5bc027ae6ebe55ddc9183defba8b866"},"cell_type":"code","source":"masks.info()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"5c280f6308cc97d6f30484391a9e785c00ee653f"},"cell_type":"markdown","source":"## Look at 25 images with ships..."},{"metadata":{"trusted":true,"_uuid":"8b48df1a40648b4c212768e130848560de994c7e","scrolled":true},"cell_type":"code","source":"sample = masks[~masks.EncodedPixels.isna()].sample(25)\n\nfig, ax = plt.subplots(5, 5, sharex='col', sharey='row')\nfig.set_size_inches(25, 25)\n\nfor i, imgid in enumerate(sample.ImageId):\n    col = i % 5\n    row = i // 5\n    \n    img = get_image_data(imgid, \"Train\")\n    \n    ax[row, col].set_title(imgid)\n    ax[row, col].imshow(img)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"9641378ee21219bc60e4e369c2b0d33b8517fe01"},"cell_type":"markdown","source":"## ...and 25 without ships."},{"metadata":{"trusted":true,"_uuid":"851eeefbf8259aeabefed2ae1a9291aa6b1479de"},"cell_type":"code","source":"# Show imgs with size < 50kB and with ships\n# sample = masks[masks.ImageId.apply(lambda x: (os.stat(get_filename(x, \"Train\")).st_size/1024) < 50) & ~masks.EncodedPixels.isna()].sample(25)\n\nsample = masks[masks.EncodedPixels.isna()].sample(25)\n\nfig, ax = plt.subplots(5, 5, sharex='col', sharey='row')\nfig.set_size_inches(25, 25)\n\nfor i, imgid in enumerate(sample.ImageId):\n    col = i % 5\n    row = i // 5\n    \n    img = get_image_data(imgid, \"Train\")\n    \n    ax[row, col].set_title(imgid)\n    ax[row, col].imshow(img)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"5f4788a59df37d5cfa6cd0ab49601a888d91ecc0"},"cell_type":"markdown","source":"## Some images with mask overlayed"},{"metadata":{"trusted":true,"_uuid":"d948713255b2b56a5fab1819258e823bf42fad90"},"cell_type":"raw","source":"# number of images with size < 42kB and simultaneously with ship in image\nlen((masks[masks.ImageId.apply(lambda x: (os.stat(get_filename(x, \"Train\")).st_size/1024) < 42) & ~masks.EncodedPixels.isna()]).ImageId.unique())"},{"metadata":{"trusted":true,"_uuid":"a191db00d904f7b7bb7db762b73b11e57ba9f116","scrolled":true},"cell_type":"code","source":"NUM_IMG = 20\nNUM_COL = 2\nNUM_MASKS = 2\nIMG_SIZE = 25\n\nsample = masks[~masks.EncodedPixels.isna()].sample(NUM_IMG)\n\n# Show imgs with size < 50kB and with ships\n# sample = masks[masks.ImageId.apply(lambda x: (os.stat(get_filename(x, \"Train\")).st_size/1024) < 42) & ~masks.EncodedPixels.isna()].sample(NUM_IMG)\n# sample = masks[masks.ImageId.apply(lambda x: (os.stat(get_filename(x, \"Train\")).st_size/1024) < 40)].sample(NUM_IMG)\n\nnumber_of_rows = ceil(NUM_IMG / NUM_COL)\nnumber_of_cols = NUM_COL * NUM_MASKS\n\nfig, ax = plt.subplots(number_of_rows, number_of_cols, sharex='col', sharey='row')\nfig.set_size_inches(IMG_SIZE, IMG_SIZE * (number_of_rows / number_of_cols))\n\nfor i, imgid in enumerate(sample.ImageId):\n    col = (i % NUM_COL) * 2\n    row = i // NUM_COL\n    \n    img = get_image_data(imgid, \"Train\")\n    \n    # if the ship is in the image, show next to original image, image with mask \n    if all(isinstance(x, str) for x in masks[masks.ImageId == imgid].EncodedPixels):\n        decoded_masks = masks[masks.ImageId == imgid].EncodedPixels.apply(lambda x: rle_decode(x))\n        mask = sum(decoded_masks)\n        mask = np.expand_dims(mask,axis=2)\n        mask = np.repeat(mask,3,axis=2).astype('uint8')*255\n\n        img_masked = mask_overlay(img, mask)\n    else:\n        img_masked = np.full((img.shape[0],img.shape[1],3), 255)\n        \n    ax[row, col].set_title(imgid)\n    ax[row, col].imshow(img)\n    ax[row, col+1].imshow(img_masked)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"286e72c66ff89d998579468fef39bf653f19512d"},"cell_type":"markdown","source":""},{"metadata":{"_uuid":"2ecdd77961c380009a0753b94b85092955dfda62"},"cell_type":"markdown","source":"## Make sure encode/decode works\nWe want to check if/that  Image0=?Image1  We could check the RLEs as well but that is more tedious. Also depending on how the objects have been labeled we might have different counts."},{"metadata":{"trusted":true,"_uuid":"868b0a411349c0f52fd3ea5f38a3eab8f4543227"},"cell_type":"code","source":"fig, (ax1, ax2) = plt.subplots(1, 2, figsize = (14, 7))\nrle_0 = masks.query('ImageId==\"00021ddc3.jpg\"')['EncodedPixels']\nimg_0 = masks_as_image(rle_0)\nax1.imshow(img_0[:, :, 0])\nax1.set_title('Image$_0$')\nrle_1 = multi_rle_encode(img_0)\nimg_1 = masks_as_image(rle_1)\nax2.imshow(img_1[:, :, 0])\nax2.set_title('Image$_1$')\nprint('Check Decoding->Encoding',\n      'RLE_0:', len(rle_0), '->',\n      'RLE_1:', len(rle_1))","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"899ef6e2a72db9749b7200290c537465a7814da8"},"cell_type":"markdown","source":"## Look at class balance"},{"metadata":{"trusted":true,"_uuid":"0280055240c9ca385532beff63424f1f917d46a6"},"cell_type":"code","source":"ships = masks[~masks.EncodedPixels.isna()].ImageId.unique()\nnoships = masks[masks.EncodedPixels.isna()].ImageId.unique()\n\nplt.bar(['Ships', 'No Ships'], [len(ships), len(noships)]);\nplt.ylabel('Number of Images');","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"373dc3653ec420002e53e84ba7b8ed716a5bba65"},"cell_type":"markdown","source":"## Split into training and validation groups"},{"metadata":{"trusted":true,"_uuid":"eff3ec0abd6742b6888f8a84bbd105c80589ea1a","scrolled":true},"cell_type":"code","source":"# groupby ImageId and make list from EncodedPixels\nunique_img_ids = masks.groupby('ImageId', as_index=False)['EncodedPixels'].agg({'EncodedPixels':(lambda x: list(x))})\n# count of ships in 1 img\nunique_img_ids['ships_count'] = unique_img_ids['EncodedPixels'].map(lambda x: len(x) if isinstance(x[0], str) else 0)\n\n# some files are too small/corrupt\nunique_img_ids['file_size_kb'] = unique_img_ids['ImageId'].map(lambda img_id: os.stat(get_filename(img_id, \"Train\")).st_size/1024)\nunique_img_ids = unique_img_ids[unique_img_ids['file_size_kb']>50] # keep only 50kb files\n# unique_img_ids['file_size_kb'].hist()\n\nunique_img_ids[unique_img_ids.ships_count > 0]['ships_count'].hist(bins=np.arange(12))\nunique_img_ids.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"2e348c3792bc8551d0c6549e2ed9af2770d9e294"},"cell_type":"code","source":"train_df, valid_df = train_test_split(unique_img_ids, \n                 test_size = 0.05, \n                 stratify = unique_img_ids['ships_count'])\nprint(train_df.shape[0], 'training masks')\nprint(valid_df.shape[0], 'validation masks')","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"2d20a61b2110c95ed08b6da328d50e406660f5af"},"cell_type":"markdown","source":"## Undersample Empty Images\nHere we undersample the empty images to get a better balanced group with more ships to try and segment"},{"metadata":{"trusted":true,"scrolled":false,"_uuid":"e252f24640c6566c2685001f6916e7b601015c78"},"cell_type":"code","source":"def sample_ships(in_df, base_rep_val=30000):\n    if in_df['ships_count'].values[0]==0:\n        return in_df.sample(base_rep_val) # undersample img without ships\n    else:\n        return in_df\n    \nbalanced_train_df = train_df.groupby('ships_count').apply(sample_ships)\nbalanced_train_df['ships_count'].hist(bins=np.arange(12))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"3bc3d300582ef40c8c2b97e4e868322b99fc3004"},"cell_type":"code","source":"sample_ships_valid = partial(sample_ships, base_rep_val = 1500)\nbalanced_valid_df = valid_df.groupby('ships_count').apply(sample_ships_valid)\nbalanced_valid_df['ships_count'].hist(bins=np.arange(12))","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"a466b501c1c9d96285270c2ff35a111cff5dde04"},"cell_type":"markdown","source":"## Decode all the RLEs into Images\nMake a generator to produce batches of images"},{"metadata":{"trusted":true,"_uuid":"1b6d01860dfd6720858e8264444288a1c5f554be"},"cell_type":"code","source":"def make_image_gen(in_df, batch_size = BATCH_SIZE):\n    out_rgb = []\n    out_mask = []\n    while True:\n        in_df = in_df.sample(frac = 1)    # schuffle\n        for index, row in in_df.iterrows():\n            c_img = get_image_data(row['ImageId'], \"Train\")\n            c_mask = masks_as_image(row['EncodedPixels'])\n            if IMG_SCALING is not None:\n                c_img = c_img[::IMG_SCALING[0], ::IMG_SCALING[1]]\n                c_mask = c_mask[::IMG_SCALING[0], ::IMG_SCALING[1]]\n            out_rgb += [c_img]\n            out_mask += [c_mask]\n            if len(out_rgb)>=batch_size:\n                yield np.stack(out_rgb, 0)/255.0, np.stack(out_mask, 0)\n                out_rgb, out_mask=[], []","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"scrolled":true,"_uuid":"acc778df9e912f75084f4cf2ab12165e5527752f"},"cell_type":"code","source":"train_gen = make_image_gen(balanced_train_df)\ntrain_x, train_y = next(train_gen)\n\nprint('x', train_x.shape, train_x.min(), train_x.max())\nprint('y', train_y.shape, train_y.min(), train_y.max())\n\nSHOW_MAX_IMG = 4\nIMG_SIZE = 6\nnum_of_row = min(SHOW_MAX_IMG//2, train_x.shape[0]//2)\nfig, ax = plt.subplots(num_of_row, 4, sharex='col', sharey='row', figsize = (IMG_SIZE*4, IMG_SIZE*num_of_row))\nfor i, imgid in enumerate(train_x):\n    if i >= num_of_row*2:\n        break\n    col = i // num_of_row * 2\n    row = i % num_of_row\n    ax[row, col].imshow(train_x[i])\n    ax[row, col+1].imshow(train_y[i][:, :, 0])","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"b5168b800c511945a239443cbb4bfe87192a5fcf"},"cell_type":"markdown","source":"## Make the Validation Set"},{"metadata":{"trusted":true,"_uuid":"1a594ebab861cbe7291641d2d62b28ff4a4d9e6f"},"cell_type":"code","source":"VALID_IMG_COUNT = 400        #beware on RAM usage!  \nif IMG_SCALING == (2, 2):\n    VALID_IMG_COUNT = 800\nif IMG_SCALING == (3, 3):\n    VALID_IMG_COUNT = 1200\nvalid_x, valid_y = next(make_image_gen(balanced_valid_df, VALID_IMG_COUNT))\nprint('x', valid_x.shape, train_x.min(), train_x.max())\nprint('y', valid_y.shape, train_y.min(), train_y.max())","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"970771473a458170bcd9dc9a311b354a7ac96896"},"cell_type":"markdown","source":"# Augment Data"},{"metadata":{"trusted":true,"_uuid":"cefa9e14632bc3361f7c46839fd6614615a2b318","scrolled":true},"cell_type":"code","source":"# treshold mask_img values to 0.0 or 1.0\ndef treshold_mask(mask_img, threshold = 0.5):\n    ret, thresh = cv2.threshold(mask_img, 0.5, 1.0, cv2.THRESH_BINARY)\n    # returns back the third dimension\n    return np.reshape(thresh, (thresh.shape[0], thresh.shape[1], -1))\n\ndg_args = dict(featurewise_center = False, \n                  samplewise_center = False,\n#                   rotation_range = 15, \n#                   width_shift_range = 0.1, \n#                   height_shift_range = 0.1, \n#                   shear_range = 0.01,\n#                   zoom_range = [0.9, 1.25],  \n                  horizontal_flip = True, \n                  vertical_flip = True,\n                  fill_mode = 'reflect',\n                   data_format = 'channels_last')\n# brightness can be problematic since it seems to change the labels differently from the images \nif AUGMENT_BRIGHTNESS:\n    dg_args['brightness_range'] = [0.5, 1.5]\nimage_gen = ImageDataGenerator(**dg_args)\n\nif AUGMENT_BRIGHTNESS:\n    dg_args.pop('brightness_range')\nlabel_gen = ImageDataGenerator(**dg_args)\n\ndef create_aug_gen(in_gen, seed = None):\n    np.random.seed(seed if seed is not None else np.random.choice(range(9999)))\n    for in_x, in_y in in_gen:\n        seed = np.random.choice(range(9999))\n        # keep the seeds syncronized otherwise the augmentation to the images is different from the masks\n        g_x = image_gen.flow(255*in_x,\n                             batch_size = in_x.shape[0], \n                             seed = seed, \n                             shuffle=True)\n        g_y = label_gen.flow(in_y, \n                             batch_size = in_x.shape[0], \n                             seed = seed, \n                             shuffle=True)\n        \n        # treshold g_y values to 0.0 or 1.0\n        yield next(g_x)/255.0, np.asarray([treshold_mask(x) for x in next(g_y)])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"4f3ef640eb205f2e074aba520e26ce61d5cf57ef","scrolled":true},"cell_type":"code","source":"cur_gen = create_aug_gen(train_gen, seed = 42)\nt_x, t_y = next(cur_gen)\n\n \nprint('x', t_x.shape, t_x.dtype, t_x.min(), t_x.max())\nprint('y', t_y.shape, t_y.dtype, t_y.min(), t_y.max())\n\nSHOW_MAX_IMG = 4\nIMG_SIZE = 6\nnum_of_row = min(SHOW_MAX_IMG//2, train_x.shape[0]//2)\nfig, ax = plt.subplots(num_of_row, 4, sharex='col', sharey='row', figsize = (IMG_SIZE*4, IMG_SIZE*num_of_row))\nfor i, imgid in enumerate(t_x):\n    if i >= num_of_row*2:\n        break\n    col = i // num_of_row * 2\n    row = i % num_of_row\n    ax[row, col].imshow(t_x[i])\n    ax[row, col+1].imshow(t_y[i][:, :, 0])","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"4ec084c7ad9fa410b7754814547a68199d58efe9"},"cell_type":"markdown","source":"### Test and Visualize data augmentation,  Just for debugging"},{"metadata":{"trusted":true,"_uuid":"c02d942c20e774a50b875e0e9a256f29188c4ccf"},"cell_type":"code","source":"test_gen = make_image_gen(balanced_train_df, batch_size = 4)\n# np.random.seed(seed if seed is not None else np.random.choice(range(9999)))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"5b379400793cb336ddd3dfe12e487f04c6efefa2"},"cell_type":"code","source":"in_x, in_y = next(test_gen)\nseed = np.random.choice(range(9999))\ng_x = image_gen.flow(255*in_x,\n                     batch_size = t_x.shape[0], \n                     seed = seed, \n                     shuffle=True)\ng_y = label_gen.flow(in_y, \n                     batch_size = in_x.shape[0], \n                     seed = seed, \n                     shuffle=True)\nt_x = next(g_x)\nt_x /= 255\nt_y = next(g_y)\nt_y = np.asarray([treshold_mask(x) for x in t_y])\n\nfig, ax = plt.subplots(4, 4, sharex='col', sharey='row', figsize = (24, 24))\nfor i, imgid in enumerate(t_x):\n    ax[i, 0].imshow(in_x[i])\n    ax[i, 1].imshow(in_y[i][:, :, 0])\n    ax[i, 2].imshow(t_x[i])\n    ax[i, 3].imshow(t_y[i][:, :, 0])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"0399410b8fd2034f5a38b83b8ea351b137f1da06"},"cell_type":"code","source":"gc.collect()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"ac8530aa4de596b7b60f721bff9486d57106de02"},"cell_type":"markdown","source":"## Load model for next training"},{"metadata":{"trusted":true,"_uuid":"94b180437d75dfbcea504e496819a1fd2ffa5234"},"cell_type":"raw","source":"seg_model = models.load_model(\"seg_model.h5\", compile=False)\nseg_in_shape = seg_model.get_input_shape_at(0)[1:3]\nseg_out_shape = seg_model.get_output_shape_at(0)[1:3]\nprint(seg_in_shape, '->', seg_out_shape)"},{"metadata":{"_uuid":"b3de582be0585a5ecb714625b41bbcc547240446"},"cell_type":"markdown","source":"## Build UXception model"},{"metadata":{"trusted":true,"_uuid":"bfce68baa2604c72046d664f89dea34f0b7ee581"},"cell_type":"code","source":"def convolution_block(x, filters, size, strides=(1,1), padding='same', activation=True):\n    x = Conv2D(filters, size, strides=strides, padding=padding)(x)\n    x = BatchNormalization()(x)\n    if activation == True:\n        x = LeakyReLU(alpha=0.1)(x)\n    return x\n\ndef residual_block(blockInput, num_filters=16):\n    x = LeakyReLU(alpha=0.1)(blockInput)\n    x = BatchNormalization()(x)\n    blockInput = BatchNormalization()(blockInput)\n    x = convolution_block(x, num_filters, (3,3) )\n    x = convolution_block(x, num_filters, (3,3), activation=False)\n    x = Add()([x, blockInput])\n    return x","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"b02dd75b5f3a8ddaa059124231d4fd4d156788a6"},"cell_type":"code","source":"def UXception(input_shape=(None, None, 3)):\n\n    backbone = Xception(input_shape=input_shape,weights='imagenet',include_top=False)\n    input = backbone.input\n    start_neurons = 16\n\n    conv4 = backbone.layers[121].output\n    conv4 = LeakyReLU(alpha=0.1)(conv4)\n    pool4 = MaxPooling2D((2, 2))(conv4)\n    pool4 = Dropout(0.1)(pool4)\n    \n     # Middle\n    convm = Conv2D(start_neurons * 32, (3, 3), activation=None, padding=\"same\")(pool4)\n    convm = residual_block(convm,start_neurons * 32)\n    convm = residual_block(convm,start_neurons * 32)\n    convm = LeakyReLU(alpha=0.1)(convm)\n    \n    # 10 -> 20\n    deconv4 = Conv2DTranspose(start_neurons * 16, (3, 3), strides=(2, 2), padding=\"same\")(convm)\n    uconv4 = concatenate([deconv4, conv4])\n    uconv4 = Dropout(0.1)(uconv4)\n    \n    uconv4 = Conv2D(start_neurons * 16, (3, 3), activation=None, padding=\"same\")(uconv4)\n    uconv4 = residual_block(uconv4,start_neurons * 16)\n    uconv4 = residual_block(uconv4,start_neurons * 16)\n    uconv4 = LeakyReLU(alpha=0.1)(uconv4)\n    \n    # 10 -> 20\n    deconv3 = Conv2DTranspose(start_neurons * 8, (3, 3), strides=(2, 2), padding=\"same\")(uconv4)\n    conv3 = backbone.layers[31].output\n    uconv3 = concatenate([deconv3, conv3])    \n    uconv3 = Dropout(0.1)(uconv3)\n    \n    uconv3 = Conv2D(start_neurons * 8, (3, 3), activation=None, padding=\"same\")(uconv3)\n    uconv3 = residual_block(uconv3,start_neurons * 8)\n    uconv3 = residual_block(uconv3,start_neurons * 8)\n    uconv3 = LeakyReLU(alpha=0.1)(uconv3)\n    \n    # 20 -> 40\n    deconv2 = Conv2DTranspose(start_neurons * 4, (3, 3), strides=(2, 2), padding=\"same\")(uconv3)\n    conv2 = backbone.layers[21].output\n    conv2 = ZeroPadding2D(((1,0),(1,0)))(conv2)\n    uconv2 = concatenate([deconv2, conv2])\n        \n    uconv2 = Dropout(0.1)(uconv2)\n    uconv2 = Conv2D(start_neurons * 4, (3, 3), activation=None, padding=\"same\")(uconv2)\n    uconv2 = residual_block(uconv2,start_neurons * 4)\n    uconv2 = residual_block(uconv2,start_neurons * 4)\n    uconv2 = LeakyReLU(alpha=0.1)(uconv2)\n    \n    # 40 -> 80\n    deconv1 = Conv2DTranspose(start_neurons * 2, (3, 3), strides=(2, 2), padding=\"same\")(uconv2)\n    conv1 = backbone.layers[11].output\n    conv1 = ZeroPadding2D(((3,0),(3,0)))(conv1)\n    uconv1 = concatenate([deconv1, conv1])\n    \n    uconv1 = Dropout(0.1)(uconv1)\n    uconv1 = Conv2D(start_neurons * 2, (3, 3), activation=None, padding=\"same\")(uconv1)\n    uconv1 = residual_block(uconv1,start_neurons * 2)\n    uconv1 = residual_block(uconv1,start_neurons * 2)\n    uconv1 = LeakyReLU(alpha=0.1)(uconv1)\n    \n    # 80 -> 160\n    uconv0 = Conv2DTranspose(start_neurons * 1, (3, 3), strides=(2, 2), padding=\"same\")(uconv1)   \n    uconv0 = Dropout(0.1)(uconv0)\n    uconv0 = Conv2D(start_neurons * 1, (3, 3), activation=None, padding=\"same\")(uconv0)\n    uconv0 = residual_block(uconv0,start_neurons * 1)\n    uconv0 = residual_block(uconv0,start_neurons * 1)\n    uconv0 = LeakyReLU(alpha=0.1)(uconv0)\n    \n    uconv0 = Dropout(0.1/2)(uconv0)\n    output_layer = Conv2D(1, (1,1), padding=\"same\", activation=\"sigmoid\")(uconv0)    \n    \n    model = Model(input, output_layer)\n    model.name = 'u-xception'\n\n    return model","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"f822b3087770b468b0ace364f04bc82a69404ef2"},"cell_type":"code","source":"K.clear_session()\nseg_model = UXception(input_shape=(t_x.shape[1],t_x.shape[1],3))\n# seg_model.summary()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"6a2c609d99b57cb7ddb63a41377a2b43fc76d33b"},"cell_type":"markdown","source":"## Build simple Unet model"},{"metadata":{"trusted":true,"_uuid":"5e4a4fdddd0fef20a0094620fc080f6189f3d6a1","scrolled":false,"_kg_hide-input":false,"_kg_hide-output":false},"cell_type":"raw","source":"# Build U-Net model\ndef upsample_conv(filters, kernel_size, strides, padding):\n    return layers.Conv2DTranspose(filters, kernel_size, strides=strides, padding=padding)\ndef upsample_simple(filters, kernel_size, strides, padding):\n    return layers.UpSampling2D(strides)\n\nif UPSAMPLE_MODE=='DECONV':\n    upsample=upsample_conv\nelse:\n    upsample=upsample_simple\n    \ninput_img = layers.Input(t_x.shape[1:], name = 'RGB_Input')\npp_in_layer = input_img\nif NET_SCALING is not None:\n    pp_in_layer = layers.AvgPool2D(NET_SCALING)(pp_in_layer)\n    \npp_in_layer = layers.GaussianNoise(GAUSSIAN_NOISE)(pp_in_layer)\npp_in_layer = layers.BatchNormalization()(pp_in_layer)\n\nc1 = layers.Conv2D(8, (3, 3), activation='relu', padding='same') (pp_in_layer)\nc1 = layers.Conv2D(8, (3, 3), activation='relu', padding='same') (c1)\np1 = layers.MaxPooling2D((2, 2)) (c1)\n\nc2 = layers.Conv2D(16, (3, 3), activation='relu', padding='same') (p1)\nc2 = layers.Conv2D(16, (3, 3), activation='relu', padding='same') (c2)\np2 = layers.MaxPooling2D((2, 2)) (c2)\n\nc3 = layers.Conv2D(32, (3, 3), activation='relu', padding='same') (p2)\nc3 = layers.Conv2D(32, (3, 3), activation='relu', padding='same') (c3)\np3 = layers.MaxPooling2D((2, 2)) (c3)\n\nc4 = layers.Conv2D(64, (3, 3), activation='relu', padding='same') (p3)\nc4 = layers.Conv2D(64, (3, 3), activation='relu', padding='same') (c4)\np4 = layers.MaxPooling2D(pool_size=(2, 2)) (c4)\n\nc5 = layers.Conv2D(128, (3, 3), activation='relu', padding='same') (p4)\nc5 = layers.Conv2D(128, (3, 3), activation='relu', padding='same') (c5)\n\nu6 = upsample(64, (2, 2), strides=(2, 2), padding='same') (c5)\nu6 = layers.concatenate([u6, c4])\nc6 = layers.Conv2D(64, (3, 3), activation='relu', padding='same') (u6)\nc6 = layers.Conv2D(64, (3, 3), activation='relu', padding='same') (c6)\n\nu7 = upsample(32, (2, 2), strides=(2, 2), padding='same') (c6)\nu7 = layers.concatenate([u7, c3])\nc7 = layers.Conv2D(32, (3, 3), activation='relu', padding='same') (u7)\nc7 = layers.Conv2D(32, (3, 3), activation='relu', padding='same') (c7)\n\nu8 = upsample(16, (2, 2), strides=(2, 2), padding='same') (c7)\nu8 = layers.concatenate([u8, c2])\nc8 = layers.Conv2D(16, (3, 3), activation='relu', padding='same') (u8)\nc8 = layers.Conv2D(16, (3, 3), activation='relu', padding='same') (c8)\n\nu9 = upsample(8, (2, 2), strides=(2, 2), padding='same') (c8)\nu9 = layers.concatenate([u9, c1], axis=3)\nc9 = layers.Conv2D(8, (3, 3), activation='relu', padding='same') (u9)\nc9 = layers.Conv2D(8, (3, 3), activation='relu', padding='same') (c9)\n\nd = layers.Conv2D(1, (1, 1), activation='sigmoid') (c9)\nd = layers.Cropping2D((EDGE_CROP, EDGE_CROP))(d)\nd = layers.ZeroPadding2D((EDGE_CROP, EDGE_CROP))(d)\nif NET_SCALING is not None:\n    d = layers.UpSampling2D(NET_SCALING)(d)\n\nseg_model = models.Model(inputs=[input_img], outputs=[d])\nseg_model.summary()"},{"metadata":{"trusted":true,"_uuid":"96cbf87b0a71c0348f206ef0bccde730df4799cc"},"cell_type":"code","source":"def dice_coef(y_true, y_pred):\n    y_true_f = K.flatten(y_true)\n    y_pred = K.cast(y_pred, 'float32')\n    y_pred_f = K.cast(K.greater(K.flatten(y_pred), 0.5), 'float32')\n    intersection = y_true_f * y_pred_f\n    score = 2. * K.sum(intersection) / (K.sum(y_true_f) + K.sum(y_pred_f))\n    return score\n\ndef dice_loss(y_true, y_pred, smooth = 1.):\n    y_true_f = K.flatten(y_true)\n    y_pred_f = K.flatten(y_pred)\n    intersection = y_true_f * y_pred_f\n    score = (2. * K.sum(intersection) + smooth) / (K.sum(y_true_f) + K.sum(y_pred_f) + smooth)\n    return 1. - score\n\ndef bce_dice_loss(y_true, y_pred):\n    return K.mean(binary_crossentropy(y_true, y_pred)) + dice_loss(y_true, y_pred)\n\ndef bce_logdice_loss(y_true, y_pred):\n    return K.mean(binary_crossentropy(y_true, y_pred)) - K.log(1. - dice_loss(y_true, y_pred))\n\ndef true_positive_rate(y_true, y_pred):\n    return K.sum(K.flatten(y_true)*K.flatten(K.round(y_pred)))/K.sum(y_true)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"8862f110be60719807aaa49d995f596f91c3de9e"},"cell_type":"code","source":"weight_path=\"{}_weights.best.hdf5\".format('seg_model')\n\ncheckpoint = ModelCheckpoint(weight_path, monitor='val_dice_coef', verbose=1, \n                             save_best_only=True, mode='max', save_weights_only = True)\n\nreduceLROnPlat = ReduceLROnPlateau(monitor='val_dice_coef', factor=0.5, \n                                   patience=3, \n                                   verbose=1, mode='max', min_delta=0.0001, cooldown=2, min_lr=1e-6)\nearly = EarlyStopping(monitor=\"val_dice_coef\", \n                      mode=\"max\", \n                      patience=15) # probably needs to be more patient, but kaggle time is limited\ncallbacks_list = [checkpoint, early, reduceLROnPlat]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"21bd16d9a05fc20a7af158ce4b0cd97607507a6e"},"cell_type":"code","source":"seg_model.compile(optimizer=Adam(1e-4, decay=1e-6), loss=bce_logdice_loss, metrics=[dice_coef, 'binary_accuracy', true_positive_rate])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"75da401202ae13cea9c17c8b8d92a2a02583e5ca"},"cell_type":"code","source":"MAX_TRAIN_STEPS = 500\nNB_EPOCHS = 16","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"4c3938f67c76c03629fe2f85845434eec33d2b68"},"cell_type":"code","source":"step_count = min(MAX_TRAIN_STEPS, balanced_train_df.shape[0]//BATCH_SIZE)\ntrain_gen = create_aug_gen(make_image_gen(balanced_train_df))\n# train_gen = make_image_gen(balanced_train_df)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"55944a430dea9bdd3ad71e36b4cead5af76912ac","scrolled":true},"cell_type":"code","source":"loss_history = [seg_model.fit_generator(train_gen, \n                             steps_per_epoch=step_count, \n                             epochs=NB_EPOCHS, \n                             validation_data=(valid_x, valid_y),\n                             callbacks=callbacks_list,\n                            workers=1 # the generator is not very thread safe\n                                       )]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"898dccb2beaf95e68fa1b34d9a75354c886bf822"},"cell_type":"code","source":"def show_loss(loss_history):\n    epich = np.cumsum(np.concatenate(\n        [np.linspace(0.5, 1, len(mh.epoch)) for mh in loss_history]))\n    fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(16, 14))\n    _ = ax1.plot(epich,\n                 np.concatenate([mh.history['loss'] for mh in loss_history]),\n                 'b-',\n                 epich, np.concatenate(\n            [mh.history['val_loss'] for mh in loss_history]), 'r-')\n    ax1.legend(['Training', 'Validation'])\n    ax1.set_title('Loss')\n\n    _ = ax2.plot(epich, np.concatenate(\n        [mh.history['true_positive_rate'] for mh in loss_history]), 'b-',\n                     epich, np.concatenate(\n            [mh.history['val_true_positive_rate'] for mh in loss_history]),\n                     'r-')\n    ax2.legend(['Training', 'Validation'])\n    ax2.set_title('True Positive Rate\\n(Positive Accuracy)')\n     \n    _ = ax3.plot(epich, np.concatenate(\n        [mh.history['binary_accuracy'] for mh in loss_history]), 'b-',\n                     epich, np.concatenate(\n            [mh.history['val_binary_accuracy'] for mh in loss_history]),\n                     'r-')\n    ax3.legend(['Training', 'Validation'])\n    ax3.set_title('Binary Accuracy (%)')\n    \n    _ = ax4.plot(epich, np.concatenate(\n        [mh.history['dice_coef'] for mh in loss_history]), 'b-',\n                     epich, np.concatenate(\n            [mh.history['val_dice_coef'] for mh in loss_history]),\n                     'r-')\n    ax4.legend(['Training', 'Validation'])\n    ax4.set_title('DICE')\n\nshow_loss(loss_history)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"ec65b63ec256643b34ad3086548c18edd168b073"},"cell_type":"markdown","source":"## Load best model and save it"},{"metadata":{"trusted":true,"_uuid":"27b8d3f2c0c1d1c4c1e33efa4df9ff79d9e0020c"},"cell_type":"code","source":"seg_model.load_weights(weight_path)\nseg_model.save('seg_model.h5')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"fb12fe0ff3311f45e103b909742b67516cea3b03"},"cell_type":"code","source":"pred_y = seg_model.predict(valid_x)\nprint(pred_y.shape, pred_y.min(), pred_y.max(), pred_y.mean())","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"e80ccfa5727aef7c4cad80bad4edea64929dd30e"},"cell_type":"code","source":"fig, ax = plt.subplots(1, 1, figsize = (8, 6))\nax.hist(pred_y.ravel(), np.linspace(0, 1, 10))\nax.set_xlim(0, 1)\nax.set_yscale('log', nonposy='clip')","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"927eb3aec67bf4695829c879f72187e642bd0681"},"cell_type":"markdown","source":"## Prepare Full Resolution Model\nHere we account for the scaling so everything can happen in the model itself"},{"metadata":{"trusted":true,"_uuid":"fedb2e93cb8c7c14efdd1c7bd5b972330db4c0d8"},"cell_type":"code","source":"if IMG_SCALING is not None:\n    fullres_model = models.Sequential()\n    fullres_model.add(layers.AvgPool2D(IMG_SCALING, input_shape = (None, None, 3)))\n    fullres_model.add(seg_model)\n    fullres_model.add(layers.UpSampling2D(IMG_SCALING))\nelse:\n    fullres_model = seg_model\nfullres_model.save('fullres_model.h5')","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"ae616852f16bc58f1bd91f38bd87c3a05f4044e5"},"cell_type":"markdown","source":"## Load fullres model for testing "},{"metadata":{"_uuid":"b347869c42a09dc248b18ec3fc34a02f9737a2c4"},"cell_type":"raw","source":"fullres_model = models.load_model(\"fullres_model.h5\", compile=False)\nseg_in_shape = fullres_model.get_input_shape_at(0)[1:3]\nseg_out_shape = fullres_model.get_output_shape_at(0)[1:3]\nprint(seg_in_shape, '->', seg_out_shape)"},{"metadata":{"trusted":true,"_uuid":"1b4e8561bbeeda5abed2fd976c44b98452345e3a"},"cell_type":"code","source":"test_paths = os.listdir(TEST)\nprint(len(test_paths), 'test images found')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"6b147920af1d2e8f1080cf74c9e40bed25b41413"},"cell_type":"code","source":"NUMBER_OF_IMG = 40\nIMG_SIZE = 6\nnum_of_row = math.ceil(NUMBER_OF_IMG / 2)\n\nfig, ax = plt.subplots(num_of_row, 4, sharex='col', sharey='row', figsize = (IMG_SIZE*4, IMG_SIZE*num_of_row))\n\nrandom.shuffle(test_paths)\nfor i, imgid in enumerate(test_paths[:NUMBER_OF_IMG]):\n    col = i // num_of_row * 2\n    row = i % num_of_row\n    \n    img = get_image_data(imgid, \"Test\")\n    img = np.expand_dims(img, 0)/255.0\n    img_seg = fullres_model.predict(img)\n    \n    ax[row, col].imshow(img[0])\n    ax[row, col+1].imshow(img_seg[0][:, :, 0], vmin = 0, vmax = 1)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"e5b2b4f441d7d73baa3a2573f280b59a0b3f478e"},"cell_type":"markdown","source":"## Look at colour distributions between images with ships and those without.\n\nLets look at 250 of each, sampled at random."},{"metadata":{"trusted":true,"_uuid":"642e23bc325641393361420a4f3c3ae290a42121"},"cell_type":"code","source":"fig, ax = plt.subplots(1, 2, sharex='col', sharey='row')\nfig.set_size_inches(24, 7)\n\nmask = masks.EncodedPixels.isna()\nfor i, (msk, label) in enumerate(zip([mask, ~mask], ['No Ships', 'Ships'])):\n    _ids = masks[msk].ImageId.sample(250)\n    imgs = np.array([get_image_data(_id, \"Train\") for _id in _ids])\n    \n    red = imgs[:, :, :, 0]\n    green = imgs[:, :, :, 1]\n    blue = imgs[:, :, :, 2]\n    \n    ax[i].plot(np.bincount(red.ravel()), color='orangered', label='red', lw=2)\n    ax[i].plot(np.bincount(green.ravel()), color='yellowgreen', label='green', lw=2)\n    ax[i].plot(np.bincount(blue.ravel()), color='skyblue', label='blue', lw=2)\n    ax[i].legend()\n    ax[i].title.set_text(label)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"collapsed":true,"_uuid":"d2e4c851dd1b61e23dc5e5f8efa23cc4a60eb7b3"},"cell_type":"markdown","source":"## Look at colour distributions of areas with no ships and ships themselves."},{"metadata":{"trusted":true,"_uuid":"9672423a9db4d5ec4aae6cbbc2220fb61bb84261"},"cell_type":"code","source":"def apply_masks_to_img(img, _id, df):\n    '''Apply masks to image given img, its id and the dataframe.'''\n    masks = df[df.ImageId == _id].EncodedPixels.apply(lambda x: rle_decode(x)).tolist()\n    masks = sum(masks)\n    return img * masks.reshape(img.shape[0], img.shape[1], 1)\n\n\nfig, ax = plt.subplots(1, 2, sharex='col')#, sharey='row')\nfig.set_size_inches(24, 7)\n\nmask = masks.EncodedPixels.isna()\nfor i, (msk, label) in enumerate(zip([mask, ~mask], ['No Ships', 'Ships'])):\n    _ids = masks[msk].ImageId.sample(250)\n    imgs = [get_image_data(_id, \"Train\") for _id in _ids]\n    \n    # if we have an encoding to decode\n    if i == 1:\n        imgs = [apply_masks_to_img(i, _id, masks) for (i, _id) in zip(imgs, _ids)]\n\n    imgs = np.array(imgs)\n    red = imgs[:, :, :, 0]\n    green = imgs[:, :, :, 1]\n    blue = imgs[:, :, :, 2]\n    \n    # skip bincount index 0 to avoid the masked pixels to overpower the others.\n    ax[i].plot(np.bincount(red.ravel())[1:], color='orangered', label='red', lw=2)\n    ax[i].plot(np.bincount(green.ravel())[1:], color='yellowgreen', label='green', lw=2)\n    ax[i].plot(np.bincount(blue.ravel())[1:], color='skyblue', label='blue', lw=2)\n    ax[i].legend()\n    ax[i].title.set_text(label)","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"name":"python","version":"3.6.6","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"}},"nbformat":4,"nbformat_minor":1}