{"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","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"},"vscode":{"interpreter":{"hash":"0987726327b6c9b2f5f936e5ed66944479e247a882213e2c65b0f41fe619a749"}},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceType":"competition","sourceId":90316,"databundleVersionId":10477888},{"sourceType":"datasetVersion","sourceId":10368855,"datasetId":6422393,"databundleVersionId":10680652}],"dockerImageVersionId":30823,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import numpy as np\nimport cv2 as cv\nimport csv\nimport matplotlib.pyplot as plt","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-04T09:39:21.715855Z","iopub.execute_input":"2025-01-04T09:39:21.716157Z","iopub.status.idle":"2025-01-04T09:39:21.719881Z","shell.execute_reply.started":"2025-01-04T09:39:21.716134Z","shell.execute_reply":"2025-01-04T09:39:21.719098Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!ls /kaggle/input/\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-04T09:39:25.666162Z","iopub.execute_input":"2025-01-04T09:39:25.666565Z","iopub.status.idle":"2025-01-04T09:39:25.786547Z","shell.execute_reply.started":"2025-01-04T09:39:25.666528Z","shell.execute_reply":"2025-01-04T09:39:25.785658Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!ls /kaggle/input/ai-dl-multiclass-segmentation/\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-04T09:39:27.673046Z","iopub.execute_input":"2025-01-04T09:39:27.673337Z","iopub.status.idle":"2025-01-04T09:39:27.793212Z","shell.execute_reply.started":"2025-01-04T09:39:27.673313Z","shell.execute_reply":"2025-01-04T09:39:27.792426Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Decoding RLEs into masks for training","metadata":{},"attachments":{}},{"cell_type":"code","source":"def decode_rle_to_mask(rle, height, width, viz=False):\n    '''\n    rle : run-length as string formated (start value, count)\n    height : height of the mask \n    width : width of the mask\n    returns binary mask\n    '''\n    rle = np.array(rle.split(' ')).reshape(-1, 2)\n    mask = np.zeros((height*width, 1, 3))\n    if viz:\n        color = np.random.rand(3)\n    else:\n        color = [1,1,1]\n    for i in rle:\n        mask[int(i[0]):int(i[0])+int(i[1]), :, :] = color\n\n    return mask.reshape(height, width, 3)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-04T09:39:30.797434Z","iopub.execute_input":"2025-01-04T09:39:30.797748Z","iopub.status.idle":"2025-01-04T09:39:30.803570Z","shell.execute_reply.started":"2025-01-04T09:39:30.797727Z","shell.execute_reply":"2025-01-04T09:39:30.802756Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Open train file\nwith open('/kaggle/input/ai-dl-multiclass-segmentation/train.csv', 'r', encoding='utf-8') as train:\n    reader = csv.reader(train)\n    # skip headers row\n    next(reader)\n\n    # pull the masks for the first image\n    row = next(iter(reader))\n    print(row[0])\n    mask1 = decode_rle_to_mask(row[4],int(row[1]), int(row[2]))\n    row = next(iter(reader))\n    mask2 = decode_rle_to_mask(row[4],int(row[1]), int(row[2]))\n    row = next(iter(reader))\n    mask3 = decode_rle_to_mask(row[4],int(row[1]), int(row[2]))    ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-04T09:39:35.053928Z","iopub.execute_input":"2025-01-04T09:39:35.054197Z","iopub.status.idle":"2025-01-04T09:39:35.073700Z","shell.execute_reply.started":"2025-01-04T09:39:35.054176Z","shell.execute_reply":"2025-01-04T09:39:35.073004Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Head mask\nplt.imshow(mask1)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-04T09:39:37.069992Z","iopub.execute_input":"2025-01-04T09:39:37.070284Z","iopub.status.idle":"2025-01-04T09:39:37.416202Z","shell.execute_reply.started":"2025-01-04T09:39:37.070260Z","shell.execute_reply":"2025-01-04T09:39:37.415285Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Body mask\nplt.imshow(mask2)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-04T09:39:39.998013Z","iopub.execute_input":"2025-01-04T09:39:39.998331Z","iopub.status.idle":"2025-01-04T09:39:40.238205Z","shell.execute_reply.started":"2025-01-04T09:39:39.998303Z","shell.execute_reply":"2025-01-04T09:39:40.237401Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Legs mask\nplt.imshow(mask3)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-04T09:39:42.631939Z","iopub.execute_input":"2025-01-04T09:39:42.632224Z","iopub.status.idle":"2025-01-04T09:39:42.870277Z","shell.execute_reply.started":"2025-01-04T09:39:42.632202Z","shell.execute_reply":"2025-01-04T09:39:42.869468Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# combine masks for training\nmixed_mask = mask1*1+mask2*2+mask3*3 #+mask4*4\n\n# This is an image multi-segmentation task. The classes are:\n    # 1: Head\n    # 2: Body\n    # 3: Legs\n    # 4: Tail\n# As seen below (notice this image does not include a tail in the image so no class 4)\nprint(np.unique(mixed_mask))\n\n## For visualization purposes only, we can see the different segments colorcoded in the mixed mask by dividing my the sum of the class values (normalization)\n## However, for submission, you should split the mask into 4 binary masks and RLE encode them individually as shown below.\n\nplt.imshow(mixed_mask/6) # normalized just to show different segmentations (divide by sum of values)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-04T09:39:46.180151Z","iopub.execute_input":"2025-01-04T09:39:46.180496Z","iopub.status.idle":"2025-01-04T09:39:46.435079Z","shell.execute_reply.started":"2025-01-04T09:39:46.180466Z","shell.execute_reply":"2025-01-04T09:39:46.434151Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Training Pipeline","metadata":{},"attachments":{}},{"cell_type":"code","source":"import os\nimport numpy as np\nimport tensorflow as tf\nfrom tensorflow.keras import layers, models\nfrom tensorflow.keras.applications import MobileNetV2\nimport cv2","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-04T09:44:14.076024Z","iopub.execute_input":"2025-01-04T09:44:14.076339Z","iopub.status.idle":"2025-01-04T09:44:14.080717Z","shell.execute_reply.started":"2025-01-04T09:44:14.076316Z","shell.execute_reply":"2025-01-04T09:44:14.079764Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Load and resize images from a directory\ndef load_images_from_directory(directory_path, target_size=(64, 64)):\n    images = []\n    for img_name in os.listdir(directory_path):\n        img_path = os.path.join(directory_path, img_name)\n        img = cv2.imread(img_path)\n        if img is not None:\n            img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n            img = cv2.resize(img, target_size)\n            images.append(img)\n    images = np.array(images, dtype=np.float32) / 255.0  # Normalize\n    print(f\"Loaded {len(images)} images with shape {images.shape}\")\n    return images\n\n# Load train images\ntrain_images_path = \"/kaggle/input/ai-dl-multiclass-segmentation/TrainImages\"\nX_train = load_images_from_directory(train_images_path)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-04T09:44:47.271021Z","iopub.execute_input":"2025-01-04T09:44:47.271295Z","iopub.status.idle":"2025-01-04T09:44:57.004835Z","shell.execute_reply.started":"2025-01-04T09:44:47.271274Z","shell.execute_reply":"2025-01-04T09:44:57.004010Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Apply strong augmentations for contrastive pairs\ndata_augmentation = tf.keras.Sequential([\n    layers.RandomFlip(\"horizontal\"),\n    layers.RandomRotation(0.2),\n    layers.RandomZoom(0.2)\n])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-04T09:45:11.913796Z","iopub.execute_input":"2025-01-04T09:45:11.914101Z","iopub.status.idle":"2025-01-04T09:45:11.928463Z","shell.execute_reply.started":"2025-01-04T09:45:11.914079Z","shell.execute_reply":"2025-01-04T09:45:11.927822Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Create a contrastive learning model using MobileNetV2\ndef create_pretraining_model(input_shape=(64, 64, 3)):\n    # Base encoder (MobileNetV2 is lighter than ResNet50)\n    base_model = MobileNetV2(weights=None, include_top=False, input_shape=input_shape)\n    x = layers.GlobalAveragePooling2D()(base_model.output)\n    x = layers.Dense(128, activation='relu')(x)  # Projection head\n    representation = layers.Dense(128)(x)\n\n    # Dual-input for contrastive learning\n    input_view_1 = tf.keras.Input(shape=input_shape)\n    input_view_2 = tf.keras.Input(shape=input_shape)\n\n    # Encode both views\n    z_i = tf.keras.Model(inputs=base_model.input, outputs=representation)(input_view_1)\n    z_j = tf.keras.Model(inputs=base_model.input, outputs=representation)(input_view_2)\n\n    return tf.keras.Model(inputs=[input_view_1, input_view_2], outputs=[z_i, z_j])\n\n# Initialize the model\npretraining_model = create_pretraining_model()\npretraining_model.summary()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-04T09:45:34.091652Z","iopub.execute_input":"2025-01-04T09:45:34.091973Z","iopub.status.idle":"2025-01-04T09:45:34.828004Z","shell.execute_reply.started":"2025-01-04T09:45:34.091945Z","shell.execute_reply":"2025-01-04T09:45:34.827345Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Convert the dataset into a TensorFlow Dataset for memory efficiency\nbatch_size = 32  # Reduced batch size for memory optimization\ntrain_dataset = tf.data.Dataset.from_tensor_slices(X_train).batch(batch_size).shuffle(buffer_size=100)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-04T09:45:52.774175Z","iopub.execute_input":"2025-01-04T09:45:52.774490Z","iopub.status.idle":"2025-01-04T09:45:53.046208Z","shell.execute_reply.started":"2025-01-04T09:45:52.774465Z","shell.execute_reply":"2025-01-04T09:45:53.045538Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Compile the model with contrastive loss and Adam optimizer\nloss_fn = ContrastiveLoss()\noptimizer = tf.keras.optimizers.Adam(learning_rate=0.001)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-04T09:46:30.061898Z","iopub.execute_input":"2025-01-04T09:46:30.062203Z","iopub.status.idle":"2025-01-04T09:46:30.069792Z","shell.execute_reply.started":"2025-01-04T09:46:30.062179Z","shell.execute_reply":"2025-01-04T09:46:30.068957Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Define a single training step\n@tf.function\ndef train_step(images):\n    # Generate contrastive pairs\n    augmented_views_1, augmented_views_2 = generate_contrastive_pairs(images)\n\n    with tf.GradientTape() as tape:\n        # Forward pass with both views\n        z_i, z_j = pretraining_model([augmented_views_1, augmented_views_2], training=True)\n        # Calculate contrastive loss\n        loss = loss_fn(z_i, z_j)\n\n    # Backpropagation and gradient descent\n    gradients = tape.gradient(loss, pretraining_model.trainable_variables)\n    optimizer.apply_gradients(zip(gradients, pretraining_model.trainable_variables))\n    return loss","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-04T09:46:44.100844Z","iopub.execute_input":"2025-01-04T09:46:44.101238Z","iopub.status.idle":"2025-01-04T09:46:44.106498Z","shell.execute_reply.started":"2025-01-04T09:46:44.101202Z","shell.execute_reply":"2025-01-04T09:46:44.105576Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Train the model with mini-batch gradient descent\nEPOCHS = 10\nfor epoch in range(EPOCHS):\n    total_loss = 0\n    num_batches = 0\n    for batch in train_dataset:\n        loss = train_step(batch)\n        total_loss += loss\n        num_batches += 1\n    print(f\"Epoch {epoch + 1}/{EPOCHS}, Loss: {total_loss / num_batches:.4f}\")\n\n# Save the pretrained model\npretraining_model.save(\"/kaggle/working/contrastive_pretrained_model.h5\")\nprint(\"✅ Pretraining Completed and Model Saved!\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-04T09:46:57.271305Z","iopub.execute_input":"2025-01-04T09:46:57.271643Z","iopub.status.idle":"2025-01-04T09:48:36.154841Z","shell.execute_reply.started":"2025-01-04T09:46:57.271617Z","shell.execute_reply":"2025-01-04T09:48:36.154117Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class NTXentLoss(tf.keras.losses.Loss):\n    def __init__(self, temperature=0.5):\n        super().__init__()\n        self.temperature = temperature\n\n    def call(self, z_i, z_j):\n        # Normalize embeddings\n        z_i = tf.math.l2_normalize(z_i, axis=1)\n        z_j = tf.math.l2_normalize(z_j, axis=1)\n\n        # Combine positive pairs for contrastive comparison\n        batch_size = tf.shape(z_i)[0]\n        projections = tf.concat([z_i, z_j], axis=0)\n\n        # Compute similarity matrix\n        similarity_matrix = tf.matmul(projections, projections, transpose_b=True) / self.temperature\n\n        # Mask self-similar pairs (diagonal masking)\n        mask = tf.eye(2 * batch_size)\n        mask = tf.cast(mask, dtype=tf.bool)\n        logits = tf.where(mask, -1e9 * tf.ones_like(similarity_matrix), similarity_matrix)\n\n        # Correct positive pair labeling\n        labels = tf.range(batch_size)\n        labels = tf.concat([labels, labels], axis=0)\n\n        # Apply softmax and compute cross-entropy loss\n        loss = tf.keras.losses.sparse_categorical_crossentropy(labels, logits, from_logits=True)\n        return tf.reduce_mean(loss)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-04T09:53:15.507717Z","iopub.execute_input":"2025-01-04T09:53:15.508017Z","iopub.status.idle":"2025-01-04T09:53:15.514261Z","shell.execute_reply.started":"2025-01-04T09:53:15.507992Z","shell.execute_reply":"2025-01-04T09:53:15.513306Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Adjusting the temperature and optimizer settings\nloss_fn = NTXentLoss(temperature=0.1)  # Reduced temperature for stability\noptimizer = tf.keras.optimizers.Adam(learning_rate=0.0003)  # Reduced LR to prevent gradient explosion","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-04T09:53:26.848237Z","iopub.execute_input":"2025-01-04T09:53:26.848579Z","iopub.status.idle":"2025-01-04T09:53:26.855651Z","shell.execute_reply.started":"2025-01-04T09:53:26.848551Z","shell.execute_reply":"2025-01-04T09:53:26.854865Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Updated Training Step with Gradient Clipping\n@tf.function\ndef train_step(images):\n    augmented_views_1, augmented_views_2 = generate_contrastive_pairs(images)\n    with tf.GradientTape() as tape:\n        z_i, z_j = pretraining_model([augmented_views_1, augmented_views_2], training=True)\n        z_j = tf.stop_gradient(z_j)  # Prevent collapsing gradients\n        loss = loss_fn(z_i, z_j)\n\n    # Apply gradient clipping to avoid exploding gradients\n    gradients = tape.gradient(loss, pretraining_model.trainable_variables)\n    gradients = [tf.clip_by_value(g, -1.0, 1.0) for g in gradients]  # Gradient clipping\n    optimizer.apply_gradients(zip(gradients, pretraining_model.trainable_variables))\n    return loss","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-04T09:53:34.754970Z","iopub.execute_input":"2025-01-04T09:53:34.755259Z","iopub.status.idle":"2025-01-04T09:53:34.977426Z","shell.execute_reply.started":"2025-01-04T09:53:34.755238Z","shell.execute_reply":"2025-01-04T09:53:34.976565Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Train with corrected settings and loss function\nEPOCHS = 10\nfor epoch in range(EPOCHS):\n    total_loss = 0\n    num_batches = 0\n    for batch in train_dataset:\n        loss = train_step(batch)\n        total_loss += loss\n        num_batches += 1\n    print(f\"Epoch {epoch + 1}/{EPOCHS}, Loss: {total_loss / num_batches:.4f}\")\n\n# Save the corrected contrastive learning model\npretraining_model.save(\"/kaggle/working/fixed_contrastive_model_stable.h5\")\nprint(\"✅ Fixed Training Completed with Stable Loss!\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-04T09:53:44.495645Z","iopub.execute_input":"2025-01-04T09:53:44.495960Z","iopub.status.idle":"2025-01-04T09:54:50.128714Z","shell.execute_reply.started":"2025-01-04T09:53:44.495935Z","shell.execute_reply":"2025-01-04T09:54:50.127384Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Encoding masks into RLE for submission","metadata":{},"attachments":{}},{"cell_type":"code","source":"def encode_mask_to_rle(mask):\n    '''\n    mask: numpy array binary mask \n    1 - mask \n    0 - background\n    Returns encoded run length \n    '''\n    pixels = mask.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)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-04T08:34:28.099903Z","iopub.status.idle":"2025-01-04T08:34:28.100200Z","shell.execute_reply":"2025-01-04T08:34:28.100072Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"mask1.shape","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-04T08:34:28.100900Z","iopub.status.idle":"2025-01-04T08:34:28.101232Z","shell.execute_reply":"2025-01-04T08:34:28.101063Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# IMPORTANT || Convert mask to grayscale\nmask1 = mask1[:,:,0] # or using other methods depending on mask\nmask1.shape","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-04T08:34:28.102280Z","iopub.status.idle":"2025-01-04T08:34:28.102583Z","shell.execute_reply":"2025-01-04T08:34:28.102459Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"rle = encode_mask_to_rle(mask1)\nrle","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-04T08:34:28.103194Z","iopub.status.idle":"2025-01-04T08:34:28.103575Z","shell.execute_reply":"2025-01-04T08:34:28.103396Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}