{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.12.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[],"dockerImageVersionId":28755,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import pandas as pd\nimport os\n\ncsv_path = '/kaggle/input/competitions/airbus-ship-detection/train_ship_segmentations_v2.csv'\n\ndf = pd.read_csv(csv_path)\n\ndisplay(df.head())","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-08-11T12:05:34.834392Z","iopub.execute_input":"2026-08-11T12:05:34.834817Z","iopub.status.idle":"2026-08-11T12:05:35.489077Z","shell.execute_reply.started":"2026-08-11T12:05:34.834768Z","shell.execute_reply":"2026-08-11T12:05:35.488102Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df[\"HasShip\"] = df[\"EncodedPixels\"].notnull().astype(int)\nships_per_image = df.groupby(\"ImageId\")[\"HasShip\"].sum().reset_index()\nships_per_image.columns = [\"ImageId\", \"TotalShips\"]\n\nempty_images = (ships_per_image[\"TotalShips\"] == 0).sum()\nship_images = (ships_per_image[\"TotalShips\"] > 0).sum()\n\nprint(f\"Total number of images: {len(ships_per_image)}\")\nprint(f\"Number of empty images: {empty_images}\")\nprint(f\"Number of images with at least one ship: {ship_images}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-11T12:05:35.490522Z","iopub.execute_input":"2026-08-11T12:05:35.490831Z","iopub.status.idle":"2026-08-11T12:05:35.627614Z","shell.execute_reply.started":"2026-08-11T12:05:35.490804Z","shell.execute_reply":"2026-08-11T12:05:35.626822Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df_ships = ships_per_image[ships_per_image[\"TotalShips\"] > 0]\nship_counts = df_ships[\"TotalShips\"].value_counts().sort_index()\n\nprint(\"Number of ships per image: \")\nprint(ship_counts) ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-11T12:05:35.628429Z","iopub.execute_input":"2026-08-11T12:05:35.628756Z","iopub.status.idle":"2026-08-11T12:05:35.646435Z","shell.execute_reply.started":"2026-08-11T12:05:35.628714Z","shell.execute_reply":"2026-08-11T12:05:35.645328Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df_ships = df.dropna(subset = [\"EncodedPixels\"]).copy()\ndf_ships[\"ShipArea\"] = df_ships[\"EncodedPixels\"].apply(lambda x: sum([int(length) for length in x.split()[1::2]]))\n\nprint(f\"Minimum ship area: {df_ships[\"ShipArea\"].min()} pixels\")\nprint(f\"Maximum ship area: {df_ships[\"ShipArea\"].max()} pixels\")\nprint(f\"Average Ship Area: {int(df_ships['ShipArea'].mean())} pixels\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-11T12:05:35.648395Z","iopub.execute_input":"2026-08-11T12:05:35.648991Z","iopub.status.idle":"2026-08-11T12:05:36.645605Z","shell.execute_reply.started":"2026-08-11T12:05:35.648944Z","shell.execute_reply":"2026-08-11T12:05:36.644846Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import matplotlib.pyplot as plt\nimport numpy as np\nfrom skimage.io import imread\n\ndef rle_decode(mask_rle, shape = (768, 768)):\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\n\ndef get_mask(image_id, dataframe):\n    rle_list = dataframe[dataframe[\"ImageId\"] == image_id][\"EncodedPixels\"].tolist()\n    all_masks = np.zeros((768, 768))\n    for rle in rle_list:\n        if pd.notna(rle):\n            all_masks += rle_decode(rle)\n    return all_masks\n\ntrain_img_dir = '/kaggle/input/competitions/airbus-ship-detection/train_v2'\nsample_image_id = df_ships[\"ImageId\"].iloc[10]\nimg_path = os.path.join(train_img_dir, sample_image_id)\n\nimg = imread(img_path)\nmask = get_mask(sample_image_id, df)\n\nfig, ax = plt.subplots(1,3,figsize = (16,5))\n\nax[0].imshow(img)\nax[0].set_title('Original Satellite Image')\nax[0].axis('off')\n\nax[1].imshow(mask, cmap='gray')\nax[1].set_title('Target Masks')\nax[1].axis('off')\n\nax[2].imshow(img)\nax[2].imshow(mask, cmap='Reds', alpha=0.5) \nax[2].set_title('Overlay (Image + Mask)')\nax[2].axis('off')\n\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-11T12:05:36.646702Z","iopub.execute_input":"2026-08-11T12:05:36.647108Z","iopub.status.idle":"2026-08-11T12:05:37.274327Z","shell.execute_reply.started":"2026-08-11T12:05:36.647069Z","shell.execute_reply":"2026-08-11T12:05:37.273232Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"unique_ship_images = df_ships[\"ImageId\"].unique()\ndf_positive_unique = pd.DataFrame({\"ImageId\": unique_ship_images})\n\ndf_empty = df[df[\"EncodedPixels\"].isna()].copy()\ndf_empty = df_empty.drop_duplicates(subset = [\"ImageId\"])\n\nnum_ships = len(df_positive_unique)\ndf_empty_sampled = df_empty.sample(n = num_ships, random_state = 42)\n\nprint(f\"Number of images with ships: {num_ships}\")\nprint(f\"Number of balanced empty images: {len(df_empty_sampled)}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-11T12:05:37.275452Z","iopub.execute_input":"2026-08-11T12:05:37.276158Z","iopub.status.idle":"2026-08-11T12:05:37.343931Z","shell.execute_reply.started":"2026-08-11T12:05:37.276133Z","shell.execute_reply":"2026-08-11T12:05:37.343232Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df_balanced = pd.concat([df_positive_unique, df_empty_sampled], ignore_index = True)\ndf_balanced = df_balanced.sample(frac = 1, random_state = 42).reset_index(drop = True)\n\nprint(f\"Total balanced dataset size: {len(df_balanced)}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-11T12:05:37.344912Z","iopub.execute_input":"2026-08-11T12:05:37.345420Z","iopub.status.idle":"2026-08-11T12:05:37.407781Z","shell.execute_reply.started":"2026-08-11T12:05:37.345393Z","shell.execute_reply":"2026-08-11T12:05:37.406928Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import numpy as np\nimport tensorflow as tf\nimport os\nimport cv2\nfrom tensorflow.keras.utils import Sequence\n\nimg_height = 256\nimg_width = 256\nbatch_size = 16\ntrain_img_dir = \"/kaggle/input/competitions/airbus-ship-detection/train_v2\"\n\nclass AirbusDataGenerator(Sequence):\n    def __init__(self, dataframe, image_dir, batch_size = 16, img_size = (256, 256), shuffle = True):\n        self.df = dataframe\n        self.image_dir = image_dir\n        self.batch_size = batch_size\n        self.img_size = img_size  \n        self.shuffle = shuffle\n        self.indexes = np.arange(len(self.df))\n        self.on_epoch_end()\n\n    def on_epoch_end(self):\n        if self.shuffle == True:\n            np.random.shuffle(self.indexes)\n\n    def __len__(self):\n        return int(np.floor(len(self.df) / self.batch_size))\n\n    def __getitem__(self, index):\n        batch_indexes = self.indexes[index * self.batch_size:(index + 1) * self.batch_size]\n        batch_df = self.df.iloc[batch_indexes]\n    \n        x = np.empty((self.batch_size, self.img_size[0], self.img_size[1], 3), dtype = np.float32)\n        y = np.empty((self.batch_size, self.img_size[0], self.img_size[1], 1), dtype = np.float32)\n\n        for i, (_, row) in enumerate(batch_df.iterrows()):\n            image_id = row[\"ImageId\"]\n            img_path = os.path.join(self.image_dir, image_id)\n\n            img = imread(img_path)\n            img_resized = tf.image.resize(img, self.img_size).numpy() / 255.0\n            \n            mask = get_mask(image_id, df)\n\n            mask_expanded = np.expand_dims(mask, axis = -1)\n            mask_resized = tf.image.resize(mask_expanded, self.img_size).numpy()\n\n            x[i] = img_resized\n            y[i] = mask_resized\n\n        return x, y","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-11T12:05:37.408984Z","iopub.execute_input":"2026-08-11T12:05:37.409345Z","iopub.status.idle":"2026-08-11T12:05:50.453678Z","shell.execute_reply.started":"2026-08-11T12:05:37.409318Z","shell.execute_reply":"2026-08-11T12:05:50.452900Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.model_selection import train_test_split\n\ntrain_df, val_df = train_test_split(df_balanced, test_size = 0.2, random_state = 42)\n\ntrain_generator = AirbusDataGenerator(\n    dataframe = train_df,\n    image_dir = train_img_dir,\n    batch_size = 16,\n    img_size = (256, 256),\n    shuffle = True\n)\n\nx_batch, y_batch = train_generator[0]\n\nprint(f\"x_batch size: {x_batch.shape}\")\nprint(f\"y_batch size: {y_batch.shape}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-11T12:05:50.454712Z","iopub.execute_input":"2026-08-11T12:05:50.455297Z","iopub.status.idle":"2026-08-11T12:05:53.130052Z","shell.execute_reply.started":"2026-08-11T12:05:50.455272Z","shell.execute_reply":"2026-08-11T12:05:53.129257Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import tensorflow.keras.backend as K \n\ndef dice_coef(y_true, y_pred, smooth = 1):\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    return (2. * intersection + smooth) / (K.sum(y_true_f) + K.sum(y_pred_f) + smooth)\n\ndef dice_loss(y_true, y_pred):\n    return 1.0 - dice_coef(y_true, y_pred)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-11T12:05:53.132419Z","iopub.execute_input":"2026-08-11T12:05:53.133070Z","iopub.status.idle":"2026-08-11T12:05:53.139569Z","shell.execute_reply.started":"2026-08-11T12:05:53.132990Z","shell.execute_reply":"2026-08-11T12:05:53.138599Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from tensorflow.keras.models import Model\nfrom tensorflow.keras.layers import Input, Conv2D, MaxPooling2D, Conv2DTranspose, concatenate\nfrom tensorflow.keras.optimizers import Adam\n\n\n\ndef build_unet(input_shape=(256, 256, 3)):\n    inputs = Input(input_shape)\n\n    c1 = Conv2D(16, (3, 3), activation='relu', padding='same')(inputs)\n    c1 = Conv2D(16, (3, 3), activation='relu', padding='same')(c1)\n    p1 = MaxPooling2D((2, 2))(c1)\n\n    c2 = Conv2D(32, (3, 3), activation='relu', padding='same')(p1)\n    c2 = Conv2D(32, (3, 3), activation='relu', padding='same')(c2)\n    p2 = MaxPooling2D((2, 2))(c2)\n\n    c3 = Conv2D(64, (3, 3), activation='relu', padding='same')(p2)\n    c3 = Conv2D(64, (3, 3), activation='relu', padding='same')(c3)\n\n    u4 = Conv2DTranspose(32, (2, 2), strides=(2, 2), padding='same')(c3)\n    u4 = concatenate([u4, c2]) \n    c4 = Conv2D(32, (3, 3), activation='relu', padding='same')(u4)\n    c4 = Conv2D(32, (3, 3), activation='relu', padding='same')(c4)\n\n    u5 = Conv2DTranspose(16, (2, 2), strides=(2, 2), padding='same')(c4)\n    u5 = concatenate([u5, c1]) \n    c5 = Conv2D(16, (3, 3), activation='relu', padding='same')(u5)\n    c5 = Conv2D(16, (3, 3), activation='relu', padding='same')(c5)\n\n    outputs = Conv2D(1, (1, 1), activation='sigmoid')(c5)\n\n    model = Model(inputs=[inputs], outputs=[outputs])\n    return model\n\n\nmodel = build_unet()\n\nmodel.compile(\n    optimizer=Adam(learning_rate=1e-4),\n    loss=dice_loss,\n    metrics=[dice_coef, \"accuracy\"]\n)\n\nmodel.summary()","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"val_generator = AirbusDataGenerator(\n    dataframe = val_df,\n    image_dir = train_img_dir,\n    batch_size = 16,\n    img_size = (256, 256),\n    shuffle = False\n)\n\nprint(\"2 epoch starting..\")\n\nhistory = model.fit(\n    train_generator,\n    validation_data = val_generator,\n    epochs = 2\n)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!pip install segmentation_models","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"os.environ[\"SM_FRAMEWORK\"] = \"tf.keras\"\nimport segmentation_models as sm","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-11T12:06:34.686418Z","iopub.execute_input":"2026-08-11T12:06:34.686732Z","iopub.status.idle":"2026-08-11T12:06:34.744122Z","shell.execute_reply.started":"2026-08-11T12:06:34.686701Z","shell.execute_reply":"2026-08-11T12:06:34.743092Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"backbone = \"resnet34\"\n\nmodel = sm.Unet(\n    backbone_name = backbone,\n    encoder_weights = \"imagenet\",\n    classes = 1,\n    activation = \"sigmoid\"\n)\n\nmodel.summary()","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from tensorflow.keras.optimizers import Adam\noptimizer = Adam(learning_rate = 0.001)\n\nloss = sm.losses.bce_jaccard_loss\n\nmetrics = [sm.metrics.IOUScore(threshold = 0.5)]\n\nmodel.compile(\n    optimizer = optimizer,\n    loss = loss,\n    metrics = metrics\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-11T12:06:47.253637Z","iopub.execute_input":"2026-08-11T12:06:47.254288Z","iopub.status.idle":"2026-08-11T12:06:47.264072Z","shell.execute_reply.started":"2026-08-11T12:06:47.254214Z","shell.execute_reply":"2026-08-11T12:06:47.263176Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from tensorflow.keras.callbacks import ModelCheckpoint, EarlyStopping\n\ncheckpoint = ModelCheckpoint(\n    filepath = \"best_resnet34_unet_epoch_{epoch:02d}_iou_{val_iou_score:.4f}.keras\",\n    monitor =  \"val_iou_score\",\n    mode = \"max\",\n    save_best_only = True,\n    verbose = 1\n)\n\nearly_stop = EarlyStopping(\n    monitor = \"val_iou_score\",\n    mode = \"max\",\n    patience = 4,\n    verbose = 1,\n    restore_best_weights = True\n)\n\ncallbacks_list = [checkpoint, early_stop]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-11T12:40:19.392781Z","iopub.execute_input":"2026-08-11T12:40:19.393095Z","iopub.status.idle":"2026-08-11T12:40:19.398269Z","shell.execute_reply.started":"2026-08-11T12:40:19.393070Z","shell.execute_reply":"2026-08-11T12:40:19.397418Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"Training of the ResNet34-backboned model is starting..\")\n\nhistory = model.fit(\n    train_generator,\n    validation_data = val_generator,\n    epochs = 30,\n    steps_per_epoch = 1000,\n    validation_steps = 200,\n    callbacks = callbacks_list\n)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import matplotlib.pyplot as plt\nimport numpy as np\nimport random \n\nrandom_index = random.randint(0, len(val_generator) - 1)\nimages, true_masks = val_generator[random_index]\n\npredictions = model.predict(images)\n\nfor i in range(3):\n    plt.figure(figsize = (15, 5))\n\n    plt.subplot(1, 3, 1)\n    plt.title(\"Original Image\")\n    plt.imshow(images[i])\n\n    plt.subplot(1, 3, 2)\n    plt.title(\"Real Mask\")\n    plt.imshow(true_masks[i].squeeze(), cmap = \"jet\", alpha = 0.5)\n\n    plt.subplot(1, 3, 3)\n    plt.title(\"Model prediction\")\n    plt.imshow(images[i])\n\n    pred_mask = (predictions[i].squeeze() > 0.525).astype(np.uint8)\n    plt.imshow(pred_mask, cmap = \"jet\", alpha = 0.5)\n\n    plt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-11T18:47:11.606651Z","iopub.execute_input":"2026-08-11T18:47:11.607699Z","iopub.status.idle":"2026-08-11T18:47:13.797360Z","shell.execute_reply.started":"2026-08-11T18:47:11.607663Z","shell.execute_reply":"2026-08-11T18:47:13.796447Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.metrics import classification_report\n\ny_true =true_masks[0].squeeze().flatten()\ny_pred = (predictions[0].squeeze() > 0.525).astype(np.uint8).flatten()\n\nprint(classification_report(y_true, y_pred, target_names = [\"Background\", \"Ship\"]))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-11T18:31:12.083512Z","iopub.execute_input":"2026-08-11T18:31:12.084465Z","iopub.status.idle":"2026-08-11T18:31:12.110273Z","shell.execute_reply.started":"2026-08-11T18:31:12.084430Z","shell.execute_reply":"2026-08-11T18:31:12.109627Z"}},"outputs":[],"execution_count":null}]}