{"cells":[{"metadata":{"trusted":true},"cell_type":"code","source":"# import re\n# import tensorflow as tf\n# import numpy as np\n# from matplotlib import pyplot as plt\n# print(\"Tensorflow version \" + tf.__version__)\n# AUTO = tf.data.experimental.AUTOTUNE\n# from kaggle_datasets import KaggleDatasets\n# # Detect hardware, return appropriate distribution strategy\n# try:\n#     tpu = tf.distribute.cluster_resolver.TPUClusterResolver()  # TPU detection. No parameters necessary if TPU_NAME environment variable is set. On Kaggle this is always the case.\n#     print('Running on TPU ', tpu.master())\n# except ValueError:\n#     tpu = None\n\n# if tpu:\n#     tf.config.experimental_connect_to_cluster(tpu)\n#     tf.tpu.experimental.initialize_tpu_system(tpu)\n#     strategy = tf.distribute.experimental.TPUStrategy(tpu)\n# else:\n#     strategy = tf.distribute.get_strategy() # default distribution strategy in Tensorflow. Works on CPU and single GPU.\n\n# print(\"REPLICAS: \", strategy.num_replicas_in_sync)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"a6cd9d5ad61ffe3b8858769f20a5f9493f024a56"},"cell_type":"markdown","source":"## Model Parameters\nWe might want to adjust these later (or do some hyperparameter optimizations)\n模型参数\n\n我们以后可能需要调整这些（或者做一些超参数优化）"},{"metadata":{"trusted":true,"_uuid":"301a5d939c566d1487a049bb2554d09b592b18b1"},"cell_type":"code","source":"BATCH_SIZE = 48\nEDGE_CROP = 16\nGAUSSIAN_NOISE = 0.1\nUPSAMPLE_MODE = 'SIMPLE'\n# downsampling inside the network\nNET_SCALING = (1, 1)\n# downsampling in preprocessing\nIMG_SCALING = (3, 3)\n# number of validation images to use\nVALID_IMG_COUNT = 900\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":{"trusted":true},"cell_type":"code","source":"import 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\n# from skimage.util import montage2d as montage\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":{"trusted":true},"cell_type":"code","source":"montage_rgb = lambda x: np.stack([montage(x[:, :, :, i]) for i in range(x.shape[3])], -1)\nship_dir = '../input'\ntrain_image_dir = os.path.join(ship_dir, 'train_v2')\ntest_image_dir = os.path.join(ship_dir, 'test_v2')\n","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"def multi_rle_encode(img, **kwargs):\n    '''\n    Encode connected regions as separated masks\n    将连接区域编码为分离的掩码\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)\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\n\ndef masks_as_color(in_mask_list):\n    # Take the individual ship masks and create a color mask array for each ships\n    all_masks = np.zeros((768, 768), dtype = np.float)\n    scale = lambda x: (len(in_mask_list)+x+1) / (len(in_mask_list)*2) ## scale the heatmap image to shift \n    for i,mask in enumerate(in_mask_list):\n        if isinstance(mask, str):\n            all_masks[:,:] += scale(i) * rle_decode(mask)\n    return all_masks","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"3ca7119188fbb4c6540d9df55f5833b55435287e"},"cell_type":"code","source":"masks = pd.read_csv(os.path.join('../input', 'train_ship_segmentations_v2.csv'))\nnot_empty = pd.notna(masks.EncodedPixels)\nprint(not_empty.sum(), 'masks in', masks[not_empty].ImageId.nunique(), 'images')#非空图片中的mask数量\nprint((~not_empty).sum(), 'empty images in', masks.ImageId.nunique(), 'total images')#所有图片中非空图片\nmasks.head()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"fdedd5965f47f84aa8f3aab1cad978512781a1cc"},"cell_type":"markdown","source":"# Make sure encode/decode works\nGiven the process\n$$  RLE_0 \\stackrel{Decode}{\\longrightarrow} \\textrm{Image}_0 \\stackrel{Encode}{\\longrightarrow} RLE_1 \\stackrel{Decode}{\\longrightarrow} \\textrm{Image}_1 $$\nWe want to check if/that\n$ \\textrm{Image}_0 \\stackrel{?}{=} \\textrm{Image}_1 $\nWe 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.\n\n"},{"metadata":{"trusted":true,"_uuid":"0081fd6f387abd7c05eb35f29575a2ee6ddc2236"},"cell_type":"code","source":"fig, (ax1, ax2, ax3, ax4) = plt.subplots(1, 4, figsize = (16, 5))\n#划分图表分布\nrle_0 = masks.query('ImageId==\"00021ddc3.jpg\"')['EncodedPixels']\nimg_0 = masks_as_image(rle_0)\nax1.imshow(img_0)\nax1.set_title('Mask as image')\nrle_1 = multi_rle_encode(img_0)\nimg_1 = masks_as_image(rle_1)\nax2.imshow(img_1)\nax2.set_title('Re-encoded')\nimg_c = masks_as_color(rle_0)\nax3.imshow(img_c)\nax3.set_title('Masks in colors')\nimg_c = masks_as_color(rle_1)\nax4.imshow(img_c)\nax4.set_title('Re-encoded in colors')\nprint('Check Decoding->Encoding',\n      'RLE_0:', len(rle_0), '->',\n      'RLE_1:', len(rle_1))\nprint(np.sum(img_0 - img_1), 'error')","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"40cb72e241c0c3d8bc245b4e3c663b4a835b0011"},"cell_type":"markdown","source":"# Split into training and validation groups\nWe stratify by the number of boats appearing so we have nice balances in each set\n#分成培训和验证组\n我们根据出现的船只数量进行分层，所以每套都有很好的平衡"},{"metadata":{"trusted":true},"cell_type":"code","source":"masks['ships'] = masks['EncodedPixels'].map(lambda c_row: 1 if isinstance(c_row, str) else 0)\nmasks.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"unique_img_ids = masks.groupby('ImageId').agg({'ships': 'sum'}).reset_index()\nunique_img_ids.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"unique_img_ids['has_ship'] = unique_img_ids['ships'].map(lambda x: 1.0 if x>0 else 0.0)\nunique_img_ids.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"unique_img_ids['has_ship_vec'] = unique_img_ids['has_ship'].map(lambda x: [x])\nunique_img_ids['file_size_kb'] = unique_img_ids['ImageId'].map(lambda c_img_id: \n                                                               os.stat(os.path.join(train_image_dir, \n                                                                                    c_img_id)).st_size/1024)\nunique_img_ids.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"c4f008bf6898518fd371de013418f936edaa09f8"},"cell_type":"code","source":"unique_img_ids = unique_img_ids[unique_img_ids['file_size_kb'] > 50] # keep only +50kb files\nunique_img_ids['file_size_kb'].hist()#绘制直方图\nmasks.drop(['ships'], axis=1, inplace=True)\nunique_img_ids.sample(7)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"c21d5bff04bf9180463969ac120379345745ed03"},"cell_type":"markdown","source":"### Examine Number of Ship Images\nHere we examine how often ships appear and replace the ones without any ships with 0\n###检查船舶图像数量\n\n这里我们检查船只出现的频率，并用0替换没有船只的"},{"metadata":{"trusted":true,"_uuid":"2612fa47c7e9fdcaa7aa720c4e15fc86fd65d69a"},"cell_type":"code","source":"unique_img_ids['ships'].hist(bins=unique_img_ids['ships'].max())","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"unique_img_ids = unique_img_ids[unique_img_ids['ships']!=0]","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"ef8115a80749ac47f295e9a70217a5553970c2b3"},"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\n#欠采样空图像\n在这里，我们对空图像进行欠采样，以获得一个更好的平衡组，其中有更多的船要尝试分割"},{"metadata":{"trusted":true,"_uuid":"0cf0bb261eda957cb0a12a330260e1390c57c8c9"},"cell_type":"code","source":"SAMPLES_PER_GROUP = 1800\nbalanced_train_df = unique_img_ids.groupby('ships').apply(lambda x: x.sample(SAMPLES_PER_GROUP) if len(x) > SAMPLES_PER_GROUP else x)\n#图片有相同船舶数量，但超出2000的不要\nbalanced_train_df['ships'].hist(bins=balanced_train_df['ships'].max())\nprint(balanced_train_df.shape[0], 'masks')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"balanced_train_df=balanced_train_df.reset_index(drop = True)#删除原来的索引。\nbalanced_train_df=balanced_train_df.sample(frac=1.0)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"a26cd030942c2cd763c6fcd08b370f886c93ecdf"},"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'])\n#stratify使训练和测试的ships比例一样\ntrain_df = pd.merge(masks, train_ids)\nvalid_df = pd.merge(masks, valid_ids)\nprint(train_df.shape[0], 'training masks')\nprint(valid_df.shape[0], 'validation masks')","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"a3fb9fe33d81374c7bd836f5bc86a1df89190805"},"cell_type":"markdown","source":"# Decode all the RLEs into Images\nWe make a generator to produce batches of images\n#把所有的RLE解码成图像\n我们制造一个生成器来产生成批的图像"},{"metadata":{"trusted":true,"_uuid":"6181ac51577e5636995e38a9e29311cf47f513ca"},"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=[], []","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"1983738da75b031f2bec8ba36db01c095e7c5d59"},"cell_type":"code","source":"train_gen = make_image_gen(train_df)\ntrain_x, train_y = next(train_gen) #返回迭代器的下一个项目。\nprint('x', train_x.shape, train_x.min(), train_x.max())\nprint('y', train_y.shape, train_y.min(), train_y.max())","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"b4396cd28ddd2e4c8076fcb165e9b61e3baeeeb7"},"cell_type":"code","source":"fig, (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, batch_seg.astype(int)))#显示遮掩的边缘\nax3.set_title('Outlined Ships')\nfig.savefig('overview.png')","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"8f47639c987a10ebcb53e51f55aa8a11c98fa860"},"cell_type":"markdown","source":"# Make the Validation Set\n#设置验证集"},{"metadata":{"trusted":true,"_uuid":"30cb02a2a7103a9d66e90f701991199de1e5b73e"},"cell_type":"code","source":"%%time\n#将会给出cell的代码运行一次所花费的时间。\nvalid_x, valid_y = next(make_image_gen(valid_df, VALID_IMG_COUNT))\nprint(valid_x.shape, valid_y.shape)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"a8f65e7942816fb75b687a549dc1d5cc48d00e21"},"cell_type":"markdown","source":"# Augment Data\n#扩充数据"},{"metadata":{"_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","trusted":true},"cell_type":"code","source":"from keras.preprocessing.image import ImageDataGenerator\ndg_args = dict(featurewise_center = False, \n                  samplewise_center = False,\n                  rotation_range = 45, \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 \n#亮度可能有问题，因为它似乎改变了不同于图像的标签\nif AUGMENT_BRIGHTNESS:\n    dg_args[' brightness_range'] = [0.5, 1.5]\nimage_gen = ImageDataGenerator(**dg_args)\n#**kwargs 表示关键字参数，它本质上是一个 dict\nif AUGMENT_BRIGHTNESS:\n    dg_args.pop('brightness_range')\nlabel_gen = ImageDataGenerator(**dg_args)\n#pop删除 arrayObject 的最后一个元素\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        ##保持种子同步否则对图像的增强与遮罩不同\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        yield next(g_x)/255.0, next(g_y)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"6122ccb9e58bfac6fa5e11c86121e78d9e5151b1"},"cell_type":"code","source":"cur_gen = create_aug_gen(train_gen)\nt_x, t_y = next(cur_gen)\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# only keep first 9 samples to examine in detail\n#只保留前9个样本进行详细检查\nt_x = t_x[:9]\nt_y = t_y[:9]\nfig, (ax1, ax2) = plt.subplots(1, 2, figsize = (20, 10))\nax1.imshow(montage_rgb(t_x), cmap='gray')\nax1.set_title('images')\nax2.imshow(montage(t_y[:, :, :, 0]), cmap='gray_r')\nax2.set_title('ships')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"33300c4f03b6600da7b418f775d11d7ebf76a35a"},"cell_type":"code","source":"gc.collect()#清理内存","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"ba08494eb9736ec3556b7c879143cdcdea89febf"},"cell_type":"markdown","source":"# Build a Model\n\n"},{"metadata":{"trusted":true},"cell_type":"code","source":"IMAGE_ORDERING = 'channels_last'","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import keras\nfrom keras.models import *\nfrom keras.layers import *\nfrom keras import layers\n\n# Source:\n# https://github.com/fchollet/deep-learning-models/blob/master/resnet50.py\n\n\n\nif IMAGE_ORDERING == 'channels_first':\n    pretrained_url = \"https://github.com/fchollet/deep-learning-models/\" \\\n                     \"releases/download/v0.2/\" \\\n                     \"resnet50_weights_th_dim_ordering_th_kernels_notop.h5\"\nelif IMAGE_ORDERING == 'channels_last':\n    pretrained_url = \"https://github.com/fchollet/deep-learning-models/\" \\\n                     \"releases/download/v0.2/\" \\\n                     \"resnet50_weights_tf_dim_ordering_tf_kernels_notop.h5\"\n\n\ndef one_side_pad(x):\n    x = ZeroPadding2D((1, 1), data_format=IMAGE_ORDERING)(x)\n    if IMAGE_ORDERING == 'channels_first':\n        x = Lambda(lambda x: x[:, :, :-1, :-1])(x)\n    elif IMAGE_ORDERING == 'channels_last':\n        x = Lambda(lambda x: x[:, :-1, :-1, :])(x)\n    return x\n\n\ndef identity_block(input_tensor, kernel_size, filters, stage, block):\n    \"\"\"The identity block is the block that has no conv layer at shortcut.\n    # Arguments\n        input_tensor: input tensor\n        kernel_size: defualt 3, the kernel size of middle conv layer at\n                     main path\n        filters: list of integers, the filterss of 3 conv layer at main path\n        stage: integer, current stage label, used for generating layer names\n        block: 'a','b'..., current block label, used for generating layer names\n    # Returns\n        Output tensor for the block.\n    \"\"\"\n    filters1, filters2, filters3 = filters\n\n    if IMAGE_ORDERING == 'channels_last':\n        bn_axis = 3\n    else:\n        bn_axis = 1\n\n    conv_name_base = 'res' + str(stage) + block + '_branch'\n    bn_name_base = 'bn' + str(stage) + block + '_branch'\n\n    x = Conv2D(filters1, (1, 1), data_format=IMAGE_ORDERING,\n               name=conv_name_base + '2a')(input_tensor)\n    x = BatchNormalization(axis=bn_axis, name=bn_name_base + '2a')(x)\n    x = Activation('relu')(x)\n\n    x = Conv2D(filters2, kernel_size, data_format=IMAGE_ORDERING,\n               padding='same', name=conv_name_base + '2b')(x)\n    x = BatchNormalization(axis=bn_axis, name=bn_name_base + '2b')(x)\n    x = Activation('relu')(x)\n\n    x = Conv2D(filters3, (1, 1), data_format=IMAGE_ORDERING,\n               name=conv_name_base + '2c')(x)\n    x = BatchNormalization(axis=bn_axis, name=bn_name_base + '2c')(x)\n\n    x = layers.add([x, input_tensor])\n    x = Activation('relu')(x)\n    return x\n\n\ndef conv_block(input_tensor, kernel_size, filters, stage, block,\n               strides=(2, 2)):\n    \"\"\"conv_block is the block that has a conv layer at shortcut\n    # Arguments\n        input_tensor: input tensor\n        kernel_size: defualt 3, the kernel size of middle conv layer at\n                     main path\n        filters: list of integers, the filterss of 3 conv layer at main path\n        stage: integer, current stage label, used for generating layer names\n        block: 'a','b'..., current block label, used for generating layer names\n    # Returns\n        Output tensor for the block.\n    Note that from stage 3, the first conv layer at main path is with\n    strides=(2,2) and the shortcut should have strides=(2,2) as well\n    \"\"\"\n    filters1, filters2, filters3 = filters\n\n    if IMAGE_ORDERING == 'channels_last':\n        bn_axis = 3\n    else:\n        bn_axis = 1\n\n    conv_name_base = 'res' + str(stage) + block + '_branch'\n    bn_name_base = 'bn' + str(stage) + block + '_branch'\n\n    x = Conv2D(filters1, (1, 1), data_format=IMAGE_ORDERING, strides=strides,\n               name=conv_name_base + '2a')(input_tensor)\n    x = BatchNormalization(axis=bn_axis, name=bn_name_base + '2a')(x)\n    x = Activation('relu')(x)\n\n    x = Conv2D(filters2, kernel_size, data_format=IMAGE_ORDERING,\n               padding='same', name=conv_name_base + '2b')(x)\n    x = BatchNormalization(axis=bn_axis, name=bn_name_base + '2b')(x)\n    x = Activation('relu')(x)\n\n    x = Conv2D(filters3, (1, 1), data_format=IMAGE_ORDERING,\n               name=conv_name_base + '2c')(x)\n    x = BatchNormalization(axis=bn_axis, name=bn_name_base + '2c')(x)\n\n    shortcut = Conv2D(filters3, (1, 1), data_format=IMAGE_ORDERING,\n                      strides=strides, name=conv_name_base + '1')(input_tensor)\n    shortcut = BatchNormalization(\n        axis=bn_axis, name=bn_name_base + '1')(shortcut)\n\n    x = layers.add([x, shortcut])\n    x = Activation('relu')(x)\n    return x\n\n\ndef get_resnet50_encoder(input_height=256,  input_width=256,\n                         pretrained=None,\n                         include_top=True, weights='imagenet',\n                         input_tensor=None, input_shape=None,\n                         pooling=None,\n                         classes=1000):\n\n    assert input_height % 32 == 0\n    assert input_width % 32 == 0\n\n    if IMAGE_ORDERING == 'channels_first':\n        img_input = Input(shape=(3, input_height, input_width))\n    elif IMAGE_ORDERING == 'channels_last':\n        img_input = Input(shape=(input_height, input_width, 3))\n\n    if IMAGE_ORDERING == 'channels_last':\n        bn_axis = 3\n    else:\n        bn_axis = 1\n\n    x = ZeroPadding2D((3, 3), data_format=IMAGE_ORDERING)(img_input)\n    x = Conv2D(64, (7, 7), data_format=IMAGE_ORDERING,\n               strides=(2, 2), name='conv1')(x)\n    f1 = x\n\n    x = BatchNormalization(axis=bn_axis, name='bn_conv1')(x)\n    x = Activation('relu')(x)\n    x = MaxPooling2D((3, 3), data_format=IMAGE_ORDERING, strides=(2, 2))(x)\n\n    x = conv_block(x, 3, [64, 64, 256], stage=2, block='a', strides=(1, 1))\n    x = identity_block(x, 3, [64, 64, 256], stage=2, block='b')\n    x = identity_block(x, 3, [64, 64, 256], stage=2, block='c')\n    f2 = one_side_pad(x)\n\n    x = conv_block(x, 3, [128, 128, 512], stage=3, block='a')\n    x = identity_block(x, 3, [128, 128, 512], stage=3, block='b')\n    x = identity_block(x, 3, [128, 128, 512], stage=3, block='c')\n    x = identity_block(x, 3, [128, 128, 512], stage=3, block='d')\n    f3 = x\n\n    x = conv_block(x, 3, [256, 256, 1024], stage=4, block='a')\n    x = identity_block(x, 3, [256, 256, 1024], stage=4, block='b')\n    x = identity_block(x, 3, [256, 256, 1024], stage=4, block='c')\n    x = identity_block(x, 3, [256, 256, 1024], stage=4, block='d')\n    x = identity_block(x, 3, [256, 256, 1024], stage=4, block='e')\n    x = identity_block(x, 3, [256, 256, 1024], stage=4, block='f')\n    f4 = x\n\n    x = conv_block(x, 3, [512, 512, 2048], stage=5, block='a')\n    x = identity_block(x, 3, [512, 512, 2048], stage=5, block='b')\n    x = identity_block(x, 3, [512, 512, 2048], stage=5, block='c')\n    f5 = x\n\n    x = AveragePooling2D(\n        (7, 7), data_format=IMAGE_ORDERING, name='avg_pool')(x)\n    # f6 = x\n\n    if pretrained == 'imagenet':\n        weights_path = keras.utils.get_file(\n            pretrained_url.split(\"/\")[-1], pretrained_url)\n        Model(img_input, x).load_weights(weights_path)\n\n    return img_input, [f1, f2, f3, f4, f5]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from types import MethodType\n\ndef get_segmentation_model(input, output):\n\n    img_input = input\n    o = output\n\n#     o_shape = Model(img_input, o).output_shape\n#     i_shape = Model(img_input, o).input_shape\n\n#     if IMAGE_ORDERING == 'channels_first':\n#         output_height = o_shape[2]\n#         output_width = o_shape[3]\n#         input_height = i_shape[2]\n#         input_width = i_shape[3]\n#         n_classes = o_shape[1]\n#         o = (Reshape((-1, output_height*output_width)))(o)\n#         o = (Permute((2, 1)))(o)\n#     elif IMAGE_ORDERING == 'channels_last':\n#         output_height = o_shape[1]\n#         output_width = o_shape[2]\n#         input_height = i_shape[1]\n#         input_width = i_shape[2]\n#         n_classes = o_shape[3]\n#         o = (Reshape((output_height*output_width, -1)))(o)\n\n    o = (Activation('softmax'))(o)\n    model = Model(img_input, o)\n    model.output_width = output_width\n    model.output_height = output_height\n    model.n_classes = n_classes\n    model.input_height = input_height\n    model.input_width = input_width\n    model.model_name = \"\"\n\n#     model.train = MethodType(train, model)\n#     model.predict_segmentation = MethodType(predict, model)\n#     model.predict_multiple = MethodType(predict_multiple, model)\n#     model.evaluate_segmentation = MethodType(evaluate, model)\n\n    return model\n\n\n\n\n\nif IMAGE_ORDERING == 'channels_first':\n    MERGE_AXIS = 1\nelif IMAGE_ORDERING == 'channels_last':\n    MERGE_AXIS = -1\n\n\ndef unet_mini(n_classes, input_height=256, input_width=256):\n\n    if IMAGE_ORDERING == 'channels_first':\n        img_input = Input(shape=(3, input_height, input_width))\n    elif IMAGE_ORDERING == 'channels_last':\n        img_input = Input(shape=(input_height, input_width, 3))\n\n    conv1 = Conv2D(32, (3, 3), data_format=IMAGE_ORDERING,\n                   activation='relu', padding='same')(img_input)\n    conv1 = Dropout(0.2)(conv1)\n    conv1 = Conv2D(32, (3, 3), data_format=IMAGE_ORDERING,\n                   activation='relu', padding='same')(conv1)\n    pool1 = MaxPooling2D((2, 2), data_format=IMAGE_ORDERING)(conv1)\n\n    conv2 = Conv2D(64, (3, 3), data_format=IMAGE_ORDERING,\n                   activation='relu', padding='same')(pool1)\n    conv2 = Dropout(0.2)(conv2)\n    conv2 = Conv2D(64, (3, 3), data_format=IMAGE_ORDERING,\n                   activation='relu', padding='same')(conv2)\n    pool2 = MaxPooling2D((2, 2), data_format=IMAGE_ORDERING)(conv2)\n\n    conv3 = Conv2D(128, (3, 3), data_format=IMAGE_ORDERING,\n                   activation='relu', padding='same')(pool2)\n    conv3 = Dropout(0.2)(conv3)\n    conv3 = Conv2D(128, (3, 3), data_format=IMAGE_ORDERING,\n                   activation='relu', padding='same')(conv3)\n\n    up1 = concatenate([UpSampling2D((2, 2), data_format=IMAGE_ORDERING)(\n        conv3), conv2], axis=MERGE_AXIS)\n    conv4 = Conv2D(64, (3, 3), data_format=IMAGE_ORDERING,\n                   activation='relu', padding='same')(up1)\n    conv4 = Dropout(0.2)(conv4)\n    conv4 = Conv2D(64, (3, 3), data_format=IMAGE_ORDERING,\n                   activation='relu', padding='same')(conv4)\n\n    up2 = concatenate([UpSampling2D((2, 2), data_format=IMAGE_ORDERING)(\n        conv4), conv1], axis=MERGE_AXIS)\n    conv5 = Conv2D(32, (3, 3), data_format=IMAGE_ORDERING,\n                   activation='relu', padding='same')(up2)\n    conv5 = Dropout(0.2)(conv5)\n    conv5 = Conv2D(32, (3, 3), data_format=IMAGE_ORDERING,\n                   activation='relu', padding='same')(conv5)\n\n    o = Conv2D(n_classes, (1, 1), data_format=IMAGE_ORDERING,\n               padding='same')(conv5)\n\n    model = get_segmentation_model(img_input, o)\n    model.model_name = \"unet_mini\"\n    return model\n\n\ndef _unet(n_classes, encoder, l1_skip_conn=True, input_height=256,\n          input_width=256):\n\n    img_input, levels = encoder(\n        input_height=input_height, input_width=input_width)\n    [f1, f2, f3, f4, f5] = levels\n\n    o = f4\n\n    o = (ZeroPadding2D((1, 1), data_format=IMAGE_ORDERING))(o)\n    o = (Conv2D(512, (3, 3), padding='valid', data_format=IMAGE_ORDERING))(o)\n    o = (BatchNormalization())(o)\n\n    o = (UpSampling2D((2, 2), data_format=IMAGE_ORDERING))(o)\n    o = (concatenate([o, f3], axis=MERGE_AXIS))\n    o = (ZeroPadding2D((1, 1), data_format=IMAGE_ORDERING))(o)\n    o = (Conv2D(256, (3, 3), padding='valid', data_format=IMAGE_ORDERING))(o)\n    o = (BatchNormalization())(o)\n\n    o = (UpSampling2D((2, 2), data_format=IMAGE_ORDERING))(o)\n    o = (concatenate([o, f2], axis=MERGE_AXIS))\n    o = (ZeroPadding2D((1, 1), data_format=IMAGE_ORDERING))(o)\n    o = (Conv2D(128, (3, 3), padding='valid', data_format=IMAGE_ORDERING))(o)\n    o = (BatchNormalization())(o)\n\n    o = (UpSampling2D((2, 2), data_format=IMAGE_ORDERING))(o)\n\n    if l1_skip_conn:\n        o = (concatenate([o, f1], axis=MERGE_AXIS))\n\n    o = (ZeroPadding2D((1, 1), data_format=IMAGE_ORDERING))(o)\n    o = (Conv2D(32, (3, 3), padding='valid', data_format=IMAGE_ORDERING))(o)\n    o = (BatchNormalization())(o)\n    \n    o = (UpSampling2D((2, 2), data_format=IMAGE_ORDERING))(o)\n    o = Conv2D(1, (1, 1), padding='same',\n               data_format=IMAGE_ORDERING)(o)\n\n    model =Model(input=img_input, output=o, name=\"resnet50_unet\")\n\n    return model\n\n\ndef unet(n_classes, input_height=416, input_width=608, encoder_level=3):\n\n    model = _unet(n_classes, vanilla_encoder,\n                  input_height=input_height, input_width=input_width)\n    model.model_name = \"unet\"\n    return model\n\n\ndef resnet50_unet(n_classes=2, input_height=256, input_width=256,\n                  encoder_level=3):\n\n    model = _unet(n_classes, get_resnet50_encoder,\n                  input_height=input_height, input_width=input_width)\n    model.model_name = \"resnet50_unet\"\n    return model","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"seg_model =resnet50_unet()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# seg_model = mobilenet_unet(n_classes=1)\n#     # model.summary()\n# BASE_WEIGHT_PATH = ('https://github.com/fchollet/deep-learning-models/''releases/download/v0.6/')\n# model_name = 'mobilenet_%s_%d_tf_no_top.h5' % ( '1_0' , 224 )\n\n# weight_path = BASE_WEIGHT_PATH + model_name\n# weights_path = keras.utils.get_file(model_name, weight_path )","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# seg_model=_unet()\n# seg_model.summary()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"1678069aa8013510264ba898291c6ae2dce88a76"},"cell_type":"code","source":"import keras.backend as K\nfrom keras.optimizers import Adam\nfrom keras.losses import binary_crossentropy","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def IoU(y_true, y_pred, eps=1e-6):\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)\n\ndef IoUloss(y_true, y_pred, eps=1e-6):\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    loss=1-(K.mean( (intersection + eps) / (union + eps), axis=0))\n    return loss","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"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):\n    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 binary_crossentropy(y_true, y_pred) + dice_loss(y_true, y_pred)\n\ndef bce_logdice_loss(y_true, y_pred):\n    return binary_crossentropy(y_true, y_pred) - K.log(1. - dice_loss(y_true, y_pred))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def tversky(y_true, y_pred):\n    smooth=1\n    y_true_pos = K.flatten(y_true)\n    y_pred_pos = K.flatten(y_pred)\n    true_pos = K.sum(y_true_pos * y_pred_pos)\n    false_neg = K.sum(y_true_pos * (1-y_pred_pos))\n    false_pos = K.sum((1-y_true_pos)*y_pred_pos)\n    alpha = 0.7\n    return (true_pos + smooth)/(true_pos + alpha*false_neg + (1-alpha)*false_pos + smooth)\n\ndef tversky_loss(y_true, y_pred):\n    return 1 - tversky(y_true,y_pred)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def binary_iou_focal_loss(gamma=2, alpha=0.25):\n    \"\"\"\n    Binary form of focal loss.\n    适用于二分类问题的focal loss\n    \n    focal_loss(p_t) = -alpha_t * (1 - p_t)**gamma * log(p_t)\n        where p = sigmoid(x), p_t = p or 1 - p depending on if the label is 1 or 0, respectively.\n    References:\n        https://arxiv.org/pdf/1708.02002.pdf\n    Usage:\n     model.compile(loss=[binary_focal_loss(alpha=.25, gamma=2)], metrics=[\"accuracy\"], optimizer=adam)\n    \"\"\"\n    alpha = tf.constant(alpha, dtype=tf.float32)\n    gamma = tf.constant(gamma, dtype=tf.float32)\n\n    def binary_focal_loss_fixed(y_true, y_pred):\n        \"\"\"\n        y_true shape need be (None,1)\n        y_pred need be compute after sigmoid\n        \"\"\"\n        eps=1e-6\n        y_true = tf.cast(y_true, tf.float32)\n        alpha_t = y_true*alpha + (K.ones_like(y_true)-y_true)*(1-alpha)\n    \n        p_t = y_true*y_pred + (K.ones_like(y_true)-y_true)*(K.ones_like(y_true)-y_pred) + K.epsilon()\n        focal_loss = - alpha_t * K.pow((K.ones_like(y_true)-p_t),gamma) * K.log(p_t)\n        \n        smooth=1\n        y_true_pos = K.flatten(y_true)\n        y_pred_pos = K.flatten(y_pred)\n        true_pos = K.sum(y_true_pos * y_pred_pos)\n        false_neg = K.sum(y_true_pos * (1-y_pred_pos))\n        false_pos = K.sum((1-y_true_pos)*y_pred_pos)\n        alpha1 = 0.5\n        loss1=(true_pos + smooth)/(true_pos + alpha1*false_neg + (1-alpha1)*false_pos + smooth)\n        \n        loss2=(1.-loss1)+focal_loss\n        \n        return K.mean(loss2)\n    return binary_focal_loss_fixed","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"7282d18de3aff1cee12ff89b7d511a391702814f"},"cell_type":"code","source":"from 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#weight_path保存模型的路径，monitor：需要监视的值，verbose：信息展示模式，save_best_only：当设置为True时，监测值有改进时才会保存当前的模型\n#save_weights_only：若设置为True，则只保存模型权重，否则将保存整个模型（包括模型结构，配置信息等）\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\n#当指标停止提升时，降低学习速率。\n#monitor：要监测的数量。patience：没有提升的epoch数，之后学习率将降低。verbose：int。0：安静，1：更新消息。\n#mode：{auto，min，max}之一。在min模式下，当监测量停止下降时，lr将减少；在max模式下，当监测数量停止增加时，它将减少；\n#在auto模式下，从监测数量的名称自动推断方向。\n#min_delta：对于测量新的最优化的阀值，仅关注重大变化。\n#cooldown：在学习速率被降低之后，重新恢复正常操作之前等待的epoch数量。\n#min_lr：学习率的下限。\n\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#目的：防止过拟合\n#monitor: 需要监视的量，val_loss，val_acc\n#patience: 当early stop被激活(如发现loss相比上一个epoch训练没有下降)，则经过patience个epoch后停止训练\n#verbose: 信息展示模式\n#mode: 'auto','min','max'之一，在min模式训练，如果检测值停止下降则终止训练。在max模式下，当检测值不再上升的时候则停止训练。\n\ncallbacks_list = [checkpoint, early, reduceLROnPlat]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"MAX_TRAIN_EPOCHS=40\nBATCH_SIZE=50\nMAX_TRAIN_STEPS=300","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"5b67d808c0b8c7e28bff41e6d3858ff6f09dd626","scrolled":false},"cell_type":"code","source":"# def fit():\n# with strategy.scope():\n#     seg_model.compile(optimizer=Adam(1e-3, decay=1e-6), loss=tversky_loss, metrics=[IoU,dice_coef,'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(aug_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\n# while True:\n#     loss_history = fit()\n#     if np.min([mh.history['val_loss'] for mh in loss_history]) < 0.01:\n#         break","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# with strategy.scope():\nseg_model.compile(optimizer=Adam(1e-2, decay=1e-6), loss=binary_iou_focal_loss, metrics=[IoU,dice_coef,'binary_accuracy'])#tversky_loss\n\nstep_count = min(MAX_TRAIN_STEPS, train_df.shape[0]//BATCH_SIZE)\naug_gen = create_aug_gen(make_image_gen(train_df))\nloss_history = [seg_model.fit_generator(aug_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\n# while True:\n#     loss_history = fit()\n#     if np.min([mh.history['val_loss'] for mh in loss_history]) < 0.01:\n#         break","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"a168c8b1af446b800f6129104906003ededd61c4"},"cell_type":"code","source":"def show_loss(loss_history):\n    epochs = np.concatenate([mh.epoch for mh in loss_history])\n    fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(22, 10))\n    \n    _ = ax1.plot(epochs, np.concatenate([mh.history['loss'] for mh in loss_history]), 'b-',\n                 epochs, np.concatenate([mh.history['val_loss'] for mh in loss_history]), 'r-')\n    ax1.legend(['Training', 'Validation'])\n    ax1.set_title('Loss')\n    \n    _ = ax2.plot(epochs, np.concatenate([mh.history['IoU'] for mh in loss_history]), 'b-',\n                 epochs, np.concatenate([mh.history['val_IoU'] for mh in loss_history]), 'r-')\n    ax2.legend(['Training', 'Validation'])\n    ax2.set_title('IoU Accuracy (%)')\n\nshow_loss(loss_history)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"ce1167e9f09200f537e61f93f486168a13be1711"},"cell_type":"code","source":"seg_model.load_weights(weight_path)\nseg_model.save_weights('seg_model111.h5')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"275b411dc97a350aacaba46c8562efcf2658b1a7"},"cell_type":"code","source":"pred_y = seg_model.predict(valid_x)\nprint(pred_y.shape, pred_y.min(axis=0).max(), pred_y.max(axis=0).min(), pred_y.mean())","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"6a4fd2ca0cf47ba069a314356bf74c7b531c56ac"},"cell_type":"code","source":"fig, ax = plt.subplots(1, 1, figsize = (6, 6))\nax.hist(pred_y.ravel(), np.linspace(0, 1, 20))\nax.set_xlim(0, 1)\nax.set_yscale('log', nonposy='clip')","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"0018ab172d18936f8cc2c5df33d2f840dc16bf4f"},"cell_type":"markdown","source":"# Prepare Full Resolution Model\nHere we account for the scaling so everything can happen in the model itself\n#准备全分辨率模型\n#在这里，我们考虑了缩放，这样模型本身就可以发生任何事情"},{"metadata":{"_uuid":"17edb177402ae51651692511827a7e9d60646533"},"cell_type":"markdown","source":"# Visualize predictions\n#可视化预测"},{"metadata":{"trusted":true},"cell_type":"code","source":"def predict(img, path=test_image_dir):\n    c_img = imread(os.path.join(path, c_img_name))\n    c_img = c_img[::IMG_SCALING[0], ::IMG_SCALING[1]]\n    c_img = np.expand_dims(c_img, 0)/255.0\n    \n    cur_seg = seg_model.predict(c_img)[0]\n    cur_seg = binary_opening(cur_seg>0.99, np.expand_dims(disk(2), -1))\n    return cur_seg, c_img\n\ndef pred_encode(img):\n    cur_seg, _ = predict(img)\n    cur_rles = rle_encode(cur_seg)\n    return [img, cur_rles if len(cur_rles) > 0 else None]\n\n## Get a sample of each group of ship count\nsamples = train_df.groupby('ships').apply(lambda x: x.sample(2))#1\n\nfig, m_axs = plt.subplots(samples.shape[0], 3, figsize = (11, samples.shape[0]*4))\n[c_ax.axis('off') for c_ax in m_axs.flatten()]\n\nfor (ax1, ax2, ax3), c_img_name in zip(m_axs, samples.ImageId.values):\n    first_seg, first_img = predict(c_img_name, train_image_dir)\n    ax1.imshow(first_img[0])\n    ax1.set_title('Image')\n    ax2.imshow(first_seg[:, :, 0])\n    ax2.set_title('Prediction')\n    ground_truth = masks_as_color(masks.query('ImageId==\"{}\"'.format(c_img_name))['EncodedPixels'])\n    ax3.imshow(ground_truth)\n    ax3.set_title('Ground Truth')\n    \nfig.savefig('predictions.png')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","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":4}