{"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":185469,"sourceType":"datasetVersion","datasetId":79726},{"sourceId":1227211,"sourceType":"datasetVersion","datasetId":637073},{"sourceId":1364744,"sourceType":"datasetVersion","datasetId":750746},{"sourceId":3916507,"sourceType":"datasetVersion","datasetId":2325960}],"dockerImageVersionId":30788,"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-30T16:21:33.279253Z","iopub.execute_input":"2024-10-30T16:21:33.279720Z","iopub.status.idle":"2024-10-30T16:22:10.234261Z","shell.execute_reply.started":"2024-10-30T16:21:33.279678Z","shell.execute_reply":"2024-10-30T16:22:10.232971Z"},"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, this ","metadata":{"execution":{"iopub.status.busy":"2024-10-30T16:22:10.236769Z","iopub.execute_input":"2024-10-30T16:22:10.237131Z","iopub.status.idle":"2024-10-30T16:22:10.242658Z","shell.execute_reply.started":"2024-10-30T16:22:10.237093Z","shell.execute_reply":"2024-10-30T16:22:10.241712Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#Model 1\nimport 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 pandas as pd\nimport numpy as np\nfrom torchvision.ops import box_convert\nimport albumentations as A\nfrom albumentations.pytorch import ToTensorV2\nfrom torch.optim.lr_scheduler import StepLR\nfrom lightning.pytorch.callbacks import ModelCheckpoint\nfrom sklearn.model_selection import train_test_split\n\n# Dataset setup\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        records = self.df[self.df['image_id'] == image_id]\n        boxes = [list(map(float, row['bbox'][1:-1].split(\",\"))) for _, row in records.iterrows()]\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\n\n# Basic augmentations\ndata_transforms = A.Compose([\n    A.Resize(1024, 1024),\n    A.HorizontalFlip(p=0.5),\n    A.VerticalFlip(p=0.5),\n    A.Normalize(mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225)),\n    ToTensorV2()\n])\n\nclass WheatDataModule(pl.LightningDataModule):\n    def __init__(self, img_dir, annotations_file, batch_size=4, 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        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(train_dataset, batch_size=self.batch_size, shuffle=True, num_workers=self.num_workers, collate_fn=self.collate_fn)\n\n    def val_dataloader(self):\n        val_dataset = WheatDataset(self.img_dir, self.annotations_file, transforms=data_transforms)\n        return DataLoader(val_dataset, batch_size=self.batch_size, shuffle=False, num_workers=self.num_workers, collate_fn=self.collate_fn)\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\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(pretrained = True)\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        # Ensure the model is in training mode to get loss values\n        self.model.train()\n        with torch.set_grad_enabled(True):\n            loss_dict = self.model(images, targets)\n        \n        # Check if `loss_dict` is actually a dictionary\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            # Handle the case where `loss_dict` is a list (in evaluation mode)\n            val_loss = torch.tensor(0.0, device=self.device)  # Default to zero loss if needed\n            self.log('val_loss', val_loss)\n            return val_loss\n\n    def configure_optimizers(self):\n        optimizer = optim.SGD(self.parameters(), lr=self.lr, momentum=self.momentum, weight_decay=self.weight_decay)\n        scheduler = StepLR(optimizer, step_size=2, gamma=0.5)\n        return [optimizer], [scheduler]\n\ndef objective(trial):\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    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    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 = pl.Trainer(\n        max_epochs=3,  # Set to 5 epochs\n        accelerator='auto',\n        precision=16,\n        log_every_n_steps=10,\n        callbacks=[checkpoint_callback, PyTorchLightningPruningCallback(trial, monitor=\"val_loss\")]\n    )\n\n    trainer.fit(model, data_module)\n    val_loss = trainer.callback_metrics.get(\"val_loss\")\n    \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\nif __name__ == \"__main__\":\n    pruner = optuna.pruners.MedianPruner()\n    study = optuna.create_study(direction=\"minimize\", pruner=pruner)\n    study.optimize(objective, n_trials=3, timeout=60)  # Reduced trials to 5 for quicker optimization\n\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    best_model_path = trial.user_attrs[\"best_model_path\"]\n    print(\"Best model checkpoint path:\", best_model_path)","metadata":{"execution":{"iopub.status.busy":"2024-10-30T16:22:10.244294Z","iopub.execute_input":"2024-10-30T16:22:10.244665Z","iopub.status.idle":"2024-10-30T16:57:39.571839Z","shell.execute_reply.started":"2024-10-30T16:22:10.244629Z","shell.execute_reply":"2024-10-30T16:57:39.570821Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import torch\nimport torchvision\nimport pytorch_lightning as pl\nfrom pytorch_lightning import LightningModule\nfrom torchvision import transforms\nfrom PIL import Image\nimport matplotlib.pyplot as plt\nimport cv2\n\n# Step 1: Define the Model Class with Box Predictor Adjustment\nfrom torchvision.models.detection.faster_rcnn import FastRCNNPredictor\n\nclass WheatDetectionModel(LightningModule):\n    def __init__(self, num_classes=2, lr=1e-3):\n        super(WheatDetectionModel, self).__init__()\n        self.model = torchvision.models.detection.fasterrcnn_resnet50_fpn(pretrained=True)\n        \n        # Adjust the box predictor to match the number of classes\n        in_features = self.model.roi_heads.box_predictor.cls_score.in_features\n        self.model.roi_heads.box_predictor = FastRCNNPredictor(in_features, num_classes)\n        \n        self.lr = lr\n\n    def forward(self, images):\n        return self.model(images)\n\n# Step 2: Load the Model from Checkpoint with `strict=False`\ndef load_model_from_checkpoint(checkpoint_path):\n    model = WheatDetectionModel(num_classes=2)\n    checkpoint = torch.load(checkpoint_path, map_location=torch.device('cpu'))\n    model.load_state_dict(checkpoint['state_dict'], strict=False)\n    model.eval()\n    model.to(torch.device('cpu'))\n    return model\n\n# Step 3: Define Function to Load and Transform the Test Image\ndef load_image(image_path):\n    # Load and preprocess the image\n    image = Image.open(image_path).convert(\"RGB\")\n    transform = transforms.ToTensor()\n    image = transform(image)\n    return image.unsqueeze(0)  # Add batch dimension\n\n# Step 4: Run Prediction and Display Results\ndef test_single_image(model, image_tensor):\n    with torch.no_grad():\n        predictions = model(image_tensor)\n    \n    # Display the image and predictions\n    image_np = image_tensor[0].permute(1, 2, 0).numpy()\n    fig, ax = plt.subplots(1, 1, figsize=(12, 8))\n    ax.imshow(image_np)\n    \n    for i, box in enumerate(predictions[0]['boxes']):\n        score = predictions[0]['scores'][i].item()\n        if score > 0.4:  # Adjust threshold as necessary\n            x_min, y_min, x_max, y_max = box\n            rect = plt.Rectangle((x_min, y_min), x_max - x_min, y_max - y_min,\n                                 fill=False, color=\"red\", linewidth=2)\n            ax.add_patch(rect)\n            ax.text(x_min, y_min, f\"{score:.2f}\", color=\"red\", fontsize=12)\n    \n    plt.show()\n\n# Step 5: Main Execution\nif __name__ == \"__main__\":\n    # Paths to the checkpoint and test image\n    checkpoint_path = \"/kaggle/working/best_model_trial_0-v2.ckpt\"\n    test_image_path = \"/kaggle/input/global-wheat-detection/test/53f253011.jpg\"\n    \n    # Load the model from checkpoint\n    model = load_model_from_checkpoint(checkpoint_path)\n    \n    # Load and preprocess the test image\n    image_tensor = load_image(test_image_path)\n    \n    # Run the model on the test image and display results\n    test_single_image(model, image_tensor)\n","metadata":{"execution":{"iopub.status.busy":"2024-10-31T04:54:29.618262Z","iopub.execute_input":"2024-10-31T04:54:29.619049Z","iopub.status.idle":"2024-10-31T04:54:40.411685Z","shell.execute_reply.started":"2024-10-31T04:54:29.619011Z","shell.execute_reply":"2024-10-31T04:54:40.410175Z"},"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\nfrom torch.utils.data import Dataset, DataLoader\nfrom optuna.integration import PyTorchLightningPruningCallback\nimport lightning.pytorch as pl\nfrom PIL import Image\nimport pandas as pd\nimport numpy as np\nfrom torchvision.ops import box_convert\nimport albumentations as A\nfrom albumentations.pytorch import ToTensorV2\nfrom lightning.pytorch.callbacks import ModelCheckpoint\nfrom sklearn.model_selection import train_test_split\n\n# Paths for the dataset\nTRAIN_IMG_DIR = \"/kaggle/input/global-wheat-detection/train\"\nTRAIN_ANNOTATIONS = \"/kaggle/input/global-wheat-detection/train.csv\"\n\n# Dataset setup\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        records = self.df[self.df['image_id'] == image_id]\n        boxes = [list(map(float, row['bbox'][1:-1].split(\",\"))) for _, row in records.iterrows()]\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\n\n# Advanced data augmentations\ndata_transforms = A.Compose([\n    A.Resize(1024, 1024),\n    A.RandomRotate90(p=0.5),\n    A.Flip(p=0.5),\n    A.RandomBrightnessContrast(p=0.5),\n    A.MotionBlur(p=0.2),\n    A.CLAHE(p=0.2),\n    A.RandomGamma(p=0.2),\n    A.Normalize(mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225)),\n    ToTensorV2()\n])\n\n# Lightning DataModule\nclass WheatDataModule(pl.LightningDataModule):\n    def __init__(self, img_dir, annotations_file, batch_size=4, 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        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(train_dataset, batch_size=self.batch_size, shuffle=True, num_workers=self.num_workers, collate_fn=self.collate_fn)\n\n    def val_dataloader(self):\n        val_dataset = WheatDataset(self.img_dir, self.annotations_file, transforms=data_transforms)\n        return DataLoader(val_dataset, batch_size=self.batch_size, shuffle=False, num_workers=self.num_workers, collate_fn=self.collate_fn)\n\n    @staticmethod\n    def collate_fn(batch):\n        images, targets = zip(*batch)\n        return list(images), list(targets)\n\n# Model Definition\nclass FasterRCNNModel(pl.LightningModule):\n    def __init__(self, lr=1e-3, weight_decay=1e-4, 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        images = list(image.to(self.device) for image in images)\n        targets = [{k: v.to(self.device) for k, v in target.items()} for target in targets]\n        \n        loss_dict = self.model(images, targets)\n        print(f\"Debug - Loss Dict (training_step): {loss_dict}\")  # Debugging line\n\n        if isinstance(loss_dict, dict):\n            loss = sum(loss for loss in loss_dict.values())\n            self.log('train_loss', loss)\n            return loss\n        else:\n            raise ValueError(\"Model did not return a dictionary of losses.\")\n\n    def validation_step(self, batch, batch_idx):\n        images, targets = batch\n        images = list(image.to(self.device) for image in images)\n        targets = [{k: v.to(self.device) for k, v in target.items()} for target in targets]\n\n        # Temporarily set model to training mode for validation to get losses\n        self.model.train()\n        with torch.no_grad():\n            loss_dict = self.model(images, targets)\n        self.model.eval()  # Reset to evaluation mode\n\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(\"Model did not return a dictionary of losses.\")\n\n\n    def configure_optimizers(self):\n        optimizer = optim.SGD(self.parameters(), lr=self.lr, momentum=self.momentum, weight_decay=self.weight_decay)\n        scheduler = optim.lr_scheduler.ReduceLROnPlateau(optimizer, mode='min', patience=2, factor=0.2)\n        return {\"optimizer\": optimizer, \"lr_scheduler\": {\"scheduler\": scheduler, \"monitor\": \"val_loss\"}}\n\n# Optuna Objective Function\ndef objective(trial):\n    lr = trial.suggest_float(\"lr\", 1e-5, 1e-3, log=True)\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    batch_size = trial.suggest_categorical(\"batch_size\", [4, 8])\n\n    data_module = WheatDataModule(TRAIN_IMG_DIR, TRAIN_ANNOTATIONS, batch_size=batch_size)\n    model = FasterRCNNModel(lr=lr, weight_decay=weight_decay, momentum=momentum)\n\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 = pl.Trainer(\n        max_epochs=5,\n        accelerator='auto',\n        precision=16,\n        log_every_n_steps=10,\n        callbacks=[checkpoint_callback, PyTorchLightningPruningCallback(trial, monitor=\"val_loss\")]\n    )\n\n    trainer.fit(model, data_module)\n    val_loss = trainer.callback_metrics.get(\"val_loss\")\n\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\nif __name__ == \"__main__\":\n    pruner = optuna.pruners.MedianPruner()\n    study = optuna.create_study(direction=\"minimize\", pruner=pruner)\n    study.optimize(objective, n_trials=5, timeout=300)\n\n    print(\"Number of finished trials:\", len(study.trials))\n    trial = study.best_trial\n    print(f\"Best trial - Lowest val_loss: {trial.value}\")\n    print(\"Hyperparameters:\")\n    for key, value in trial.params.items():\n        print(f\"  {key}: {value}\")\n    print(\"Best model checkpoint path:\", trial.user_attrs.get(\"best_model_path\", \"No path saved\"))\n","metadata":{"execution":{"iopub.status.busy":"2024-10-30T17:33:47.774212Z","iopub.execute_input":"2024-10-30T17:33:47.774854Z","iopub.status.idle":"2024-10-30T18:28:17.182640Z","shell.execute_reply.started":"2024-10-30T17:33:47.774807Z","shell.execute_reply":"2024-10-30T18:28:17.181328Z"},"trusted":true},"execution_count":null,"outputs":[]}]}