{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Contrail segmentation with Tensorflow","metadata":{}},{"cell_type":"markdown","source":"## Notebook features\n\n- Working with preprocessed dataset. Source: https://www.kaggle.com/code/bencetar/contrail-dataset-preprocess    \n    (filtered samples 20k->9k training and 1.8k->540 valid samples)\n- Data augmentation applied\n- Mixed precision training\n- Weighted loss for class inbalance\n- 5 block UNet model ","metadata":{}},{"cell_type":"markdown","source":"## Libraries\n","metadata":{}},{"cell_type":"code","source":"import tensorflow as tf\ntf.config.experimental.list_physical_devices('GPU')","metadata":{"execution":{"iopub.status.busy":"2023-09-14T08:19:49.847954Z","iopub.execute_input":"2023-09-14T08:19:49.848472Z","iopub.status.idle":"2023-09-14T08:19:58.796573Z","shell.execute_reply.started":"2023-09-14T08:19:49.848436Z","shell.execute_reply":"2023-09-14T08:19:58.795572Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import os\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nimport tensorflow as tf\nimport gc\nimport albumentations as A\n\n# Enable GPU memory growth\ngpus = tf.config.experimental.list_physical_devices('GPU')\nif gpus:\n    for gpu in gpus:\n        tf.config.experimental.set_memory_growth(gpu, True)","metadata":{"execution":{"iopub.status.busy":"2023-09-14T08:19:58.798657Z","iopub.execute_input":"2023-09-14T08:19:58.799318Z","iopub.status.idle":"2023-09-14T08:20:00.170008Z","shell.execute_reply.started":"2023-09-14T08:19:58.799283Z","shell.execute_reply":"2023-09-14T08:20:00.168833Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Preprocessed dataset\n\nThe data used in this notebook is preprocessed in this notebook:\nhttps://www.kaggle.com/code/bencetar/contrail-dataset-preprocess\n\nThe created datasets can be found:\n- training images: https://www.kaggle.com/datasets/bencetar/contrail-train-imgs\n- validation images: https://www.kaggle.com/datasets/bencetar/contrail-valid-imgs\n- training masks: https://www.kaggle.com/datasets/bencetar/prep-train-masks\n- validation masks: https://www.kaggle.com/datasets/bencetar/prep-valid-masks","metadata":{}},{"cell_type":"markdown","source":"## Create custom dataset","metadata":{}},{"cell_type":"code","source":"class CFG:\n    \"\"\" Set the hyperparameters. \"\"\"\n    INPUT_IMG = (256, 256, 3)\n    INPUT_LBL = (256, 256, 1)\n    BATCH_SIZE = 8\n    BUFFER_SIZE = 2000\n    EPOCHS = 15\n    LR = 5e-4","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# data source\nTEST_IMGS = \"/kaggle/input/google-research-identify-contrails-reduce-global-warming/test\"\nPREP_TRAIN_IMGS = \"/kaggle/input/contrail-train-imgs/contrail_training\"\nPREP_VALID_IMGS = \"/kaggle/input/contrail-valid-imgs/contrail_valid\"\nPREP_TRAIN_MSKS = \"/kaggle/input/prep-train-masks\"\nPREP_VALID_MSKS = \"/kaggle/input/prep-valid-masks\"\n\n# img name format: rgb_image_ID.npz\n# msk name format: prep_mask_ID.npz\n\n# Check out the preprocessed dataset:\nprint(f\"Images in train: {len(os.listdir(PREP_TRAIN_IMGS))} in valid: {len(os.listdir(PREP_VALID_IMGS))}\")\nprint(f\"Masks in train: {len(os.listdir(PREP_TRAIN_MSKS))} in valid: {len(os.listdir(PREP_VALID_MSKS))}\")","metadata":{"execution":{"iopub.status.busy":"2023-09-14T08:20:00.184697Z","iopub.execute_input":"2023-09-14T08:20:00.185434Z","iopub.status.idle":"2023-09-14T08:20:00.959108Z","shell.execute_reply.started":"2023-09-14T08:20:00.185396Z","shell.execute_reply":"2023-09-14T08:20:00.957858Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Define constants\n_T11_BOUNDS = (243, 303)\n_CLOUD_TOP_TDIFF_BOUNDS = (-4, 5)\n_TDIFF_BOUNDS = (-4, 2)\n\n# Data augmentation\naugmentation = A.Compose([\n    A.OneOf([\n        A.HorizontalFlip(),\n        A.VerticalFlip(),\n        A.ShiftScaleRotate(scale_limit=0.2, rotate_limit=45),\n        A.RandomBrightnessContrast(brightness_limit=0.2, contrast_limit=0.2),\n    ], p=0.4)\n])\n\nclass ContrailDataset(tf.keras.utils.Sequence):\n    \"\"\" Custom dataset with generator to load images and masks. \"\"\"\n\n    def __init__(self, img_folder=None, lbl_folder=None, training=True, subset_num=None, folds=None):\n        self.img_folder = img_folder\n        self.lbl_folder = lbl_folder\n        self.training = training\n        self.subset_num = subset_num\n        self.folds = folds\n        \n        if self.training:\n            self.correct_ids = [i.split(\"_\")[-1][:-4] for i in os.listdir(self.img_folder)]\n            self.subset_size = int(len(self.correct_ids)/self.folds)\n            start = self.subset_size * (self.subset_num - 1)\n            end = start + self.subset_size\n            self.correct_ids = self.correct_ids[start:end]            \n        else:\n            self.correct_ids = os.listdir(self.img_folder)    \n        \n    def normalize_range(self, data, bounds):\n        return (data - bounds[0]) / (bounds[1] - bounds[0])\n    \n    def __len__(self):\n        return len(self.correct_ids)\n            \n    def __get_image(self, record_id):        \n        if self.training:            \n            with np.load(os.path.join(self.img_folder, f'rgb_image_{record_id}.npz')) as data:\n                composite = np.asarray(data['data'][..., 4], dtype=np.float32) # take only current timestep\n            return composite\n        else:\n            with open(os.path.join(self.img_folder, record_id, 'band_15.npy'), 'rb') as f:\n                band15= np.load(f).astype(np.float32)\n            with open(os.path.join(self.img_folder, record_id, 'band_14.npy'), 'rb') as f:\n                band14 = np.load(f).astype(np.float32)\n            with open(os.path.join(self.img_folder, record_id, 'band_11.npy'), 'rb') as f:\n                band11 = np.load(f).astype(np.float32)\n            r = self.normalize_range(band15 - band14, _TDIFF_BOUNDS)\n            g = self.normalize_range(band14 - band11, _CLOUD_TOP_TDIFF_BOUNDS)\n            b = self.normalize_range(band14, _T11_BOUNDS)\n            composite = np.clip(np.stack([r,g, b], axis=2), 0, 1)[..., 4]\n            return composite\n\n    def __get_label(self, record_id):\n        with np.load(os.path.join(self.lbl_folder, f'prep_mask_{record_id}.npz')) as data:\n            mask = np.expand_dims(data['data'], axis=-1).astype(np.float32)\n        return mask\n\n    def generator(self):\n        for rec_id in self.correct_ids:\n            if self.training:\n                image = self.__get_image(rec_id)\n                mask = self.__get_label(rec_id)                                               \n                augmented = augmentation(image=image, mask=mask)                \n                yield tf.convert_to_tensor(augmented[\"image\"], dtype=tf.float32), tf.convert_to_tensor(augmented[\"mask\"], dtype=tf.float32)\n            else:\n                image = self.__get_image(rec_id)\n                yield tf.convert_to_tensor(image, dtype=tf.float32)\n            gc.collect()","metadata":{"execution":{"iopub.status.busy":"2023-09-14T08:20:00.960815Z","iopub.execute_input":"2023-09-14T08:20:00.96117Z","iopub.status.idle":"2023-09-14T08:20:00.986928Z","shell.execute_reply.started":"2023-09-14T08:20:00.961137Z","shell.execute_reply":"2023-09-14T08:20:00.985806Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Define UNet model and losses","metadata":{}},{"cell_type":"code","source":"def dice_coef(y_true, y_pred):\n    intersection = tf.reduce_sum(y_true * y_pred)\n    score = (2.0 * intersection + 1.0) / (tf.reduce_sum(y_true) + tf.reduce_sum(y_pred) + 1.0)\n    return score\n    \ndef dice_loss(y_true, y_pred):\n    dice_loss = 1 - dice_coef(y_true, y_pred)\n    return dice_loss\n\ndef weighted_binary_crossentropy(y_true, y_pred):\n    # In order to weight up the targets\n    weights = tf.where(y_true == 1, 100.0, 1.0)  # 100 for positive class, 1 for negative class\n    loss = tf.keras.losses.BinaryCrossentropy(from_logits=False, reduction=\"auto\")(y_true, y_pred)\n    weighted_loss = loss * weights\n    return tf.reduce_mean(weighted_loss)","metadata":{"execution":{"iopub.status.busy":"2023-09-14T08:20:00.98922Z","iopub.execute_input":"2023-09-14T08:20:00.989804Z","iopub.status.idle":"2023-09-14T08:20:01.001778Z","shell.execute_reply.started":"2023-09-14T08:20:00.989678Z","shell.execute_reply":"2023-09-14T08:20:01.000461Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from tensorflow.keras import mixed_precision\nmixed_precision.set_global_policy(policy=\"mixed_float16\")\n\ndef conv_block(input_l, filters, encoder=True):    \n    conv_1 = tf.keras.layers.Conv2D(filters, 3, padding=\"same\")(input_l)\n    bn_1 = tf.keras.layers.BatchNormalization()(conv_1)\n    relu_1 = tf.keras.activations.relu(bn_1)\n    conv_2 = tf.keras.layers.Conv2D(filters, 3, padding=\"same\")(relu_1)\n    bn_2 = tf.keras.layers.BatchNormalization()(conv_2)\n    relu_2 = tf.keras.activations.relu(bn_2)\n    if encoder == True:\n        maxpool = tf.keras.layers.MaxPooling2D((2,2))(relu_2) \n        return maxpool\n    else:\n        upscale = tf.keras.layers.Conv2DTranspose(filters, 3, padding=\"same\", strides=(2,2))(relu_2)\n        return upscale\n\ndef unet_encoder(input_layer):\n    # Encoder part of Unet\n    x = conv_block(input_layer, 32)\n    x = conv_block(x, 64) \n    x = conv_block(x, 128) \n    x = conv_block(x, 256) \n    x = conv_block(x, 512) \n    x = conv_block(x, 1024)\n    return x\n    \ndef unet_decoder(input_layer):\n    # Decoder part of Unet\n    x = conv_block(input_layer, 1024, encoder=False) \n    x = conv_block(x, 512, encoder=False) \n    x = conv_block(x, 256, encoder=False) \n    x = conv_block(x, 128, encoder=False) \n    x = conv_block(x, 64, encoder=False) \n    x = conv_block(x, 32, encoder=False) \n    last_conv = tf.keras.layers.Conv2D(1, 1)(x)\n    sigmoid_f32 = tf.keras.layers.Activation(\"sigmoid\", dtype=tf.float32, name=\"sigmoid_f32\")(last_conv)\n    return sigmoid_f32\n\ndef UNET_model(input_shape, training=True):\n    input_layer = tf.keras.layers.Input(shape=input_shape)\n    encoder = unet_encoder(input_layer)\n    decoder = unet_decoder(input_layer=encoder)\n    model = tf.keras.Model(inputs=input_layer, outputs=decoder, name=\"unet_model\")\n    return model","metadata":{"execution":{"iopub.status.busy":"2023-09-14T08:20:01.005922Z","iopub.execute_input":"2023-09-14T08:20:01.006409Z","iopub.status.idle":"2023-09-14T08:20:01.026494Z","shell.execute_reply.started":"2023-09-14T08:20:01.006377Z","shell.execute_reply":"2023-09-14T08:20:01.025111Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Model\nmodel = UNET_model(input_shape=CFG.INPUT_IMG)\n\noptimizer = tf.keras.optimizers.Adam(learning_rate=CFG.LR)\n\nmodel.compile(\n    loss=weighted_binary_crossentropy,\n    optimizer=optimizer,\n    metrics=[dice_coef]\n)\n\n# Check mixed precision setup\nfor layer in model.layers:\n    print(layer.name, '--', layer.output_shape, '--', layer.dtype_policy, \"--\", layer.trainable)","metadata":{"execution":{"iopub.status.busy":"2023-09-14T08:20:01.028585Z","iopub.execute_input":"2023-09-14T08:20:01.029191Z","iopub.status.idle":"2023-09-14T08:20:05.366548Z","shell.execute_reply.started":"2023-09-14T08:20:01.029153Z","shell.execute_reply":"2023-09-14T08:20:05.365584Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model.summary()","metadata":{"execution":{"iopub.status.busy":"2023-09-14T08:20:05.367979Z","iopub.execute_input":"2023-09-14T08:20:05.36841Z","iopub.status.idle":"2023-09-14T08:20:05.524249Z","shell.execute_reply.started":"2023-09-14T08:20:05.368375Z","shell.execute_reply":"2023-09-14T08:20:05.523477Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Model training","metadata":{}},{"cell_type":"code","source":"tf.keras.backend.clear_session()\ngc.collect()\n\n# Create datasets (taking all samples)\ntrain_dataset = ContrailDataset(img_folder=PREP_TRAIN_IMGS, lbl_folder=PREP_TRAIN_MSKS, subset_num=1, folds=1)\nvalid_dataset = ContrailDataset(img_folder=PREP_VALID_IMGS, lbl_folder=PREP_VALID_MSKS, subset_num=1, folds=1)\n\n# Create dataloaders\ntrain_dataloader = tf.data.Dataset.from_generator(\n    generator=train_dataset.generator,\n    output_signature=(\n        tf.TensorSpec(shape=CFG.INPUT_IMG, dtype=tf.float32),\n        tf.TensorSpec(shape=CFG.INPUT_LBL, dtype=tf.float32)\n    )\n).prefetch(tf.data.AUTOTUNE).shuffle(buffer_size=CFG.BUFFER_SIZE).repeat().batch(CFG.BATCH_SIZE)\n\nvalid_dataloader = tf.data.Dataset.from_generator(\n    generator=valid_dataset.generator,\n    output_signature=(\n        tf.TensorSpec(shape=CFG.INPUT_IMG, dtype=tf.float32),\n        tf.TensorSpec(shape=CFG.INPUT_LBL, dtype=tf.float32)\n    )\n).prefetch(tf.data.AUTOTUNE).cache().batch(CFG.BATCH_SIZE)\n\n# Callbacks\nMC = tf.keras.callbacks.ModelCheckpoint(\"./best_model.h5\", monitor=\"val_loss\", save_best_only=True)\nES = tf.keras.callbacks.EarlyStopping(monitor=\"val_loss\", patience=3)\nLR = tf.keras.callbacks.ReduceLROnPlateau(monitor=\"val_loss\", factor=0.5, patience=1, verbose=1)\n\n# Fit model\nsteps = len(train_dataset) // CFG.EPOCHS\nhistory = model.fit(\n    train_dataloader,\n    validation_data=valid_dataloader,\n    epochs=CFG.EPOCHS,\n    steps_per_epoch=steps,\n    callbacks=[MC, ES, LR],\n)\n","metadata":{"execution":{"iopub.status.busy":"2023-09-14T08:20:05.528266Z","iopub.execute_input":"2023-09-14T08:20:05.528721Z","iopub.status.idle":"2023-09-14T14:37:37.215907Z","shell.execute_reply.started":"2023-09-14T08:20:05.528684Z","shell.execute_reply":"2023-09-14T14:37:37.212717Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Results","metadata":{}},{"cell_type":"code","source":"# Load in best model\nbest = \"/kaggle/working/best_model.h5\"\nloaded_model = tf.keras.models.load_model(best, custom_objects={'weighted_binary_crossentropy': weighted_binary_crossentropy, 'dice_coef':dice_coef})","metadata":{"execution":{"iopub.status.busy":"2023-09-14T14:37:37.220168Z","iopub.execute_input":"2023-09-14T14:37:37.220468Z","iopub.status.idle":"2023-09-14T14:37:38.669423Z","shell.execute_reply.started":"2023-09-14T14:37:37.220442Z","shell.execute_reply":"2023-09-14T14:37:38.66835Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def plot_history(history, figsize=(10,4)):\n    train_acc = history[\"dice_coef\"]\n    val_acc = history[\"val_dice_coef\"]\n    train_loss = history[\"loss\"]\n    val_loss = history[\"val_loss\"]\n    epochs = [i for i in range(0,len(val_loss))]\n    \n    fig, (ax1, ax2) = plt.subplots(1,2, figsize=figsize)\n    ax1.plot(epochs, train_loss, label=\"Training loss\")\n    ax1.plot(epochs, val_loss, label=\"Validation loss\")\n    ax1.set_title(\"Loss\")\n    ax1.legend()\n    ax2.plot(epochs, train_acc, label=\"Training dice score\")\n    ax2.plot(epochs, val_acc, label=\"Validation dice score\")\n    ax2.set_title(\"Dice score\")\n    ax2.legend()\n    \nplot_history(history.history)","metadata":{"execution":{"iopub.status.busy":"2023-09-14T14:37:38.671226Z","iopub.execute_input":"2023-09-14T14:37:38.671624Z","iopub.status.idle":"2023-09-14T14:37:39.410594Z","shell.execute_reply.started":"2023-09-14T14:37:38.671571Z","shell.execute_reply":"2023-09-14T14:37:39.409583Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Prediction on test images","metadata":{}},{"cell_type":"code","source":"test_dataset = ContrailDataset(\n    img_folder=TEST_IMGS,\n    training=False,\n)\ntest_dataloader = tf.data.Dataset.from_generator(\n    generator=test_dataset.generator,\n    output_types=tf.float32,\n    output_shapes=(256, 256, 3),\n).prefetch(tf.data.AUTOTUNE).batch(2)\n\ntest_preds = []\nfor tests in test_dataloader:\n    for img in tests:\n        sample = {}\n        sample[\"image\"] = img\n        pred = loaded_model.predict(tf.expand_dims(img, axis=0))[0,:,:,0]\n        print(pred.shape)\n        sample[\"pred\"] = pred\n        test_preds.append(sample)","metadata":{"execution":{"iopub.status.busy":"2023-09-14T14:37:39.412141Z","iopub.execute_input":"2023-09-14T14:37:39.412551Z","iopub.status.idle":"2023-09-14T14:37:42.676862Z","shell.execute_reply.started":"2023-09-14T14:37:39.412499Z","shell.execute_reply":"2023-09-14T14:37:42.675814Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Test prediction\nfig, axs = plt.subplots(2, 3, figsize=(14,10))\n\nfor i, samp in enumerate(test_preds):\n    trshld = 0.5\n    img, pred_prob = samp[\"image\"], samp[\"pred\"]\n    pred = (pred_prob > 0.5).astype(float)\n    print(pred.shape, img.shape, pred_prob.shape)\n    axs[i][0].imshow(img)\n    axs[i][0].set_title(f\"{i}. image\")\n    axs[i][0].axis(\"off\")\n    axs[i][1].imshow(pred_prob)\n    axs[i][1].set_title(f\"{i}. pred probs\")\n    axs[i][1].axis(\"off\")\n    axs[i][2].imshow(pred)\n    axs[i][2].set_title(f\"{i}. pred (th.:{trshld})\")\n    axs[i][2].axis(\"off\")","metadata":{"execution":{"iopub.status.busy":"2023-09-14T14:37:42.678414Z","iopub.execute_input":"2023-09-14T14:37:42.678886Z","iopub.status.idle":"2023-09-14T14:37:43.627435Z","shell.execute_reply.started":"2023-09-14T14:37:42.678843Z","shell.execute_reply":"2023-09-14T14:37:43.62658Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Prediction on validation set","metadata":{}},{"cell_type":"code","source":"valid_preds = []\nfor valid_batch in valid_dataloader.take(1):\n    for img, msk in zip(valid_batch[0], valid_batch[1]): # [0]images, [1]masks\n        sample = {}\n        sample[\"image\"] = img\n        sample[\"mask\"] = msk\n        pred = loaded_model.predict(tf.expand_dims(img, axis=0), verbose=0)[0,:,:,0]\n        sample[\"pred\"] = pred\n        valid_preds.append(sample)","metadata":{"execution":{"iopub.status.busy":"2023-09-14T14:37:43.635825Z","iopub.execute_input":"2023-09-14T14:37:43.637372Z","iopub.status.idle":"2023-09-14T14:37:44.114916Z","shell.execute_reply.started":"2023-09-14T14:37:43.637339Z","shell.execute_reply":"2023-09-14T14:37:44.113714Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Lets visualze some sample\nfig, axs = plt.subplots(4, 4, figsize=(15,15))\n\nfor i, samp in enumerate(valid_preds[4:8]):\n    trshld = 0.5\n    img, msk, pred_prob = samp[\"image\"], samp[\"mask\"], samp[\"pred\"]\n    pred = (pred_prob > trshld).astype(float)\n    axs[i][0].imshow(img)\n    axs[i][0].set_title(f\"{i}. image\")\n    axs[i][0].axis(\"off\")\n    axs[i][1].imshow(msk)\n    axs[i][1].set_title(f\"{i}. mask\")\n    axs[i][1].axis(\"off\")\n    axs[i][2].imshow(pred_prob)\n    axs[i][2].set_title(f\"{i}. pred probs\")\n    axs[i][2].axis(\"off\")\n    axs[i][3].imshow(pred)\n    axs[i][3].set_title(f\"{i}. pred (th.:{trshld})\")\n    axs[i][3].axis(\"off\")","metadata":{"execution":{"iopub.status.busy":"2023-09-14T14:37:44.116699Z","iopub.execute_input":"2023-09-14T14:37:44.117268Z","iopub.status.idle":"2023-09-14T14:37:45.98904Z","shell.execute_reply.started":"2023-09-14T14:37:44.117228Z","shell.execute_reply":"2023-09-14T14:37:45.987143Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Submission with RLE\nsubmission code source: https://www.kaggle.com/code/inversion/contrails-rle-submission","metadata":{}},{"cell_type":"code","source":"\ndef rle_encode(x, fg_val=1):\n    \"\"\"\n    Args:\n        x:  numpy array of shape (height, width), 1 - mask, 0 - background\n    Returns: run length encoding as list\n    \"\"\"\n\n    dots = np.where(\n        x.T.flatten() == fg_val)[0]  # .T sets Fortran order down-then-right\n    run_lengths = []\n    prev = -2\n    for b in dots:\n        if b > prev + 1:\n            run_lengths.extend((b + 1, 0))\n        run_lengths[-1] += 1\n        prev = b\n    return run_lengths\n\n\ndef list_to_string(x):\n    \"\"\"\n    Converts list to a string representation\n    Empty list returns '-'\n    \"\"\"\n    if x: # non-empty list\n        s = str(x).replace(\"[\", \"\").replace(\"]\", \"\").replace(\",\", \"\")\n    else:\n        s = '-'\n    return s\n\n\ndef rle_decode(mask_rle, shape=(256, 256)):\n    '''\n    mask_rle: run-length as string formatted (start length)\n              empty predictions need to be encoded with '-'\n    shape: (height, width) of array to return \n    Returns numpy array, 1 - mask, 0 - background\n    '''\n\n    img = np.zeros(shape[0]*shape[1], dtype=np.uint8)\n    if mask_rle != '-': \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        for lo, hi in zip(starts, ends):\n            img[lo:hi] = 1\n    return img.reshape(shape, order='F')  # Needed to align to RLE direction","metadata":{"execution":{"iopub.status.busy":"2023-09-14T14:37:45.99055Z","iopub.execute_input":"2023-09-14T14:37:45.991595Z","iopub.status.idle":"2023-09-14T14:37:46.004794Z","shell.execute_reply.started":"2023-09-14T14:37:45.991556Z","shell.execute_reply":"2023-09-14T14:37:46.003563Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Create naive submission\nWe'll use just band_08 and predict that the 1000 pixels with the highest numerical values are contrails.","metadata":{}},{"cell_type":"code","source":"test1 = \"/kaggle/input/google-research-identify-contrails-reduce-global-warming/test/1000834164244036115/band_08.npy\"\ntest1 = np.load(test1).sum(axis=2)\ntest2 = \"/kaggle/input/google-research-identify-contrails-reduce-global-warming/test/1002653297254493116/band_08.npy\"\ntest2 = np.load(test2).sum(axis=2)\ntest3 = (test_preds[0][\"pred\"] > 0.5).astype(float)\n\nn = 1000\n\n# https://stackoverflow.com/a/57105712\npreds = np.c_[np.unravel_index(np.argpartition(test3.ravel(),-n)[-n:],test3.shape)]\nmask = np.zeros((256, 256))\nmask[preds[:, 0], preds[:, 1]] = 1\n\nplt.imshow(mask, cmap='Greys')\nplt.title(\"Obviously Not Contrails\", fontsize='16')\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2023-09-14T14:37:46.006395Z","iopub.execute_input":"2023-09-14T14:37:46.006903Z","iopub.status.idle":"2023-09-14T14:37:46.438418Z","shell.execute_reply.started":"2023-09-14T14:37:46.006866Z","shell.execute_reply":"2023-09-14T14:37:46.437488Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Convert to RLE\nlist_to_string(rle_encode(mask))","metadata":{"execution":{"iopub.status.busy":"2023-09-14T14:37:46.439882Z","iopub.execute_input":"2023-09-14T14:37:46.440278Z","iopub.status.idle":"2023-09-14T14:37:46.447722Z","shell.execute_reply.started":"2023-09-14T14:37:46.440242Z","shell.execute_reply":"2023-09-14T14:37:46.44648Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Automate submission","metadata":{}},{"cell_type":"code","source":"sub_path = \"/kaggle/input/google-research-identify-contrails-reduce-global-warming/sample_submission.csv\"\nsubmission = pd.read_csv(sub_path, index_col='record_id')\nsubmission.head()","metadata":{"execution":{"iopub.status.busy":"2023-09-14T14:37:46.449091Z","iopub.execute_input":"2023-09-14T14:37:46.449443Z","iopub.status.idle":"2023-09-14T14:37:46.512453Z","shell.execute_reply.started":"2023-09-14T14:37:46.449416Z","shell.execute_reply":"2023-09-14T14:37:46.511444Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Problem: we dont have ids.. we need to return that too with the generator...0\n# But maybe test_preds[0] is 1000834164244036115\n# and test_preds[1] is 1002653297254493116\n\ntest_im_0 = (test_preds[0][\"pred\"] > 0.5).astype(float)\npreds_0 = np.c_[np.unravel_index(np.argpartition(test_im_0.ravel(),-n)[-n:],test_im_0.shape)]\nmask_0 = np.zeros((256, 266))\nmask_0[preds_0[:, 0], preds_0[:, 1]] = 1\nsubmission.loc[1000834164244036115, 'encoded_pixels'] = list_to_string(rle_encode(mask_0))\n\ntest_im_1 = (test_preds[1][\"pred\"] > 0.5).astype(float)\npreds_1 = np.c_[np.unravel_index(np.argpartition(test_im_1.ravel(),-n)[-n:],test_im_1.shape)]\nmask_1 = np.zeros((256, 266))\nmask_1[preds_1[:, 0], preds_1[:, 1]] = 1\nsubmission.loc[1000834164244036115, 'encoded_pixels'] = list_to_string(rle_encode(mask_1))\n\nsubmission.head()","metadata":{"execution":{"iopub.status.busy":"2023-09-14T14:37:46.513969Z","iopub.execute_input":"2023-09-14T14:37:46.514331Z","iopub.status.idle":"2023-09-14T14:37:46.535817Z","shell.execute_reply.started":"2023-09-14T14:37:46.514294Z","shell.execute_reply":"2023-09-14T14:37:46.534826Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"submission.to_csv('submission.csv')","metadata":{"execution":{"iopub.status.busy":"2023-09-14T14:37:46.537245Z","iopub.execute_input":"2023-09-14T14:37:46.538084Z","iopub.status.idle":"2023-09-14T14:37:46.547893Z","shell.execute_reply.started":"2023-09-14T14:37:46.538052Z","shell.execute_reply":"2023-09-14T14:37:46.546748Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Closing thoughts\n\nThe predictions and the dice score indicates that the model isn't absolutely clueless, although there should be further training needed, and maybe the class weighting ratios could be different (I guess 1/10 instead of 1/100 might be better) along with other parameters. Also more filters and more convolutions would be beneficial.","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"}}