{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":6927,"databundleVersionId":45059,"sourceType":"competition"}],"dockerImageVersionId":31193,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Installing dependencies","metadata":{}},{"cell_type":"code","source":"! pip install protobuf==4.21.0 --upgrade\n! pip install tensorrt==8.6.1\n! pip install tensorflow[and-cuda]==2.15.0 --upgrade","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-02T13:39:09.764132Z","iopub.execute_input":"2025-12-02T13:39:09.764375Z","iopub.status.idle":"2025-12-02T13:42:11.803758Z","shell.execute_reply.started":"2025-12-02T13:39:09.764352Z","shell.execute_reply":"2025-12-02T13:42:11.803022Z"},"collapsed":true,"jupyter":{"outputs_hidden":true},"_kg_hide-output":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport shutil\nimport zipfile\n\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nfrom PIL import Image\n\nfrom sklearn.model_selection import train_test_split\n\nimport tensorflow as tf\nfrom tensorflow import keras\nfrom tensorflow.keras import backend as K\nfrom keras.utils import load_img, img_to_array\nimport tensorflow.keras.layers as tfl\nfrom tensorflow.keras.layers.experimental.preprocessing import RandomFlip, RandomRotation, RandomContrast\nfrom tensorflow.keras.layers import Input\nfrom tensorflow.keras.layers import Conv2D\nfrom tensorflow.keras.layers import MaxPooling2D\nfrom tensorflow.keras.layers import Dropout \nfrom tensorflow.keras.layers import Conv2DTranspose\nfrom tensorflow.keras.layers import concatenate\nprint(\"tensorflow\" + tf.__version__)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-02T13:42:25.170228Z","iopub.execute_input":"2025-12-02T13:42:25.170892Z","iopub.status.idle":"2025-12-02T13:42:25.176644Z","shell.execute_reply.started":"2025-12-02T13:42:25.170870Z","shell.execute_reply":"2025-12-02T13:42:25.175896Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(tf.config.list_physical_devices('GPU'))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-02T13:42:27.125691Z","iopub.execute_input":"2025-12-02T13:42:27.126306Z","iopub.status.idle":"2025-12-02T13:42:27.272000Z","shell.execute_reply.started":"2025-12-02T13:42:27.126280Z","shell.execute_reply":"2025-12-02T13:42:27.271198Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"tf.autograph.set_verbosity(0)\nstg = tf.distribute.MirroredStrategy() # For U-net model","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-02T13:42:33.811383Z","iopub.execute_input":"2025-12-02T13:42:33.812142Z","iopub.status.idle":"2025-12-02T13:42:34.072491Z","shell.execute_reply.started":"2025-12-02T13:42:33.812113Z","shell.execute_reply":"2025-12-02T13:42:34.071904Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Preliminary train/valid sample processing functions","metadata":{}},{"cell_type":"code","source":"###########################################################################################\n# ----------------------------------------------------------------------------------------\n# Get input zip files\n# ----------------------------------------------------------------------------------------\n##########################################################################################\ndef getZippedFilePaths():\n    zip_file_names = []\n    for dirname, _, filenames in os.walk('/kaggle/input'):\n        for filename in filenames:\n            if filename.split('.')[-1] == 'zip':\n                zip_file_names.append((os.path.join(dirname, filename)))\n    return zip_file_names\n\n###########################################################################################\n# ----------------------------------------------------------------------------------------\n# Preprocess images and mask\n# ----------------------------------------------------------------------------------------\n##########################################################################################\n# file_path = train_hq.zip\ndef preprocess_image(file_path):\n    # Load and decode the image\n    img = tf.io.read_file(file_path)\n    # You can adjust channels based on your images (3 for RGB)\n    img = tf.image.decode_jpeg(img, channels = 3) # Returned as uint8\n    # Normalize the pixel values to [0, 1]\n    img = tf.image.convert_image_dtype(img, tf.float32)\n    # Resize the image to your desired dimensions\n    img = tf.image.resize(img, [96, 128], method = 'nearest')\n    return img\n\n# file_path = train_masks.zip\ndef preprocess_target(file_path):\n    # Load and decode the image\n    mask = tf.io.read_file(file_path)\n    # Normalizing to between 0 and 1 (only two classes)\n    mask = tf.image.decode_image(mask, expand_animations = False, dtype = tf.float32)\n    # Get only one value for the 3rd channel\n    mask = tf.math.reduce_max(mask, axis = -1, keepdims = True)\n    # Resize the image to your desired dimensions\n    mask = tf.image.resize(mask, [96, 128], method = 'nearest')\n    return mask\n\n###########################################################################################\n# ----------------------------------------------------------------------------------------\n# Loss function\n# ----------------------------------------------------------------------------------------\n##########################################################################################\ndef dice_coef(y_true, y_pred, smooth=10e-6):\n    y_true_f = K.flatten(y_true)\n    y_pred_f = K.flatten(y_pred)\n    intersection = K.sum(y_true_f * y_pred_f)\n    dice = (2. * intersection + smooth) / (K.sum(y_true_f) + K.sum(y_pred_f) + smooth)\n    return dice\n\ndef dice_loss(y_true, y_pred):\n    return 1 - dice_coef(y_true, y_pred)\n###########################################################################################\n# ----------------------------------------------------------------------------------------\n# Display pred results\n# ----------------------------------------------------------------------------------------\n##########################################################################################\ndef display(display_list):\n    plt.figure(figsize=(15, 15))\n    title = ['Input Image', 'True Mask', 'Predicted Mask']\n    for i in range(len(display_list)):\n        plt.subplot(1, len(display_list), i+1)\n        plt.title(title[i])\n        plt.imshow(tf.keras.preprocessing.image.array_to_img(display_list[i]))\n        plt.axis('off')\n    plt.show()\n\n# Converting probabilities from *.predict(dataset) to the class index\ndef create_mask(pred_mask):\n    mask = pred_mask[..., -1] >= 0.5\n    pred_mask[..., -1] = tf.where(mask, 1, 0)\n    # Return only first mask of batch\n    return pred_mask[0]\n\n# Predict images visualization\ndef show_predictions(model, dataset = None, num = 1):\n    # Displays the first image of each of the num batches\n    if dataset:\n        for image, mask in dataset.take(num):\n            pred_mask = model.predict(image)\n            display([image[0], mask[0], create_mask(pred_mask)])\n    else:\n        display([sample_image, sample_mask,\n             create_mask(model.predict(sample_image[tf.newaxis, ...]))])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-02T13:42:36.658362Z","iopub.execute_input":"2025-12-02T13:42:36.658708Z","iopub.status.idle":"2025-12-02T13:42:36.669689Z","shell.execute_reply.started":"2025-12-02T13:42:36.658677Z","shell.execute_reply":"2025-12-02T13:42:36.669066Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Getting paths to *.zip files (move the files to the working directory)","metadata":{}},{"cell_type":"code","source":"zip_file_names = getZippedFilePaths()\n\nitems_to_remove = ['/kaggle/input/carvana-image-masking-challenge/train.zip', \n                   '/kaggle/input/carvana-image-masking-challenge/test.zip']\n     \nzip_file_names = [item for item in zip_file_names if item not in items_to_remove]\n\nfor zip_file_path in zip_file_names:\n    with zipfile.ZipFile(zip_file_path, 'r') as zip_ref:\n        zip_ref.extractall()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-02T13:42:39.322148Z","iopub.execute_input":"2025-12-02T13:42:39.322424Z","iopub.status.idle":"2025-12-02T13:47:17.817217Z","shell.execute_reply.started":"2025-12-02T13:42:39.322402Z","shell.execute_reply":"2025-12-02T13:47:17.816296Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"zip_file_names","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-02T13:47:17.818512Z","iopub.execute_input":"2025-12-02T13:47:17.818793Z","iopub.status.idle":"2025-12-02T13:47:17.824378Z","shell.execute_reply.started":"2025-12-02T13:47:17.818767Z","shell.execute_reply":"2025-12-02T13:47:17.823791Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Creating a tf.Dataset object and analyzing the source data","metadata":{}},{"cell_type":"code","source":"def analyze_mask_values(mask_files, sample_size = 5, title = \"Mask Analysis\"):\n    # Analyzes the values in masks before processing.\n        # mask_files: list of mask file paths\n        # sample_size: number of masks to analyze\n        # title: output header\n\n    print(f\"\\n{'='*60}\")\n    print(f\"{title} (analysis {min(sample_size, len(mask_files))} mask)\")\n    print('='*60)\n    \n    for i, mask_path in enumerate(mask_files[:sample_size]):\n        try:\n            # Reading and decoding the source file\n            mask_bytes = tf.io.read_file(mask_path)\n            mask = tf.image.decode_image(mask_bytes, expand_animations=False, dtype=tf.uint8)\n            \n            print(f\"\\Mask {i+1}: {os.path.basename(mask_path)}\")\n            print(f\"  Form: {mask.shape}\")\n            print(f\"  Data type: {mask.dtype}\")\n            \n            # Analyzing unique values\n            if len(mask.shape) == 3:\n                # If the image is multi-channel\n                if mask.shape[2] == 1:\n                    values = mask.numpy().flatten()\n                else:\n                    # For RGB, we look at individual channels\n                    values_r = mask.numpy()[..., 0].flatten()\n                    values_g = mask.numpy()[..., 1].flatten()\n                    values_b = mask.numpy()[..., 2].flatten()\n                    \n                    print(f\"  Unique values in the R channel: {np.unique(values_r)}\")\n                    print(f\"  Unique values in the G channel: {np.unique(values_g)}\")\n                    print(f\"  Unique values in the B channel: {np.unique(values_b)}\")\n                    \n                    # We also look at unique RGB combinations.\n                    reshaped = mask.numpy().reshape(-1, mask.shape[2])\n                    unique_combinations = np.unique(reshaped, axis=0)\n                    print(f\"  Unique RGB combinations ({len(unique_combinations)}):\")\n                    for combo in unique_combinations[:10]:  # Showing the first 10\n                        print(f\"    {combo}\")\n                    if len(unique_combinations) > 10:\n                        print(f\"    ... and more {len(unique_combinations) - 10}\")\n                    values = np.concatenate([values_r, values_g, values_b])\n            else:\n                values = mask.numpy().flatten()\n            \n            unique_values = np.unique(values)\n            print(f\"  All unique values ({len(unique_values)}): {unique_values}\")\n            print(f\"  Range of values: [{values.min()}, {values.max()}]\")\n            \n            # Histogram of values\n            plt.figure(figsize=(10, 3))\n            plt.subplot(1, 2, 1)\n            plt.hist(values, bins = 50, alpha = 0.7, color = 'blue', edgecolor = 'black')\n            plt.title(f'Distribution of values (mask {i+1})')\n            plt.xlabel('Pixel Value')\n            plt.ylabel('Frequency')\n            \n            # Mask Visualization\n            plt.subplot(1, 2, 2)\n            if len(mask.shape) == 3 and mask.shape[2] == 3:\n                plt.imshow(mask.numpy())\n            else:\n                plt.imshow(mask.numpy(), cmap='gray', vmin=0, vmax=255)\n            plt.title(f'Mask Visualization {i+1}')\n            plt.axis('off')\n            \n            plt.tight_layout()\n            plt.show()\n            \n        except Exception as e:\n            print(f\"Error during analysis {mask_path}: {e}\")\n\ndef analyze_processed_masks(dataset, sample_size = 5, title = \"Analysis of processed masks\"):\n    # Analyzes the values in masks after processing.\n        # dataset: tf.data.Dataset with processed masks\n        # sample_size: number of masks to analyze\n        # title: output header\n    \n    print(f\"\\n{'='*60}\")\n    print(f\"{title}\")\n    print('='*60)\n    \n    # We take the first sample_size elements from the dataset\n    iterator = iter(dataset)\n    for i in range(min(sample_size, 5)):\n        try:\n            mask = next(iterator)\n            \n            print(f\"\\nProcessed mask {i+1}:\")\n            print(f\"  Form: {mask.shape}\")\n            print(f\"  Data type: {mask.dtype}\")\n            \n            # Analysis of unique values\n            values = mask.numpy().flatten()\n            unique_values = np.unique(values)\n            \n            print(f\"  Unique values ({len(unique_values)}): {unique_values}\")\n            print(f\"  Range of values: [{values.min()}, {values.max()}]\")\n            \n            # Counting the number of each value\n            value_counts = {}\n            for val in values:\n                value_counts[val] = value_counts.get(val, 0) + 1\n            \n            print(\"  Distributing:\")\n            for val in sorted(unique_values):\n                count = value_counts.get(val, 0)\n                percentage = (count / len(values)) * 100\n                print(f\"    {val}: {count} pixel values ({percentage:.2f}%)\")\n            \n            # Visualization\n            plt.figure(figsize=(10, 3))\n            \n            # The histogram\n            plt.subplot(1, 2, 1)\n            bins = len(unique_values) if len(unique_values) <= 50 else 50\n            plt.hist(values, bins = bins, alpha=0.7, color='green', edgecolor='black')\n            plt.title(f'Distribution of values (processed {i+1})')\n            plt.xlabel('Pixel Value')\n            plt.ylabel('Frequency')\n            \n            # Image of the mask\n            plt.subplot(1, 2, 2)\n            if len(mask.shape) == 3 and mask.shape[2] > 1:\n                # If multi-channel\n                plt.imshow(mask.numpy())\n            else:\n                # If it is single-channel\n                plt.imshow(mask.numpy(), cmap='viridis')\n                plt.colorbar()\n            plt.title(f'Visualization of the processed mask {i+1}')\n            plt.axis('off')\n            \n            plt.tight_layout()\n            plt.show()\n            \n        except StopIteration:\n            break\n        except Exception as e:\n            print(f\"Error when analyzing the processed mask {i+1}: {e}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-02T13:47:17.825055Z","iopub.execute_input":"2025-12-02T13:47:17.825242Z","iopub.status.idle":"2025-12-02T13:47:17.843848Z","shell.execute_reply.started":"2025-12-02T13:47:17.825218Z","shell.execute_reply":"2025-12-02T13:47:17.843076Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Appending all path names to a sorted list\ntrain_hq_dir    = '/kaggle/working/train_hq/'\ntrain_masks_dir = '/kaggle/working/train_masks/'\ntest_hq_dir     = '/kaggle/working/test_hq/'\n\nX_train_id = sorted([os.path.join(train_hq_dir, filename)    for filename in os.listdir(train_hq_dir)],    key = lambda x: x.split('/')[-1].split('.')[0])\ny_train    = sorted([os.path.join(train_masks_dir, filename) for filename in os.listdir(train_masks_dir)], key = lambda x: x.split('/')[-1].split('.')[0])\nX_test_id  = sorted([os.path.join(test_hq_dir, filename)     for filename in os.listdir(test_hq_dir)],     key = lambda x: x.split('/')[-1].split('.')[0])\n\nX_train_id = X_train_id[:1000]\ny_train    = y_train[:1000]\n\n#----------------------------------------------------------\n# 1.) ANALYSIS BEFORE PROCESSING\n#----------------------------------------------------------\nprint(\"=\"*80)\nprint(\"ANALYSIS OF THE INITIAL MASKS BEFORE PROCESSING\")\nprint(\"=\"*80)\nanalyze_mask_values(y_train, sample_size = 5)\n#----------------------------------------------------------\n#----------------------------------------------------------\n\nX_train, X_val, y_train, y_val = train_test_split(X_train_id, y_train, test_size = 0.2, random_state = 42)\n\n# Create Dataset objects from the list of file paths\nX_train_ds = tf.data.Dataset.from_tensor_slices(X_train)\ny_train_ds = tf.data.Dataset.from_tensor_slices(y_train)\n\nX_val_ds = tf.data.Dataset.from_tensor_slices(X_val)\ny_val_ds = tf.data.Dataset.from_tensor_slices(y_val)\n\nX_test_ds = tf.data.Dataset.from_tensor_slices(X_test_id)\n\nimg_height = 96\nimg_width = 128\nnum_channels = 3\nimg_size = (img_height, img_width)\n\n# Apply preprocessing\nX_train_processed = X_train_ds.map(preprocess_image)\ny_train_processed = y_train_ds.map(preprocess_target)\n\nX_val_processed = X_val_ds.map(preprocess_image)\ny_val_processed  = y_val_ds.map(preprocess_target)\n\nX_test_processed = X_test_ds.map(preprocess_image)\n\n#----------------------------------------------------------\n# 2.) POST-TREATMENT ANALYSIS\n#----------------------------------------------------------\nprint(\"\\n\\n\" + \"=\"*80)\nprint(\"POST-TREATMENT MASK ANALYSIS\")\nprint(\"=\"*80)\nanalyze_processed_masks(y_train_processed, sample_size = 5, title = \"Analysis of processed masks\")\n#----------------------------------------------------------\n#----------------------------------------------------------\n\n#----------------------------------------------------------\n# 3.) Analysis of all processed masks in the dataset\n#----------------------------------------------------------\nprint(\"\\n\\n\" + \"=\"*80)\nprint(\"ANALYSIS OF ALL PROCESSED MASKS IN THE DATASET\")\nprint(\"=\"*80)\n\n# We collect statistics on all processed masks\nall_values = []\nfor mask in y_train_processed.take(100):  # We take 100 for analysis\n    all_values.extend(mask.numpy().flatten())\n\nall_values = np.array(all_values)\nprint(f\"\\nStatistics on 100 processed masks:\")\nprint(f\"  Total pixels: {len(all_values):,}\")\nprint(f\"  Unique values: {np.unique(all_values)}\")\nprint(f\"  Range: [{all_values.min()}, {all_values.max()}]\")\nprint(f\"  The average value: {all_values.mean():.6f}\")\nprint(f\"  Standard deviation: {all_values.std():.6f}\")\n\n# Distribution of values\nvalue_counts = {}\nfor val in all_values:\n    val_key = f\"{val:.6f}\"  # To avoid rounding errors with float\n    value_counts[val_key] = value_counts.get(val_key, 0) + 1\n\nprint(f\"\\nDistribution:\")\nfor val_str in sorted(value_counts.keys(), key=lambda x: float(x)):\n    count = value_counts[val_str]\n    percentage = (count / len(all_values)) * 100\n    print(f\"  {val_str}: {count:,} of pixel values ({percentage:.2f}%)\")\n\n# Visualization of the overall distribution\nplt.figure(figsize=(10, 5))\nplt.hist(all_values, bins=100, alpha=0.7, color='purple', edgecolor='black')\nplt.title('The overall distribution of pixel values in the processed masks')\nplt.xlabel('Pixel Value')\nplt.ylabel('Frequency (logarithmic)')\nplt.yscale('log')\nplt.grid(True, alpha=0.3)\nplt.show()\n#----------------------------------------------------------\n#----------------------------------------------------------\n\n# Adding labels to datasets\ntrain_dataset = tf.data.Dataset.zip((X_train_processed, y_train_processed))\nval_dataset = tf.data.Dataset.zip((X_val_processed, y_val_processed))\n\nprint(\"\\n\\n\" + \"=\"*80)\nprint(\"DATASETS ARE READY\")\nprint(\"=\"*80)\nprint(f\"The size of the training dataset: {len(X_train)}\")\nprint(f\"The size of the validation dataset: {len(X_val)}\")\nprint(f\"The size of the test dataset: {len(X_test_id)}\")\n\nBATCH_SIZE = 32\nbatched_train_dataset = train_dataset.batch(BATCH_SIZE)\nbatched_val_dataset   = val_dataset.batch(BATCH_SIZE)\nbatched_test_dataset  = X_test_processed.batch(BATCH_SIZE)\n\n# Adding autotune for pre-fetching\nAUTOTUNE = tf.data.experimental.AUTOTUNE\nbatched_train_dataset = batched_train_dataset.prefetch(buffer_size = AUTOTUNE)\nbatched_val_dataset   = batched_val_dataset.prefetch(buffer_size = AUTOTUNE)\nbatched_test_dataset  = batched_test_dataset.prefetch(buffer_size = AUTOTUNE)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-02T13:47:17.845352Z","iopub.execute_input":"2025-12-02T13:47:17.845552Z","iopub.status.idle":"2025-12-02T13:47:47.406302Z","shell.execute_reply.started":"2025-12-02T13:47:17.845537Z","shell.execute_reply":"2025-12-02T13:47:47.405719Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Control check the count of files:\nprint(\"The size of the training/validation dataset\", len(os.listdir(\"/kaggle/working/train_hq\")))\nprint(\"The size of the testing dataset\", len(os.listdir(\"/kaggle/working/test_hq\")))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-02T14:14:35.650668Z","iopub.execute_input":"2025-12-02T14:14:35.650994Z","iopub.status.idle":"2025-12-02T14:14:35.709993Z","shell.execute_reply.started":"2025-12-02T14:14:35.650975Z","shell.execute_reply":"2025-12-02T14:14:35.709248Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# View images and associated labels","metadata":{}},{"cell_type":"code","source":"plt.figure(figsize=(40, 30))\n\nfor images, masks in batched_val_dataset.take(1):\n    car_number = 0\n    for image_slot in range(16):\n        ax = plt.subplot(4, 4, image_slot + 1)\n        \n        if image_slot % 2 == 0:\n            plt.imshow((images[car_number]))\n            plt.title(f'Image {car_number + 1}', fontsize=22, pad=20)\n        else:\n            # Mask\n            mask_display = masks[car_number]\n            if len(mask_display.shape) == 3 and mask_display.shape[2] == 1:\n                mask_display = mask_display[:, :, 0]\n            \n            plt.imshow(mask_display, cmap='gray')\n            plt.title(f'Mask {car_number + 1}', fontsize=22, pad=20)\n            car_number += 1\n        \n        plt.axis('off')\n\nplt.suptitle('Dataset for binary segmentation', fontsize=28, y=1.02)\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-02T13:47:47.406996Z","iopub.execute_input":"2025-12-02T13:47:47.407281Z","iopub.status.idle":"2025-12-02T13:47:50.559172Z","shell.execute_reply.started":"2025-12-02T13:47:47.407255Z","shell.execute_reply":"2025-12-02T13:47:50.558377Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Data augmentation","metadata":{}},{"cell_type":"code","source":"data_augmentation = tf.keras.Sequential([tfl.RandomFlip(mode=\"horizontal\", seed=42),\n                                         tfl.RandomRotation(factor=0.01, seed=42),\n                                         tfl.RandomContrast(factor=0.2, seed=42)\n                                        ])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-02T13:47:50.560145Z","iopub.execute_input":"2025-12-02T13:47:50.560422Z","iopub.status.idle":"2025-12-02T13:47:50.586943Z","shell.execute_reply.started":"2025-12-02T13:47:50.560399Z","shell.execute_reply":"2025-12-02T13:47:50.586443Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Train easy custom model","metadata":{}},{"cell_type":"code","source":"def get_model(img_size):\n    inputs = Input(shape=img_size + (3,))\n    x = data_augmentation(inputs)\n    \n    # Contracting path\n    #-------------------------------------------------------------------------------------------\n    x = tfl.Conv2D(64, 3, strides = 2, activation = \"relu\", \n                                       padding = \"same\", kernel_initializer = 'he_normal')(x)\n    #-------------------------------------------------------------------------------------------\n    x = tfl.Conv2D(64, 3,              activation = \"relu\", \n                                       padding=\"same\", kernel_initializer='he_normal')(x)\n    #-------------------------------------------------------------------------------------------\n    x = tfl.Conv2D(128,3, strides = 2, activation = \"relu\", \n                                       padding=\"same\", kernel_initializer='he_normal')(x)\n    #-------------------------------------------------------------------------------------------\n    x = tfl.Conv2D(128,3,              activation = \"relu\", \n                                       padding=\"same\", kernel_initializer='he_normal')(x)\n    #-------------------------------------------------------------------------------------------\n    x = tfl.Conv2D(256,3, strides = 2, activation = \"relu\", \n                                       padding=\"same\", kernel_initializer='he_normal')(x)\n    #-------------------------------------------------------------------------------------------\n    x = tfl.Conv2D(256,3,              activation = \"relu\", \n                                       padding=\"same\", kernel_initializer='he_normal')(x)\n    #-------------------------------------------------------------------------------------------\n    \n    # Expanding path\n    #-------------------------------------------------------------------------------------------\n    x = tfl.Conv2DTranspose(256, 3, activation=\"relu\", padding=\"same\", kernel_initializer='he_normal')(x)\n    x = tfl.Conv2DTranspose(256, 3, activation=\"relu\", padding=\"same\", kernel_initializer='he_normal', strides = 2)(x)\n    x = tfl.Conv2DTranspose(128, 3, activation=\"relu\", padding=\"same\", kernel_initializer='he_normal')(x)\n    x = tfl.Conv2DTranspose(128, 3, activation=\"relu\", padding=\"same\", kernel_initializer='he_normal', strides = 2)(x)\n    x = tfl.Conv2DTranspose(64,  3, activation=\"relu\", padding=\"same\", kernel_initializer='he_normal')(x)\n    x = tfl.Conv2DTranspose(64,  3, activation=\"relu\", padding=\"same\", kernel_initializer='he_normal', strides = 2)(x)\n    outputs = tfl.Conv2D(1, 3, activation = \"sigmoid\", padding = \"same\")(x)\n    model = keras.Model(inputs, outputs) \n    return model","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-02T14:14:30.138232Z","iopub.execute_input":"2025-12-02T14:14:30.138532Z","iopub.status.idle":"2025-12-02T14:14:30.149556Z","shell.execute_reply.started":"2025-12-02T14:14:30.138510Z","shell.execute_reply":"2025-12-02T14:14:30.148907Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"custom_model = get_model(img_size=img_size) \ncustom_model.summary()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-02T14:15:11.556606Z","iopub.execute_input":"2025-12-02T14:15:11.557195Z","iopub.status.idle":"2025-12-02T14:15:11.859623Z","shell.execute_reply.started":"2025-12-02T14:15:11.557168Z","shell.execute_reply":"2025-12-02T14:15:11.859019Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"with stg.scope():\n    custom_model = get_model(img_size=img_size) \n    custom_model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate = 0.0001,\n                                                            epsilon       = 1e-06), \n                                                            loss          = [dice_loss], \n                                                            metrics       = [dice_coef])\n\n    callbacks_list = [keras.callbacks.EarlyStopping(monitor=\"val_loss\",\n                                                    patience=5,\n                                                   ),\n                      keras.callbacks.ModelCheckpoint(filepath=\"best-custom-model\",\n                                                      monitor=\"val_loss\",\n                                                      save_best_only=True,\n                                                     )\n                     ]\n\nhistory = custom_model.fit(batched_train_dataset,\n                           validation_data = batched_val_dataset,\n                           epochs          = 50,\n                           callbacks       = callbacks_list,\n                          )","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-02T14:16:00.570908Z","iopub.execute_input":"2025-12-02T14:16:00.571429Z","iopub.status.idle":"2025-12-02T14:31:13.426479Z","shell.execute_reply.started":"2025-12-02T14:16:00.571405Z","shell.execute_reply":"2025-12-02T14:31:13.425856Z"},"_kg_hide-output":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"custom_model = keras.models.load_model(\"/kaggle/working/best-custom-model\", \n                                       custom_objects = {'dice_coef': dice_coef, 'dice_loss': dice_loss})\n\nshow_predictions(model = custom_model, dataset = batched_train_dataset, num = 6)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-02T14:31:50.917137Z","iopub.execute_input":"2025-12-02T14:31:50.917431Z","iopub.status.idle":"2025-12-02T14:32:00.085048Z","shell.execute_reply.started":"2025-12-02T14:31:50.917411Z","shell.execute_reply":"2025-12-02T14:32:00.084372Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"*We get such a result due to the rather weak architecture of the model. Let's try to train the U-net model on our data.*","metadata":{}},{"cell_type":"markdown","source":"# U-net binary segmentation","metadata":{}},{"cell_type":"code","source":"def contracting_block(inputs = None, n_filters = 64, dropout_prob = 0, max_pooling = True):\n    conv = Conv2D(n_filters, 3,activation = 'relu',padding = 'same',kernel_initializer = 'he_normal')(inputs)\n    conv = Conv2D(n_filters, 3,activation = 'relu',padding = 'same',kernel_initializer = 'he_normal')(conv)\n\n    if dropout_prob > 0:\n        conv = Dropout(dropout_prob)(conv)\n\n    if max_pooling:\n        next_layer = MaxPooling2D(pool_size=(2, 2))(conv)\n    else:\n        next_layer = conv\n\n    skip_connection = conv\n\n    return next_layer, skip_connection\n\ndef expanding_block(expansive_input, contractive_input, n_filters = 64):\n    up = Conv2DTranspose(n_filters, 3, strides=(2, 2), padding='same', kernel_initializer='he_normal')(expansive_input)\n    # Merge the previous output and the contractive_input\n    merge = concatenate([up, contractive_input], axis = 3)\n    conv = Conv2D(n_filters, 3, activation='relu', padding='same', kernel_initializer='he_normal')(merge)\n    conv = Conv2D(n_filters, 3, activation='relu', padding='same', kernel_initializer='he_normal')(conv)\n\n    return conv\n\ndef Unet_model(input_size=(96, 128, 3), n_filters=64, n_classes=1):\n    inputs = Input(input_size)\n    inputs = data_augmentation(inputs)\n\n    # Contracting Path (encoding)\n    cblock1 = contracting_block(inputs,     n_filters)\n    cblock2 = contracting_block(cblock1[0], n_filters*2)\n    cblock3 = contracting_block(cblock2[0], n_filters*4)\n    cblock4 = contracting_block(cblock3[0], n_filters*8, dropout_prob = 0.3)\n\n    # Bottleneck Layer\n    cblock5 = contracting_block(cblock4[0], n_filters*16, dropout_prob = 0.3, max_pooling = False)\n    \n    # Expanding Path (decoding)\n    ublock6 = expanding_block(cblock5[0], cblock4[1],  n_filters*8)\n    ublock7 = expanding_block(ublock6,    cblock3[1],  n_filters*4)\n    ublock8 = expanding_block(ublock7,    cblock2[1],  n_filters*2)\n    ublock9 = expanding_block(ublock8,    cblock1[1],  n_filters)\n\n    conv9  = Conv2D(n_filters, 3, activation = 'relu',    padding = 'same', kernel_initializer='he_normal')(ublock9)\n    conv10 = Conv2D(n_classes, 1, activation = \"sigmoid\", padding = 'same')(conv9)\n\n    model = tf.keras.Model(inputs = inputs, outputs = conv10)\n\n    return model","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-02T13:48:22.615724Z","iopub.execute_input":"2025-12-02T13:48:22.616352Z","iopub.status.idle":"2025-12-02T13:48:22.626072Z","shell.execute_reply.started":"2025-12-02T13:48:22.616324Z","shell.execute_reply":"2025-12-02T13:48:22.625165Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"with stg.scope():\n    # Compile the model with the same loss and metric, that we used in custom model\n    unet = Unet_model(input_size = (img_height, img_width, num_channels), \n                      n_filters = 64, \n                      n_classes = 1\n                     )\n    unet.compile(optimizer=tf.keras.optimizers.Adam(learning_rate = 0.0001, \n                                                    epsilon = 1e-06\n                                                   ),\n                 loss    = [dice_loss], \n                 metrics = [dice_coef]\n                )\n\nunet.summary()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-02T13:48:25.906043Z","iopub.execute_input":"2025-12-02T13:48:25.906711Z","iopub.status.idle":"2025-12-02T13:48:26.705932Z","shell.execute_reply.started":"2025-12-02T13:48:25.906686Z","shell.execute_reply":"2025-12-02T13:48:26.705421Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"callbacks_list = [keras.callbacks.EarlyStopping(monitor  = \"val_loss\",\n                                                patience = 5,\n                                               ),\n                  keras.callbacks.ModelCheckpoint(filepath       = \"best-u_net-model.weights.h5\",\n                                                  monitor        = \"val_loss\",\n                                                  save_best_only = True,\n                                                 )\n                 ]\n\nhistory = unet.fit(batched_train_dataset,\n                   validation_data = batched_val_dataset,\n                   epochs = 50,\n                   callbacks = callbacks_list,\n                  )","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-02T13:48:42.465829Z","iopub.execute_input":"2025-12-02T13:48:42.466538Z","iopub.status.idle":"2025-12-02T14:11:16.630705Z","shell.execute_reply.started":"2025-12-02T13:48:42.466516Z","shell.execute_reply":"2025-12-02T14:11:16.630082Z"},"_kg_hide-output":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"loss = history.history['loss']\nval_loss = history.history['val_loss']\nepochs = range(1, len(loss) + 1)\n\nplt.figure(figsize=(10, 6))\nplt.plot(epochs, loss, label = 'Training dice loss')\nplt.plot(epochs, val_loss, label = 'Validation dice loss')\nplt.title('Training and validation loss')\nplt.xlabel('Epochs')\nplt.ylabel('Loss')\nplt.legend()\nplt.grid(True)\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-02T14:13:03.742868Z","iopub.execute_input":"2025-12-02T14:13:03.743606Z","iopub.status.idle":"2025-12-02T14:13:03.944202Z","shell.execute_reply.started":"2025-12-02T14:13:03.743571Z","shell.execute_reply":"2025-12-02T14:13:03.943341Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"unet = keras.models.load_model(\"/kaggle/working/best-u_net-model\", \n                               custom_objects={'dice_coef': dice_coef, 'dice_loss': dice_loss}\n                              )\nshow_predictions(model = unet, dataset = batched_train_dataset, num = 6)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-02T14:12:36.212196Z","iopub.execute_input":"2025-12-02T14:12:36.212760Z","iopub.status.idle":"2025-12-02T14:12:53.296580Z","shell.execute_reply.started":"2025-12-02T14:12:36.212737Z","shell.execute_reply":"2025-12-02T14:12:53.295964Z"}},"outputs":[],"execution_count":null}]}