{"cells":[{"metadata":{},"cell_type":"markdown","source":"reference : https://www.kaggle.com/hmendonca/u-net-model-with-submission"},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"#!pip install fastai\n#!pip show fastai\n#from fastai import *\n#from fastai.conv_learner import *\n#from fastai.dataset import *\n\nimport os\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\nfrom skimage.io import imread\nimport matplotlib.pyplot as plt\nfrom matplotlib.cm import get_cmap\nfrom skimage.segmentation import mark_boundaries\nfrom skimage.util import montage\nfrom skimage.morphology import binary_opening, disk, label\nimport gc; gc.enable() # memory is tight","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Settings"},{"metadata":{"trusted":true},"cell_type":"code","source":"train_image_dir = '../input/airbus-ship-detection/train_v2/'\n\nSAMPLES_PER_GROUP = 40000\nBATCH_SIZE = 48\nVALID_IMG_COUNT = 900\n# downsampling in preprocessing\nIMG_SCALING = (3, 3)\n\n# related to model fitting and prediction\n# maximum number of steps_per_epoch in training\nMAX_TRAIN_STEPS = 9\nMAX_TRAIN_EPOCHS = 99\nAUGMENT_BRIGHTNESS = False","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Useful functions"},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"from skimage.morphology import label\ndef multi_rle_encode(img):\n    labels = label(img[:, :, 0])\n    return [rle_encode(labels==k) 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):\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\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    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\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.uint8)\n    for mask in in_mask_list:\n        if isinstance(mask, str):\n            all_masks |= rle_decode(mask)\n    return all_masks","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Get data"},{"metadata":{"trusted":true},"cell_type":"code","source":"data = pd.read_csv('../input/airbus-ship-detection/train_ship_segmentations_v2.csv')\n    \ndata.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"print(type(data['EncodedPixels'][0]))\nprint(type(data['EncodedPixels'][2]))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":": when NaN, type is 'float'<br>\n  when not NaN, type is 'str'"},{"metadata":{},"cell_type":"markdown","source":"### Count ships per image"},{"metadata":{"trusted":true},"cell_type":"code","source":"data['ships'] = data['EncodedPixels'].map(lambda c_row: 1 if isinstance(c_row, str) else 0)\nunique_img_ids = data.groupby('ImageId').agg({'ships': 'sum'}).reset_index()\nunique_img_ids['has_ship'] = unique_img_ids['ships'].map(lambda x: 1.0 if x>0 else 0.0)\n#unique_img_ids['has_ship_vec'] = unique_img_ids['has_ship'].map(lambda x: [x])\n#unique_img_ids['file_size_kb'] = unique_img_ids['ImageId'].map(lambda c_img_id: os.stat('../input/airbus-ship-detection/train_v2/'+c_img_id).st_size/1024)\n#unique_img_ids = unique_img_ids[unique_img_ids['file_size_kb'] > 50]\n\ndata.drop(['ships'], axis=1, inplace=True)\n\nunique_img_ids.head(5)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Undersample images"},{"metadata":{"trusted":true},"cell_type":"code","source":"unique_img_ids['ships'].hist(bins=unique_img_ids['ships'].max())","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"images with 0 ships are too many\n\n=> undersample images regarding to log scale counts"},{"metadata":{"trusted":true},"cell_type":"code","source":"balanced_train_df = unique_img_ids.groupby('ships').apply(lambda x: x.sample(SAMPLES_PER_GROUP) if len(x) > SAMPLES_PER_GROUP else x)\nbalanced_train_df['ships'].hist(bins=balanced_train_df['ships'].max()+1)\nprint(balanced_train_df.shape[0], 'masks')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Split data into train and validation set"},{"metadata":{"trusted":true},"cell_type":"code","source":"from sklearn.model_selection import train_test_split\ntrain_ids, valid_ids = train_test_split(balanced_train_df, \n                 test_size = 0.2, \n                 stratify = balanced_train_df['ships'])\ntrain_df = pd.merge(data, train_ids)\nvalid_df = pd.merge(data, valid_ids)\nprint(train_df.shape[0], 'training masks')\nprint(valid_df.shape[0], 'validation masks')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Decode all RLEs into Images"},{"metadata":{"trusted":true},"cell_type":"code","source":"def make_image_gen(in_df, batch_size = BATCH_SIZE):\n    all_batches = list(in_df.groupby('ImageId'))\n    out_rgb = []\n    out_mask = []\n    while True:\n        np.random.shuffle(all_batches)\n        for c_img_id, c_masks in all_batches:\n            rgb_path = os.path.join(train_image_dir, c_img_id)\n            c_img = imread(rgb_path)\n            c_mask = np.expand_dims(masks_as_image(c_masks['EncodedPixels'].values), -1)\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=[], []\n                \ntrain_gen = make_image_gen(train_df)\nprint('make generater completed')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_x, train_y = next(train_gen)\nprint('x', train_x.shape)\nprint('y', train_y.shape)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"check by visual"},{"metadata":{"trusted":true},"cell_type":"code","source":"montage_rgb = lambda x: np.stack([montage(x[:, :, :, i]) for i in range(x.shape[3])], -1)\n\nfig, (ax1, ax2, ax3) = plt.subplots(1, 3, figsize = (30, 10))\nbatch_rgb = montage_rgb(train_x)\nbatch_seg = montage(train_y[:, :, :, 0])\nax1.imshow(batch_rgb)\nax1.set_title('Images')\nax2.imshow(batch_seg)\nax2.set_title('Segmentations')\nax3.imshow(mark_boundaries(batch_rgb, \n                           batch_seg.astype(int)))\nax3.set_title('Outlined Ships')\nfig.savefig('overview.png')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"this time, skip data augmentation"},{"metadata":{},"cell_type":"markdown","source":"### Make validation batch"},{"metadata":{"trusted":true},"cell_type":"code","source":"%%time\nvalid_gen = make_image_gen(valid_df, VALID_IMG_COUNT)\nvalid_x, valid_y = next(valid_gen)\nprint(valid_x.shape, valid_y.shape)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"gc.collect()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Build U-Net Model"},{"metadata":{},"cell_type":"markdown","source":"![u-net](https://miro.medium.com/max/2772/1*ovEGmOI3bcCeauu8jEBzsg.png)"},{"metadata":{},"cell_type":"markdown","source":"make this image into model (never mind the depth of tensors - due to time issue)"},{"metadata":{"trusted":true},"cell_type":"code","source":"from keras import models, layers\n\ninput_img = layers.Input(train_x.shape[1:], name = 'RGB_Input')\npp_in_layer = input_img\n\n#pp_in_layer = layers.GaussianNoise(GAUSSIAN_NOISE)(pp_in_layer)\n#pp_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)\n\nu5 = layers.Conv2DTranspose(32, (2, 2), strides=(2, 2), padding='same') (c4)\nu5 = layers.concatenate([u5, c3])\nc5 = layers.Conv2D(32, (3, 3), activation='relu', padding='same') (u5)\nc5 = layers.Conv2D(32, (3, 3), activation='relu', padding='same') (c5)\n\nu6 = layers.Conv2DTranspose(16, (2, 2), strides=(2, 2), padding='same') (c5)\nu6 = layers.concatenate([u6, c2])\nc6 = layers.Conv2D(16, (3, 3), activation='relu', padding='same') (u6)\nc6 = layers.Conv2D(16, (3, 3), activation='relu', padding='same') (c6)\n\nu7 = layers.Conv2DTranspose(8, (2, 2), strides=(2, 2), padding='same') (c6)\nu7 = layers.concatenate([u7, c1], axis=3)\nc7 = layers.Conv2D(8, (3, 3), activation='relu', padding='same') (u7)\nc7 = layers.Conv2D(8, (3, 3), activation='relu', padding='same') (c7)\n\nd = layers.Conv2D(1, (1, 1), activation='sigmoid') (c7)\n\nseg_model = models.Model(inputs=[input_img], outputs=[d])\nseg_model.summary()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### fitting model"},{"metadata":{"trusted":true},"cell_type":"code","source":"import keras.backend as K\nfrom keras.optimizers import Adam\nfrom keras.losses import binary_crossentropy\nimport tensorflow as tf\n\nsess = tf.compat.v1.Session()\n\n## intersection over union\ndef IoU(y_true, y_pred, eps=1e-6):\n    #if K.sum(y_true, axis=[1,2,3]) == 0.0:\n    #    return IoU(1-y_true, 1-y_pred) ## empty image; calc IoU of zeros\n    intersection = K.sum(y_true * y_pred, axis=[1,2,3])\n    union = K.sum(y_true, axis=[1,2,3]) + K.sum(y_pred, axis=[1,2,3]) - intersection\n    return -K.mean( (intersection + eps) / (union + eps), axis=0)\nfrom keras.callbacks import ModelCheckpoint, LearningRateScheduler, EarlyStopping, ReduceLROnPlateau\nweight_path=\"{}_weights.best.hdf5\".format('seg_model')\n\ncheckpoint = ModelCheckpoint(weight_path, monitor='val_loss', verbose=1, save_best_only=True, mode='min', save_weights_only=True)\n\nreduceLROnPlat = ReduceLROnPlateau(monitor='val_loss', factor=0.33,\n                                   patience=1, verbose=1, mode='min',\n                                   min_delta=0.0001, cooldown=0, min_lr=1e-8)\n\nearly = EarlyStopping(monitor=\"val_loss\", mode=\"min\", verbose=2,\n                      patience=20) # probably needs to be more patient, but kaggle time is limited\n\ncallbacks_list = [checkpoint, early, reduceLROnPlat]\ndef fit():\n    seg_model.compile(optimizer=Adam(1e-3, decay=1e-6), loss=IoU, metrics=['binary_accuracy'])\n    \n    step_count = min(MAX_TRAIN_STEPS, train_df.shape[0]//BATCH_SIZE)\n    #aug_gen = create_aug_gen(make_image_gen(train_df))\n    loss_history = [seg_model.fit_generator(train_gen,\n                                 steps_per_epoch=step_count,\n                                 epochs=MAX_TRAIN_EPOCHS,\n                                 validation_data=(valid_x, valid_y),\n                                 callbacks=callbacks_list,\n                                workers=1 # the generator is not very thread safe\n                                           )]\n    return loss_history\n\nwhile True:\n    loss_history = fit()\n    if np.min([mh.history['val_loss'] for mh in loss_history]) < 0.2:#< -0.2\n        break","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### prediction"},{"metadata":{"trusted":true},"cell_type":"code","source":"seg_model.load_weights(weight_path)\n#seg_model.save('seg_model.h5') # save for later use\n\nif 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":{"trusted":true},"cell_type":"code","source":"test_image_dir = '../input/airbus-ship-detection/test_v2/'\ndef raw_prediction(img, path=test_image_dir):\n    c_img = imread(os.path.join(path, img))\n    c_img = np.expand_dims(c_img, 0)/255.0\n    cur_seg = fullres_model.predict(c_img)[0]\n    return cur_seg, c_img[0]\n\ndef predict(img, path=test_image_dir):\n    cur_seg, c_img = raw_prediction(img, path=path)\n    return cur_seg, c_img","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from tqdm import tqdm_notebook\n\ntest_paths = np.array(os.listdir(test_image_dir))\n\ndef pred_encode(img):\n    cur_seg, _ = predict(img)\n    cur_rles = multi_rle_encode(cur_seg)\n    return [[img, rle] for rle in cur_rles if rle is not None]\n\nout_pred_rows = []\nfor c_img_name in tqdm_notebook(test_paths): ## only a subset as it takes too long to run\n    out_pred_rows += pred_encode(c_img_name)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"print(out_pred_rows)\nsub = pd.DataFrame([['*.jpg', '1 2']] + out_pred_rows)\nsub.columns = ['ImageId', 'EncodedPixels']\nsub = sub.drop(0,0)\nsub = sub[sub.EncodedPixels.notnull()]\nsub.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"sub1 = pd.read_csv('../input/airbus-ship-detection/sample_submission_v2.csv')\nsub1 = pd.DataFrame(np.setdiff1d(sub1['ImageId'].unique(), sub['ImageId'].unique(), assume_unique=True), columns=['ImageId'])\nsub1['EncodedPixels'] = None\nprint(len(sub1), len(sub))\n\nsub = pd.concat([sub, sub1])\nprint(len(sub))\nsub.to_csv('submission1.csv', index=False)\nsub.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"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":1}