{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.14","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":13333,"databundleVersionId":862146,"sourceType":"competition"}],"dockerImageVersionId":30787,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# **1. Imports**","metadata":{}},{"cell_type":"code","source":"import warnings\nwarnings.filterwarnings(\"ignore\")\nimport pandas as pd\nimport numpy as np\nfrom torch.utils.data import Dataset, DataLoader\nimport cv2\nimport matplotlib.pyplot as plt\nfrom tqdm import tqdm\nimport torch\nimport torch.nn as nn\nimport torchvision.models as models\nfrom tqdm import tqdm\nfrom sklearn.model_selection import train_test_split\nimport os\n!pip install segmentation-models-pytorch --quiet\nimport segmentation_models_pytorch as smp\nfrom tqdm import tqdm\nfrom sklearn.model_selection import KFold\nimport albumentations as A\nfrom albumentations import (Compose, ShiftScaleRotate, Resize, RandomRotate90,\n                            VerticalFlip, HorizontalFlip, OneOf, ElasticTransform,\n                            GridDistortion, OpticalDistortion, CLAHE,\n                            GaussNoise, ISONoise, RandomBrightnessContrast, RandomGamma)\nfrom albumentations.pytorch import ToTensorV2\nfrom torchvision import transforms\nimport time\n!pip install torch-lr-finder\nfrom torch_lr_finder import LRFinder\nfrom torch.optim.lr_scheduler import CosineAnnealingWarmRestarts","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2024-11-20T19:07:05.945233Z","iopub.execute_input":"2024-11-20T19:07:05.945497Z","iopub.status.idle":"2024-11-20T19:07:39.433744Z","shell.execute_reply.started":"2024-11-20T19:07:05.945469Z","shell.execute_reply":"2024-11-20T19:07:39.432889Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **2. Data Prep**","metadata":{}},{"cell_type":"markdown","source":"**2.1 Load and Process the DataFrame**","metadata":{}},{"cell_type":"code","source":"# Load the train.csv file\ntrain_df = pd.read_csv('/kaggle/input/understanding_cloud_organization/train.csv')\n\n# Separate Image_Label into ImageId and Label\ntrain_df[['ImageId', 'Label']] = train_df['Image_Label'].str.split('_', expand=True)\ntrain_df = train_df.drop(columns=['Image_Label'])\n\n# Fill missing EncodedPixels with NaN\ntrain_df['EncodedPixels'] = train_df['EncodedPixels'].fillna('')\n\n# Pivot the dataframe to have one row per image\ntrain_df = train_df.pivot(index='ImageId', columns='Label', values='EncodedPixels').reset_index()\n\n# Fill missing values with empty strings\ntrain_df.fillna('', inplace=True)\n\nprint(train_df.head())\n","metadata":{"execution":{"iopub.status.busy":"2024-11-20T19:07:39.435516Z","iopub.execute_input":"2024-11-20T19:07:39.436274Z","iopub.status.idle":"2024-11-20T19:07:44.075192Z","shell.execute_reply.started":"2024-11-20T19:07:39.436241Z","shell.execute_reply":"2024-11-20T19:07:44.074297Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**2.2 Split Data into Training and Validation Sets**","metadata":{}},{"cell_type":"code","source":"# Initialize the 'fold' column to -1\ntrain_df['fold'] = -1\n\n# Initialize KFold\nkf = KFold(n_splits=9, shuffle=True, random_state=42)\n\n# Assign fold numbers\nfor fold, (train_idx, val_idx) in enumerate(kf.split(train_df)):\n    train_df.loc[val_idx, 'fold'] = fold\n\n# Now select one fold for validation (e.g., fold 0)\ntrain_data = train_df[train_df['fold'] != 0].reset_index(drop=True)\nval_data = train_df[train_df['fold'] == 0].reset_index(drop=True)\n\n# Verify the split\nprint(f\"Training data size: {len(train_data)}\")\nprint(f\"Validation data size: {len(val_data)}\")\n","metadata":{"execution":{"iopub.status.busy":"2024-11-20T19:07:44.076712Z","iopub.execute_input":"2024-11-20T19:07:44.077115Z","iopub.status.idle":"2024-11-20T19:07:44.096390Z","shell.execute_reply.started":"2024-11-20T19:07:44.077072Z","shell.execute_reply":"2024-11-20T19:07:44.095628Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **3. Create Custom Dataset**","metadata":{}},{"cell_type":"code","source":"class CloudDataset(Dataset):\n    def __init__(self, df, img_dir, mask_dir=None, transform=None, resize_shape=(416, 608), mode='train'):\n        self.df = df\n        self.img_dir = img_dir\n        self.mask_dir = mask_dir\n        self.transform = transform\n        self.resize_shape = resize_shape\n        self.mode = mode  # 'train', 'val', or 'test'\n        self.labels = ['Fish', 'Flower', 'Gravel', 'Sugar']\n\n    def __len__(self):\n        return len(self.df)\n\n    def __getitem__(self, idx):\n        # Get image ID\n        img_id = self.df.iloc[idx]['ImageId']\n        # Load image\n        img_path = os.path.join(self.img_dir, img_id)\n        image = cv2.imread(img_path)\n        image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)\n\n        if self.mode != 'test':\n            # Create mask\n            mask = np.zeros((1400, 2100, 4), dtype=np.float32)\n            for i, label in enumerate(self.labels):\n                rle = self.df.iloc[idx][label]\n                if rle != '':\n                    mask[:, :, i] = rle2mask(rle, (1400, 2100))\n            # Resize image and mask\n            if self.transform:\n                augmented = self.transform(image=image, mask=mask)\n                image = augmented['image']\n                mask = augmented['mask']\n            else:\n                # Resize\n                image = cv2.resize(image, (self.resize_shape[1], self.resize_shape[0]))\n                mask = cv2.resize(mask, (self.resize_shape[1], self.resize_shape[0]))\n            # Transpose to channel-first\n            image = image.transpose(2, 0, 1).astype(np.float32) / 255.0\n            mask = mask.transpose(2, 0, 1).astype(np.float32)\n            return torch.tensor(image), torch.tensor(mask)\n        else:\n            # For test mode\n            if self.transform:\n                augmented = self.transform(image=image)\n                image = augmented['image']\n            else:\n                image = cv2.resize(image, (self.resize_shape[1], self.resize_shape[0]))\n            image = image.transpose(2, 0, 1).astype(np.float32) / 255.0\n            return torch.tensor(image), img_id\n\ndef rle2mask(rle, shape):\n    '''\n    Convert RLE(run length encoding) string to numpy array\n\n    Parameters:\n    rle (str): Run length encoding string\n    shape (tuple): (height, width) of array to return\n\n    Returns:\n    numpy.array: Mask array\n    '''\n    s = rle.split()\n    starts, lengths = [np.asarray(x, dtype=int) for x in (s[0:][::2], s[1:][::2])]\n    starts -= 1  # Convert to zero-based indexing\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, order='F')  # Reshape to the original shape\n","metadata":{"execution":{"iopub.status.busy":"2024-11-20T19:07:44.097990Z","iopub.execute_input":"2024-11-20T19:07:44.098277Z","iopub.status.idle":"2024-11-20T19:07:44.113546Z","shell.execute_reply.started":"2024-11-20T19:07:44.098249Z","shell.execute_reply":"2024-11-20T19:07:44.112807Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **4. Augmentations**","metadata":{}},{"cell_type":"code","source":"train_transform = Compose([\n    ShiftScaleRotate(scale_limit=0.5, rotate_limit=0, shift_limit=0.1, p=0.6, border_mode=0),\n    OneOf([\n        GridDistortion(p=0.5),\n        OpticalDistortion(p=0.5, distort_limit=0.4, shift_limit=0.5)\n    ], p=0.8),\n    RandomRotate90(p=0.5),\n    Resize(416, 608),\n    VerticalFlip(p=0.5),\n    HorizontalFlip(p=0.5),\n    OneOf([\n         CLAHE(p=0.8),\n        GaussNoise(var_limit=(10.0, 50.0), p=0.5),\n        # GaussianBlur(blur_limit=3, p=0.5),\n        ISONoise(color_shift=(0.01, 0.05), intensity=(0.1, 0.5), p=0.3),\n    ], p=0.8),\n    RandomBrightnessContrast(p=0.8),\n    RandomGamma(p=0.8)\n])\n\nval_transform = Compose([\n    Resize(416, 608),\n])\n","metadata":{"execution":{"iopub.status.busy":"2024-11-20T19:07:44.114514Z","iopub.execute_input":"2024-11-20T19:07:44.114782Z","iopub.status.idle":"2024-11-20T19:07:44.140668Z","shell.execute_reply.started":"2024-11-20T19:07:44.114757Z","shell.execute_reply":"2024-11-20T19:07:44.139889Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **5. Dataloaders**","metadata":{}},{"cell_type":"code","source":"# Paths to your images and masks\ntrain_img_dir = '/kaggle/input/understanding_cloud_organization/train_images'\ntest_img_dir = '/kaggle/input/understanding_cloud_organization/test_images'\n\n# Create datasets\ntrain_dataset = CloudDataset(df=train_data, img_dir=train_img_dir, transform=train_transform)\nval_dataset = CloudDataset(df=val_data, img_dir=train_img_dir, transform=val_transform)\n\n# Create dataloaders\ntrain_loader = DataLoader(train_dataset, batch_size=12, shuffle=True, num_workers=4)\nval_loader = DataLoader(val_dataset, batch_size=12, shuffle=False, num_workers=4)\n\nprint(len(train_loader))\nprint(len(val_loader))","metadata":{"execution":{"iopub.status.busy":"2024-11-20T19:10:35.568137Z","iopub.execute_input":"2024-11-20T19:10:35.568995Z","iopub.status.idle":"2024-11-20T19:10:35.574173Z","shell.execute_reply.started":"2024-11-20T19:10:35.568960Z","shell.execute_reply":"2024-11-20T19:10:35.573288Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **6. Model**","metadata":{}},{"cell_type":"code","source":"# Define the model\nclass UNetPlusPlus(nn.Module):\n    def __init__(self, encoder='timm-efficientnet-b4', num_classes=4):\n        super().__init__()\n        self.model = smp.UnetPlusPlus(\n            encoder_name=encoder,\n            encoder_weights='noisy-student',\n            in_channels=3,\n            classes=num_classes,\n            activation=None\n        )\n        \n    def forward(self, x):\n        return self.model(x)\n","metadata":{"execution":{"iopub.status.busy":"2024-11-20T19:10:37.500009Z","iopub.execute_input":"2024-11-20T19:10:37.500655Z","iopub.status.idle":"2024-11-20T19:10:37.505307Z","shell.execute_reply.started":"2024-11-20T19:10:37.500620Z","shell.execute_reply":"2024-11-20T19:10:37.504516Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **7. Loss Function and Metrics**","metadata":{}},{"cell_type":"code","source":"# Define Dice Loss\nclass DiceLoss(nn.Module):\n    def __init__(self):\n        super(DiceLoss, self).__init__()\n\n    def forward(self, inputs, targets, smooth=1):\n        inputs = torch.sigmoid(inputs)  # Apply sigmoid to get probabilities\n        inputs = inputs.view(-1)\n        targets = targets.view(-1)\n        intersection = (inputs * targets).sum()\n        dice = (2. * intersection + smooth) / (inputs.sum() + targets.sum() + smooth)\n        return 1 - dice\n\n# Define BCE + Dice Loss\nclass BCEDiceLoss(nn.Module):\n    def __init__(self):\n        super(BCEDiceLoss, self).__init__()\n        self.bce = nn.BCEWithLogitsLoss()\n        self.dice = DiceLoss()\n\n    def forward(self, inputs, targets):\n        bce_loss = self.bce(inputs, targets)\n        dice_loss = self.dice(inputs, targets)\n        return bce_loss + dice_loss","metadata":{"execution":{"iopub.status.busy":"2024-11-20T19:10:39.649748Z","iopub.execute_input":"2024-11-20T19:10:39.650562Z","iopub.status.idle":"2024-11-20T19:10:39.656639Z","shell.execute_reply.started":"2024-11-20T19:10:39.650526Z","shell.execute_reply":"2024-11-20T19:10:39.655716Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **8. Training**","metadata":{}},{"cell_type":"markdown","source":"**8.1 Early Stop Definition**","metadata":{}},{"cell_type":"code","source":"class EarlyStopping:\n    def __init__(self, patience=5, verbose=False, delta=0):\n        \"\"\"\n        Early stops the training if validation loss doesn't improve after a given patience.\n        \"\"\"\n        self.patience = patience\n        self.verbose = verbose\n        self.counter = 0\n        self.best_loss = None\n        self.early_stop = False\n        self.delta = delta  # Minimum change to qualify as an improvement\n\n    def __call__(self, val_loss):\n        if self.best_loss is None:\n            self.best_loss = val_loss\n        elif val_loss > self.best_loss - self.delta:\n            self.counter += 1\n            if self.verbose:\n                print(f'EarlyStopping counter: {self.counter} out of {self.patience}')\n            if self.counter >= self.patience:\n                self.early_stop = True\n        else:\n            self.best_loss = val_loss\n            self.counter = 0\n","metadata":{"execution":{"iopub.status.busy":"2024-11-20T19:10:41.514370Z","iopub.execute_input":"2024-11-20T19:10:41.515000Z","iopub.status.idle":"2024-11-20T19:10:41.520616Z","shell.execute_reply.started":"2024-11-20T19:10:41.514967Z","shell.execute_reply":"2024-11-20T19:10:41.519666Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**8.2 Hyperparameters**","metadata":{}},{"cell_type":"code","source":"# Model initialization\nmodel = UNetPlusPlus(encoder='timm-efficientnet-b4', num_classes=4)\n\n# Training parameters\noptimizer = torch.optim.AdamW(model.parameters(), lr=1e-4, weight_decay=1e-4)\nscheduler = torch.optim.lr_scheduler.ReduceLROnPlateau(\n    optimizer, mode='min', factor=0.5, patience=2, verbose=True, min_lr=1e-6)\ncriterion = BCEDiceLoss()\n\nnum_epochs = 32\nbest_loss = np.inf\n\n# Initialize early stopping\nearly_stopping = EarlyStopping(patience=5, verbose=True)","metadata":{"execution":{"iopub.status.busy":"2024-11-20T19:10:44.417226Z","iopub.execute_input":"2024-11-20T19:10:44.417543Z","iopub.status.idle":"2024-11-20T19:10:44.895047Z","shell.execute_reply.started":"2024-11-20T19:10:44.417517Z","shell.execute_reply":"2024-11-20T19:10:44.894105Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**8.3 DOUBLE GPU**","metadata":{}},{"cell_type":"code","source":"# Move the model to the first GPU (as the base device)\nmodel = model.to('cuda')\n\n# Wrap the model in DataParallel to utilize all available GPUs\nif torch.cuda.device_count() > 1:\n    print(f\"Using {torch.cuda.device_count()} GPUs!\")\n    model = nn.DataParallel(model)","metadata":{"execution":{"iopub.status.busy":"2024-11-20T19:10:46.991301Z","iopub.execute_input":"2024-11-20T19:10:46.991647Z","iopub.status.idle":"2024-11-20T19:10:47.047201Z","shell.execute_reply.started":"2024-11-20T19:10:46.991616Z","shell.execute_reply":"2024-11-20T19:10:47.046357Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**8.4 Training Loop**","metadata":{}},{"cell_type":"code","source":"for epoch in range(num_epochs):\n    model.train()\n    train_loss = 0\n    with tqdm(total=len(train_loader), desc=f'Epoch {epoch+1}/{num_epochs}', unit='batch') as pbar:\n        for images, masks in train_loader:\n            images = images.to('cuda')\n            masks = masks.to('cuda')\n            \n            optimizer.zero_grad()\n            outputs = model(images)\n            loss = criterion(outputs, masks)\n            loss.backward()\n            optimizer.step()\n            \n            train_loss += loss.item()\n            pbar.set_postfix({'loss': loss.item()})\n            pbar.update(1)\n            \n    avg_train_loss = train_loss / len(train_loader)\n\n    # Validation\n    model.eval()\n    val_loss = 0\n    with torch.no_grad():\n        for images, masks in val_loader:\n            images = images.to('cuda')\n            masks = masks.to('cuda')\n            outputs = model(images)\n            loss = criterion(outputs, masks)\n            val_loss += loss.item()\n            \n    avg_val_loss = val_loss / len(val_loader)\n\n    # Scheduler step\n    # Scheduler step (based on epoch progress, not validation loss)\n    scheduler.step(epoch + (1 / len(train_loader)))  # Use a fractional step for more precision if needed\n\n    # Evaluate \n    print(f'Epoch [{epoch+1}/{num_epochs}], Train Loss: {avg_train_loss:.4f}, Val Loss: {avg_val_loss:.4f}')\n\n    # Save the model if validation loss has decreased\n    if avg_val_loss < best_loss:\n        best_loss = avg_val_loss\n        torch.save(model.state_dict(), 'best_model2.pth')\n        print('Validation loss decreased. Model saved!')\n\n    # Check early stopping\n    early_stopping(avg_val_loss)\n    if early_stopping.early_stop:\n        print('Early stopping triggered. Stopping training.')\n        break","metadata":{"execution":{"iopub.status.busy":"2024-11-20T19:10:49.098851Z","iopub.execute_input":"2024-11-20T19:10:49.099655Z","iopub.status.idle":"2024-11-20T19:11:06.702657Z","shell.execute_reply.started":"2024-11-20T19:10:49.099621Z","shell.execute_reply":"2024-11-20T19:11:06.701409Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Define the path to your saved model state dictionary\n#model_path = '/kaggle/input/best_model1/pytorch/default/1/best_model.pth'\n\n# Initialize the model with the same architecture and settings\n#model = smp.Unet(\n#    encoder_name=\"efficientnet-b1\",        # Same encoder (ResNet34)\n#    encoder_weights=\"imagenet\",     # Same pre-trained weights\n#    in_channels=3,                  # Input channels (RGB)\n#    classes=4                   \n#)\n\n# Load the model state dictionary safely with `weights_only=True`\n#state_dict = torch.load(model_path, map_location='cpu', weights_only=True)\n#model.load_state_dict(state_dict)\n\n# Move model to GPU if available\n#device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n#model = model.to(device)","metadata":{"execution":{"iopub.status.busy":"2024-11-13T06:40:23.937849Z","iopub.execute_input":"2024-11-13T06:40:23.938524Z","iopub.status.idle":"2024-11-13T06:40:24.969173Z","shell.execute_reply.started":"2024-11-13T06:40:23.938469Z","shell.execute_reply":"2024-11-13T06:40:24.968044Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **9. Thresholds**","metadata":{}},{"cell_type":"markdown","source":"**Understanding Label and Pixel Thresholds**\nBefore we proceed, let's clarify what we mean by label thresholds and pixel thresholds in this context:\n\n**Label Thresholds:** These thresholds are used to decide whether a particular class (cloud type) is present in the image at all. If the maximum probability for a class is below its label threshold, we consider that the class is not present, and we skip generating a mask for it.\n\n**Pixel Thresholds:** These thresholds are used to binarize the predicted probability maps for each class into binary masks. Pixels with probabilities above the pixel threshold are considered part of the mask; otherwise, they're considered background.","metadata":{}},{"cell_type":"code","source":"# Define thresholds\ninitial_label_thresholds = [0.85, 0.92, 0.85, 0.85]\ninitial_pixel_thresholds = [0.21, 0.44, 0.4, 0.3]\n\n# Load the best model\nmodel.load_state_dict(torch.load('best_model2.pth'))\nmodel = model.to('cuda')","metadata":{"execution":{"iopub.status.busy":"2024-11-13T06:40:40.15379Z","iopub.execute_input":"2024-11-13T06:40:40.154189Z","iopub.status.idle":"2024-11-13T06:40:40.159172Z","shell.execute_reply.started":"2024-11-13T06:40:40.15415Z","shell.execute_reply":"2024-11-13T06:40:40.158195Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **10. Define Metrics & Evaluate**","metadata":{}},{"cell_type":"markdown","source":"**10.1 Define Metrics**","metadata":{}},{"cell_type":"code","source":"def sigmoid(x):\n    return 1 / (1 + np.exp(-x))\n\ndef dice_coef_metric(y_pred, y_true, smooth=1):\n    '''\n    Calculate Dice Coefficient.\n\n    Parameters:\n    y_pred (numpy.array): Predicted mask.\n    y_true (numpy.array): True mask.\n\n    Returns:\n    float: Dice coefficient.\n    '''\n    y_pred_f = y_pred.flatten()\n    y_true_f = y_true.flatten()\n    intersection = np.sum(y_pred_f * y_true_f)\n    dice = (2. * intersection + smooth) / (np.sum(y_pred_f) + np.sum(y_true_f) + smooth)\n    return dice\n\ndef iou_metric(y_pred, y_true, smooth=1):\n    '''\n    Calculate Intersection over Union (IoU).\n\n    Parameters:\n    y_pred (numpy.array): Predicted mask.\n    y_true (numpy.array): True mask.\n\n    Returns:\n    float: IoU score.\n    '''\n    y_pred_f = y_pred.flatten()\n    y_true_f = y_true.flatten()\n    intersection = np.sum(y_pred_f * y_true_f)\n    union = np.sum(y_pred_f) + np.sum(y_true_f) - intersection\n    iou = (intersection + smooth) / (union + smooth)\n    return iou","metadata":{"execution":{"iopub.status.busy":"2024-11-13T06:40:43.714119Z","iopub.execute_input":"2024-11-13T06:40:43.714503Z","iopub.status.idle":"2024-11-13T06:40:43.722701Z","shell.execute_reply.started":"2024-11-13T06:40:43.714464Z","shell.execute_reply":"2024-11-13T06:40:43.72157Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**10.2 Evaluate Model**","metadata":{}},{"cell_type":"code","source":"def evaluate(model, loader, label_thresholds, pixel_thresholds):\n    model.eval()\n    num_classes = 4\n    dice_scores = []\n    iou_scores = []\n    \n    with torch.no_grad():\n        for images, masks in loader:\n            images = images.to('cuda')\n            masks = masks.to('cuda')\n            outputs = model(images)\n            outputs = torch.sigmoid(outputs)\n            preds = outputs.cpu().numpy()\n            trues = masks.cpu().numpy()\n            batch_size = preds.shape[0]\n            \n            # Apply thresholds and calculate metrics\n            for i in range(batch_size):\n                pred = preds[i]\n                true = trues[i]\n                dice = []\n                iou = []\n                for ch in range(num_classes):\n                    pred_mask = pred[ch]\n                    true_mask = true[ch]\n                    \n                    # Apply label threshold\n                    max_prob = pred_mask.max()\n                    if max_prob < label_thresholds[ch]:\n                        # If max probability is below label threshold, consider class absent\n                        pred_mask = np.zeros_like(pred_mask)\n                    else:\n                        # Apply pixel threshold\n                        pred_mask = (pred_mask > pixel_thresholds[ch]).astype(np.uint8)\n                    \n                    # Calculate Dice coefficient\n                    intersection = np.logical_and(pred_mask, true_mask).sum()\n                    total = pred_mask.sum() + true_mask.sum()\n                    dice_score = (2 * intersection + 1e-7) / (total + 1e-7)\n                    dice.append(dice_score)\n                    \n                    # Calculate IoU\n                    union = pred_mask.sum() + true_mask.sum() - intersection\n                    iou_score = (intersection + 1e-7) / (union + 1e-7)\n                    iou.append(iou_score)\n                    \n                dice_scores.append(dice)\n                iou_scores.append(iou)\n                \n    # Convert lists to numpy arrays\n    dice_scores = np.array(dice_scores)\n    iou_scores = np.array(iou_scores)\n    \n    # Calculate mean scores per class\n    mean_dice_per_class = np.mean(dice_scores, axis=0)\n    mean_iou_per_class = np.mean(iou_scores, axis=0)\n    \n    # Calculate overall mean scores\n    mean_dice = np.mean(mean_dice_per_class)\n    mean_iou = np.mean(mean_iou_per_class)\n    \n    return mean_dice_per_class, mean_dice, mean_iou_per_class, mean_iou\n","metadata":{"execution":{"iopub.status.busy":"2024-11-13T06:43:35.09752Z","iopub.execute_input":"2024-11-13T06:43:35.097961Z","iopub.status.idle":"2024-11-13T06:43:35.113458Z","shell.execute_reply.started":"2024-11-13T06:43:35.097919Z","shell.execute_reply":"2024-11-13T06:43:35.112622Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Evaluate the model before threshold optimization\nprint(\"Evaluating model before threshold optimization on full validation set...\")\nmean_dice_per_class, mean_dice, mean_iou_per_class, mean_iou = evaluate(\n    model, val_loader, initial_label_thresholds, initial_pixel_thresholds\n)\n\n# Define class labels\nlabels = ['Fish', 'Flower', 'Gravel', 'Sugar']\n\n# Print per-class Dice and IoU\nfor idx, label in enumerate(labels):\n    print(f'{label} - Dice: {mean_dice_per_class[idx]:.4f}, IoU: {mean_iou_per_class[idx]:.4f}')\n\n# Print overall mean Dice and IoU\nprint(f'Overall Mean Dice: {mean_dice:.4f}')\nprint(f'Overall Mean IoU: {mean_iou:.4f}')","metadata":{"execution":{"iopub.status.busy":"2024-11-13T06:43:38.621143Z","iopub.execute_input":"2024-11-13T06:43:38.621525Z","iopub.status.idle":"2024-11-13T06:44:05.785365Z","shell.execute_reply.started":"2024-11-13T06:43:38.621486Z","shell.execute_reply":"2024-11-13T06:44:05.784162Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **11. Threshold Optimization**","metadata":{}},{"cell_type":"markdown","source":"**note: this takes too long :D**","metadata":{}},{"cell_type":"code","source":"def optimize_thresholds(model, loader, initial_label_thresholds, initial_pixel_thresholds):\n    best_label_thresholds = initial_label_thresholds.copy()\n    best_pixel_thresholds = initial_pixel_thresholds.copy()\n    best_dice_scores = np.zeros(4)\n    best_iou_scores = np.zeros(4)\n    \n    # Define threshold ranges\n    label_threshold_ranges = [np.linspace(0.5, 0.99, 10) for _ in range(4)]\n    pixel_threshold_ranges = [np.linspace(0.1, 0.9, 9) for _ in range(4)]\n    \n    # Optimize label thresholds\n    print(\"Optimizing label thresholds...\")\n    for idx in range(4):\n        best_dice_score = 0\n        best_iou_score = 0\n        for t in label_threshold_ranges[idx]:\n            temp_label_thresholds = best_label_thresholds.copy()\n            temp_label_thresholds[idx] = t\n            mean_dice_per_class, mean_dice, mean_iou_per_class, mean_iou = evaluate(\n                model, loader, temp_label_thresholds, best_pixel_thresholds\n            )\n            # Update if better Dice score is achieved\n            if mean_dice > best_dice_score:\n                best_dice_score = mean_dice\n                best_iou_score = mean_iou\n                best_label_thresholds[idx] = t\n                best_dice_scores[idx] = mean_dice\n                best_iou_scores[idx] = mean_iou\n        print(f'Best label threshold for class {idx}: {best_label_thresholds[idx]}, '\n              f'Dice: {best_dice_scores[idx]:.4f}, IoU: {best_iou_scores[idx]:.4f}')\n    \n    # Optimize pixel thresholds\n    print(\"Optimizing pixel thresholds...\")\n    for idx in range(4):\n        best_dice_score = 0\n        best_iou_score = 0\n        for t in pixel_threshold_ranges[idx]:\n            temp_pixel_thresholds = best_pixel_thresholds.copy()\n            temp_pixel_thresholds[idx] = t\n            mean_dice_per_class, mean_dice, mean_iou_per_class, mean_iou = evaluate(\n                model, loader, best_label_thresholds, temp_pixel_thresholds\n            )\n            # Update if better Dice score is achieved\n            if mean_dice > best_dice_score:\n                best_dice_score = mean_dice\n                best_iou_score = mean_iou\n                best_pixel_thresholds[idx] = t\n                best_dice_scores[idx] = mean_dice\n                best_iou_scores[idx] = mean_iou\n        print(f'Best pixel threshold for class {idx}: {best_pixel_thresholds[idx]}, '\n              f'Dice: {best_dice_scores[idx]:.4f}, IoU: {best_iou_scores[idx]:.4f}')\n    \n    return best_label_thresholds, best_pixel_thresholds\n\n# Optimize thresholds on the full validation set\nbest_label_thresholds, best_pixel_thresholds = optimize_thresholds(\n    model, val_loader, initial_label_thresholds, initial_pixel_thresholds\n)\n\nprint('Optimized label thresholds:', best_label_thresholds)\nprint('Optimized pixel thresholds:', best_pixel_thresholds)\n","metadata":{"execution":{"iopub.status.busy":"2024-11-13T06:47:23.872628Z","iopub.execute_input":"2024-11-13T06:47:23.873331Z","iopub.status.idle":"2024-11-13T06:57:02.071589Z","shell.execute_reply.started":"2024-11-13T06:47:23.873288Z","shell.execute_reply":"2024-11-13T06:57:02.070148Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **12. Submission**","metadata":{}},{"cell_type":"code","source":"def mask2rle(mask):\n    '''\n    Convert mask to RLE.\n    mask: numpy array, 1 - mask, 0 - background\n    Returns run length as string formated\n    '''\n    pixels = mask.flatten(order='F')\n    pixels = np.concatenate([[0], pixels, [0]])\n    runs = np.where(pixels[1:] != pixels[:-1])[0] + 1\n    runs[1::2] -= runs[::2]\n    rle = ' '.join(str(x) for x in runs)\n    return rle\n\ndef create_submission(model, label_thresholds, pixel_thresholds, submission_file='submission.csv'):\n    test_images = os.listdir(test_img_dir)\n    results = []\n    model.eval()\n    with torch.no_grad():\n        for img_name in tqdm(test_images):\n            # Load image\n            img_path = os.path.join(test_img_dir, img_name)\n            image = cv2.imread(img_path)\n            image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)\n            # Resize\n            image = cv2.resize(image, (608, 416))\n            image = image.transpose(2, 0, 1).astype(np.float32) / 255.0\n            image = torch.tensor(image).unsqueeze(0).to('cuda')\n            # Predict\n            output = model(image)\n            output = torch.sigmoid(output).cpu().numpy()[0]\n            # Initialize empty list for masks\n            masks = []\n            labels = ['Fish', 'Flower', 'Gravel', 'Sugar']\n            for ch in range(4):\n                pred_mask = output[ch]\n                # Apply label threshold\n                max_prob = pred_mask.max()\n                if max_prob < label_thresholds[ch]:\n                    # If max probability is below label threshold, consider class absent\n                    pred_mask = np.zeros_like(pred_mask)\n                else:\n                    # Apply pixel threshold\n                    pred_mask = (pred_mask > pixel_thresholds[ch]).astype(np.uint8)\n                # Resize to 350x525\n                pred_mask = cv2.resize(pred_mask, (525, 350))\n                masks.append(pred_mask)\n            # Convert masks to RLE\n            for i, label in enumerate(labels):\n                pred_mask = masks[i]\n                if pred_mask.sum() == 0:\n                    rle = ''\n                else:\n                    rle = mask2rle(pred_mask)\n                results.append({'Image_Label': f'{img_name}_{label}', 'EncodedPixels': rle})\n    submission_df = pd.DataFrame(results)\n    # Fill missing EncodedPixels with empty strings\n    submission_df['EncodedPixels'] = submission_df['EncodedPixels'].fillna('')\n    submission_df.to_csv(submission_file, index=False)\n    print('Submission file created!')\n","metadata":{"execution":{"iopub.status.busy":"2024-11-13T06:57:06.467707Z","iopub.execute_input":"2024-11-13T06:57:06.468182Z","iopub.status.idle":"2024-11-13T06:57:06.48653Z","shell.execute_reply.started":"2024-11-13T06:57:06.46813Z","shell.execute_reply":"2024-11-13T06:57:06.485357Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Create submission\ncreate_submission(model, initial_label_thresholds, initial_pixel_thresholds, submission_file='submission1.csv')","metadata":{"execution":{"iopub.status.busy":"2024-11-13T06:57:08.675594Z","iopub.execute_input":"2024-11-13T06:57:08.676286Z","iopub.status.idle":"2024-11-13T07:01:34.272828Z","shell.execute_reply.started":"2024-11-13T06:57:08.676245Z","shell.execute_reply":"2024-11-13T07:01:34.271781Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sub = pd.read_csv('/kaggle/working/submission1.csv')\nprint(sub)","metadata":{"execution":{"iopub.status.busy":"2024-11-13T07:03:17.814847Z","iopub.execute_input":"2024-11-13T07:03:17.815266Z","iopub.status.idle":"2024-11-13T07:03:18.393812Z","shell.execute_reply.started":"2024-11-13T07:03:17.815227Z","shell.execute_reply":"2024-11-13T07:03:18.392864Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Create submission\ncreate_submission(model, best_label_thresholds, best_pixel_thresholds, submission_file='submission2.csv')","metadata":{},"execution_count":null,"outputs":[]}]}