{"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":"none","dataSources":[{"sourceId":19989,"databundleVersionId":1160143,"sourceType":"competition"},{"sourceId":1227211,"sourceType":"datasetVersion","datasetId":637073}],"dockerImageVersionId":30786,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"!pip install -U albumentations\n!pip install -U lightning\n!pip install -U optuna optuna-integration","metadata":{"execution":{"iopub.status.busy":"2024-10-30T15:26:06.751386Z","iopub.execute_input":"2024-10-30T15:26:06.752314Z","iopub.status.idle":"2024-10-30T15:26:47.300801Z","shell.execute_reply.started":"2024-10-30T15:26:06.752244Z","shell.execute_reply":"2024-10-30T15:26:47.299211Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import os\nimport optuna\nimport torch\nimport torch.optim as optim\nfrom torchvision import models, transforms\nfrom torch.utils.data import Dataset, DataLoader\nfrom optuna.integration import PyTorchLightningPruningCallback\nimport lightning.pytorch as pl\nfrom PIL import Image\nimport json\nfrom torchvision.ops import box_convert\nimport albumentations as A\nfrom albumentations.pytorch import ToTensorV2\nimport numpy as np","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2024-10-30T15:26:47.303969Z","iopub.execute_input":"2024-10-30T15:26:47.304533Z","iopub.status.idle":"2024-10-30T15:26:47.312504Z","shell.execute_reply.started":"2024-10-30T15:26:47.304476Z","shell.execute_reply":"2024-10-30T15:26:47.311167Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"DATA_DIR = \"/kaggle/input/global-wheat-detection\"\nTRAIN_IMG_DIR = os.path.join(DATA_DIR, \"train\")        # Directory containing training images\nTRAIN_ANNOTATIONS = os.path.join(DATA_DIR, \"train.csv\")  # Path to the CSV file with annotations","metadata":{"execution":{"iopub.status.busy":"2024-10-30T15:26:47.313822Z","iopub.execute_input":"2024-10-30T15:26:47.314171Z","iopub.status.idle":"2024-10-30T15:26:47.323641Z","shell.execute_reply.started":"2024-10-30T15:26:47.314133Z","shell.execute_reply":"2024-10-30T15:26:47.322599Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import pandas as pd\nimport torch\nfrom torch.utils.data import Dataset\nfrom torchvision.ops import box_convert\nfrom PIL import Image\nimport albumentations as A\nimport numpy as np\nfrom albumentations.pytorch import ToTensorV2\n\nclass WheatDataset(Dataset):\n    def __init__(self, img_dir, annotations_file, transforms=None):\n        self.img_dir = img_dir\n        self.transforms = transforms\n        self.df = pd.read_csv(annotations_file)\n        self.imgs = self.df['image_id'].unique()\n\n    def __len__(self):\n        return len(self.imgs)\n\n    def __getitem__(self, idx):\n        image_id = self.imgs[idx]\n        img_path = os.path.join(self.img_dir, f\"{image_id}.jpg\")\n        img = Image.open(img_path).convert(\"RGB\")\n\n        # Get annotations for the image\n        records = self.df[self.df['image_id'] == image_id]\n        boxes = []\n        for _, row in records.iterrows():\n            bbox = [float(x) for x in row['bbox'][1:-1].split(\",\")]\n            boxes.append(bbox)\n        \n        # Convert boxes to the correct format and labels to 1 (wheat)\n        target = {\n            \"boxes\": torch.tensor(boxes, dtype=torch.float32),\n            \"labels\": torch.ones((len(boxes),), dtype=torch.int64)\n        }\n        target[\"boxes\"] = box_convert(target[\"boxes\"], in_fmt=\"xywh\", out_fmt=\"xyxy\")\n\n        if self.transforms:\n            augmented = self.transforms(image=np.array(img))\n            img = augmented['image']\n        \n        return img, target","metadata":{"execution":{"iopub.status.busy":"2024-10-30T15:26:47.324897Z","iopub.execute_input":"2024-10-30T15:26:47.325359Z","iopub.status.idle":"2024-10-30T15:26:47.340005Z","shell.execute_reply.started":"2024-10-30T15:26:47.325307Z","shell.execute_reply":"2024-10-30T15:26:47.338856Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"data_transforms = A.Compose([\n    A.RandomResizedCrop(height=800, width=800, scale=(0.5, 1.0), ratio=(0.75, 1.33), p=0.8),\n    A.HorizontalFlip(p=0.5),\n    A.VerticalFlip(p=0.5),\n    A.RandomBrightnessContrast(brightness_limit=0.3, contrast_limit=0.3, p=0.5),\n    A.HueSaturationValue(hue_shift_limit=30, sat_shift_limit=40, val_shift_limit=30, p=0.5),\n    A.GaussianBlur(blur_limit=(3, 7), p=0.3),\n    A.Normalize(mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225)),\n    ToTensorV2()\n])\n","metadata":{"execution":{"iopub.status.busy":"2024-10-30T15:26:47.343217Z","iopub.execute_input":"2024-10-30T15:26:47.343738Z","iopub.status.idle":"2024-10-30T15:26:47.355683Z","shell.execute_reply.started":"2024-10-30T15:26:47.343696Z","shell.execute_reply":"2024-10-30T15:26:47.354618Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from torch.utils.data import DataLoader\nimport lightning.pytorch as pl\nfrom sklearn.model_selection import train_test_split\n\nclass WheatDataModule(pl.LightningDataModule):\n    def __init__(self, img_dir, annotations_file, batch_size=2, num_workers=2):\n        super().__init__()\n        self.img_dir = img_dir\n        self.annotations_file = annotations_file\n        self.batch_size = batch_size\n        self.num_workers = num_workers\n\n        # Split train-validation\n        self.df = pd.read_csv(annotations_file)\n        train_imgs, val_imgs = train_test_split(self.df['image_id'].unique(), test_size=0.2, random_state=42)\n        self.train_df = self.df[self.df['image_id'].isin(train_imgs)]\n        self.val_df = self.df[self.df['image_id'].isin(val_imgs)]\n\n    def train_dataloader(self):\n        train_dataset = WheatDataset(self.img_dir, self.annotations_file, transforms=data_transforms)\n        return DataLoader(\n            train_dataset,\n            batch_size=self.batch_size,\n            shuffle=True,\n            num_workers=self.num_workers,\n            collate_fn=self.collate_fn\n        )\n\n    def val_dataloader(self):\n        val_dataset = WheatDataset(self.img_dir, self.annotations_file, transforms=data_transforms)\n        return DataLoader(\n            val_dataset,\n            batch_size=self.batch_size,\n            shuffle=False,\n            num_workers=self.num_workers,\n            collate_fn=self.collate_fn\n        )\n\n    @staticmethod\n    def collate_fn(batch):\n        batch = [b for b in batch if b is not None]\n        images, targets = zip(*batch)\n        return list(images), list(targets)\n","metadata":{"execution":{"iopub.status.busy":"2024-10-30T15:26:47.356964Z","iopub.execute_input":"2024-10-30T15:26:47.357364Z","iopub.status.idle":"2024-10-30T15:26:47.369247Z","shell.execute_reply.started":"2024-10-30T15:26:47.357323Z","shell.execute_reply":"2024-10-30T15:26:47.368222Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Step 1: Define the Model Class\nclass FasterRCNNModel(pl.LightningModule):\n    def __init__(self, lr=0.001, weight_decay=0.0005, momentum=0.9):\n        super().__init__()\n        self.model = models.detection.fasterrcnn_resnet50_fpn(weights=\"DEFAULT\")\n        in_features = self.model.roi_heads.box_predictor.cls_score.in_features\n        self.model.roi_heads.box_predictor = models.detection.faster_rcnn.FastRCNNPredictor(in_features, 2)\n        self.lr = lr\n        self.weight_decay = weight_decay\n        self.momentum = momentum\n\n    def forward(self, images, targets=None):\n        return self.model(images, targets)\n\n    def training_step(self, batch, batch_idx):\n        images, targets = batch\n        loss_dict = self.model(images, targets)\n        loss = sum(loss for loss in loss_dict.values())\n        self.log('train_loss', loss)\n        return loss\n\n    def validation_step(self, batch, batch_idx):\n        images, targets = batch\n        self.model.train()\n        with torch.set_grad_enabled(True):\n            loss_dict = self.model(images, targets)\n        if isinstance(loss_dict, dict):\n            val_loss = sum(loss for loss in loss_dict.values())\n            self.log('val_loss', val_loss)\n            return val_loss\n        else:\n            raise ValueError(\"Expected loss_dict to be a dictionary during validation.\")\n\n    def configure_optimizers(self):\n        return optim.SGD(self.parameters(), lr=self.lr, momentum=self.momentum, weight_decay=self.weight_decay)\n\n# Step 2: Define the Objective Function for Optuna Optimization\ndef objective(trial):\n    # Use suggest_float instead of deprecated suggest_loguniform/suggest_uniform\n    lr = trial.suggest_float(\"lr\", 1e-5, 1e-3, log=True)\n    batch_size = trial.suggest_categorical(\"batch_size\", [2, 4, 8])\n    weight_decay = trial.suggest_float(\"weight_decay\", 1e-6, 1e-3, log=True)\n    momentum = trial.suggest_float(\"momentum\", 0.8, 0.99)\n\n    # Instantiate DataModule and Model with trial parameters\n    data_module = WheatDataModule(\n        img_dir=TRAIN_IMG_DIR,\n        annotations_file=TRAIN_ANNOTATIONS,\n        batch_size=batch_size\n    )\n    model = FasterRCNNModel(lr=lr, weight_decay=weight_decay, momentum=momentum)\n\n    # Trainer setup with Optuna Pruning\n    trainer = pl.Trainer(\n        max_epochs=1,  # Set to 1 for quick testing, increase for full training\n        accelerator='auto',\n        precision=16,\n        log_every_n_steps=10,\n        callbacks=[PyTorchLightningPruningCallback(trial, monitor=\"val_loss\")]\n    )\n\n    # Run training\n    trainer.fit(model, data_module)\n    val_loss = trainer.callback_metrics.get(\"val_loss\")\n    \n    if val_loss is not None:\n        return val_loss.item()\n    else:\n        raise optuna.exceptions.TrialPruned(\"No val_loss available\")","metadata":{"execution":{"iopub.status.busy":"2024-10-30T15:26:47.37054Z","iopub.execute_input":"2024-10-30T15:26:47.370858Z","iopub.status.idle":"2024-10-30T15:26:47.387282Z","shell.execute_reply.started":"2024-10-30T15:26:47.37082Z","shell.execute_reply":"2024-10-30T15:26:47.386187Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import optuna\nimport torch.optim as optim\nimport lightning.pytorch as pl\nfrom optuna.integration import PyTorchLightningPruningCallback\nfrom lightning.pytorch.callbacks import ModelCheckpoint\n\n# Step 1: Define the Model Class\nclass FasterRCNNModel(pl.LightningModule):\n    def __init__(self, lr=0.001, weight_decay=0.0005, momentum=0.9):\n        super().__init__()\n        self.model = models.detection.fasterrcnn_resnet50_fpn(weights=\"DEFAULT\")\n        in_features = self.model.roi_heads.box_predictor.cls_score.in_features\n        self.model.roi_heads.box_predictor = models.detection.faster_rcnn.FastRCNNPredictor(in_features, 2)\n        self.lr = lr\n        self.weight_decay = weight_decay\n        self.momentum = momentum\n\n    def forward(self, images, targets=None):\n        return self.model(images, targets)\n\n    def training_step(self, batch, batch_idx):\n        images, targets = batch\n        loss_dict = self.model(images, targets)\n        loss = sum(loss for loss in loss_dict.values())\n        self.log('train_loss', loss)\n        return loss\n\n    def validation_step(self, batch, batch_idx):\n        images, targets = batch\n        self.model.train()\n        with torch.set_grad_enabled(True):\n            loss_dict = self.model(images, targets)\n        if isinstance(loss_dict, dict):\n            val_loss = sum(loss for loss in loss_dict.values())\n            self.log('val_loss', val_loss)\n            return val_loss\n        else:\n            raise ValueError(\"Expected loss_dict to be a dictionary during validation.\")\n\n    def configure_optimizers(self):\n        return optim.SGD(self.parameters(), lr=self.lr, momentum=self.momentum, weight_decay=self.weight_decay)\n\n# Step 2: Define the Objective Function for Optuna Optimization\ndef objective(trial):\n    # Use suggest_float instead of deprecated suggest_loguniform/suggest_uniform\n    lr = trial.suggest_float(\"lr\", 1e-5, 1e-3, log=True)\n    batch_size = trial.suggest_categorical(\"batch_size\", [2, 4, 8])\n    weight_decay = trial.suggest_float(\"weight_decay\", 1e-6, 1e-3, log=True)\n    momentum = trial.suggest_float(\"momentum\", 0.8, 0.99)\n\n    # Instantiate DataModule and Model with trial parameters\n    data_module = WheatDataModule(\n        img_dir=TRAIN_IMG_DIR,\n        annotations_file=TRAIN_ANNOTATIONS,\n        batch_size=batch_size\n    )\n    model = FasterRCNNModel(lr=lr, weight_decay=weight_decay, momentum=momentum)\n\n    # Set up ModelCheckpoint to save the best model\n    checkpoint_callback = ModelCheckpoint(\n        monitor=\"val_loss\",\n        dirpath=\"/kaggle/working\",\n        filename=\"best_model_trial_{}\".format(trial.number),\n        save_top_k=1,\n        mode=\"min\"\n    )\n\n    # Trainer setup with Optuna Pruning and ModelCheckpoint\n    trainer = pl.Trainer(\n        max_epochs=1,  # Set to 1 for quick testing, increase for full training\n        accelerator='auto',\n        precision= 'bf16-mixed',\n        log_every_n_steps=10,\n        callbacks=[checkpoint_callback, PyTorchLightningPruningCallback(trial, monitor=\"val_loss\")]\n    )\n\n    # Run training\n    trainer.fit(model, data_module)\n    val_loss = trainer.callback_metrics.get(\"val_loss\")\n\n    # Return validation loss and checkpoint path\n    if val_loss is not None:\n        trial.set_user_attr(\"best_model_path\", checkpoint_callback.best_model_path)\n        return val_loss.item()\n    else:\n        raise optuna.exceptions.TrialPruned(\"No val_loss available\")\n\n# Step 3: Run Optuna Study for Hyperparameter Optimization and Save Results\nif __name__ == \"__main__\":\n    pruner = optuna.pruners.MedianPruner()\n    study = optuna.create_study(direction=\"minimize\", pruner=pruner)\n    study.optimize(objective, n_trials=1, timeout=120)  # Adjust as needed\n\n    # Display best trial results\n    print(\"Number of finished trials: \", len(study.trials))\n    print(\"Best trial:\")\n    trial = study.best_trial\n    print(f\"  Value (Lowest val_loss): {trial.value}\")\n    print(\"  Params:\")\n    for key, value in trial.params.items():\n        print(f\"    {key}: {value}\")\n\n    # Retrieve and display the path of the best model checkpoint\n    best_model_path = trial.user_attrs[\"best_model_path\"]\n    print(\"Best model checkpoint path:\", best_model_path)\n","metadata":{"execution":{"iopub.status.busy":"2024-10-30T15:26:47.388471Z","iopub.execute_input":"2024-10-30T15:26:47.389606Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import torch\nfrom PIL import Image\nimport numpy as np\nimport matplotlib.pyplot as plt\nfrom albumentations.pytorch import ToTensorV2\nimport albumentations as A\nfrom torchvision.ops import box_convert\nfrom lightning.pytorch import LightningModule\nfrom torchvision import models\n\n# Define data transforms to match training pre-processing\ntest_transforms = A.Compose([\n    A.Resize(1024, 1024),\n    A.Normalize(mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225)),\n    ToTensorV2()\n])\n\n# Load the trained model class\nclass FasterRCNNModel(LightningModule):\n    def __init__(self, lr=0.001, weight_decay=0.0005, momentum=0.9):\n        super().__init__()\n        self.model = models.detection.fasterrcnn_resnet50_fpn(weights=\"DEFAULT\")\n        in_features = self.model.roi_heads.box_predictor.cls_score.in_features\n        self.model.roi_heads.box_predictor = models.detection.faster_rcnn.FastRCNNPredictor(in_features, 2)\n        self.lr = lr\n        self.weight_decay = weight_decay\n        self.momentum = momentum\n\n    def forward(self, images, targets=None):\n        return self.model(images, targets)\n\n# Load the model from checkpoint\nmodel_path = \"/kaggle/working/best_model_trial_0.ckpt\"\nmodel = FasterRCNNModel.load_from_checkpoint(model_path)\nmodel.eval()\n\n# Define the test image path\ntest_image_path = \"/kaggle/input/global-wheat-challenge/test/test/01f49b49d6eac848581312afdd32bd543b5296cbd1ad62d30421dd5502703d65.png\"\n\n# Load and preprocess the test image\nimg = Image.open(test_image_path).convert(\"RGB\")\nimg_transformed = test_transforms(image=np.array(img))['image']\nimg_transformed = img_transformed.unsqueeze(0)  # Add batch dimension\n\n# Run inference\nwith torch.no_grad():\n    predictions = model(img_transformed)\n\n# Plot predictions with bounding boxes\ndef plot_predictions(img_path, predictions, threshold=0.5):\n    img = Image.open(img_path)\n    plt.imshow(img)\n    ax = plt.gca()\n    \n    for box, score in zip(predictions[0]['boxes'], predictions[0]['scores']):\n        if score > threshold:\n            xmin, ymin, xmax, ymax = box.cpu().numpy()\n            width, height = xmax - xmin, ymax - ymin\n            rect = plt.Rectangle((xmin, ymin), width, height, fill=False, color='red', linewidth=2)\n            ax.add_patch(rect)\n            ax.text(xmin, ymin, f\"{score:.2f}\", color=\"blue\", fontsize=10, weight=\"bold\")\n    \n    plt.axis('off')\n    plt.show()\n\nplot_predictions(test_image_path, predictions, threshold=0.5)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import os\n\n# Check main dataset directory\nprint(\"Files in dataset directory:\", os.listdir(DATA_DIR))\n\n# Check for the train image directory and list sample files\nif os.path.exists(TRAIN_IMG_DIR):\n    print(\"\\nFiles in train image directory:\", os.listdir(TRAIN_IMG_DIR)[:5])  # Display first 5 files\nelse:\n    print(\"\\nTrain image directory not found.\")\n\n# Check specifically for the train.csv file\nprint(\"\\nTrain.csv found:\", os.path.exists(TRAIN_ANNOTATIONS))","metadata":{"trusted":true},"execution_count":null,"outputs":[]}]}