{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":112509,"databundleVersionId":14254895,"sourceType":"competition"}],"dockerImageVersionId":31193,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os\nimport pandas as pd\nimport numpy as np\nfrom PIL import Image\nimport torch\nimport torch.nn as nn\nimport torch.optim as optim\nfrom torch.utils.data import Dataset, DataLoader\nfrom torchvision import transforms\nfrom transformers import AutoModelForImageClassification, AutoConfig\nimport warnings\nwarnings.filterwarnings('ignore')\n\n# Set device\ndevice = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\nprint(f\"Using device: {device}\")\n\n# Load training data\ntrain_df = pd.read_csv('/kaggle/input/csiro-biomass/train.csv')\ntrain_df = train_df[train_df['State'] != 'WA']\nprint(f\"Total training samples: {len(train_df)}\")\nprint(f\"After removing WA: {len(train_df)}\")\n\n# Filter for the three target variables we need to predict\ntargets_to_predict = ['Dry_Clover_g', 'Dry_Dead_g', 'Dry_Green_g']\ntrain_df_filtered = train_df[train_df['target_name'].isin(targets_to_predict)]\n\n# Create a mapping from image to targets\nimage_targets = {}\nfor img_path in train_df_filtered['image_path'].unique():\n    img_data = train_df_filtered[train_df_filtered['image_path'] == img_path]\n    targets = {}\n    for target_name in targets_to_predict:\n        target_value = img_data[img_data['target_name'] == target_name]['target'].values\n        if len(target_value) > 0:\n            targets[target_name] = float(target_value[0])\n        else:\n            targets[target_name] = 0.0\n    image_targets[img_path] = targets\n\nprint(f\"Unique training images: {len(image_targets)}\")\n\n# Dataset class\nclass BiomassDataset(Dataset):\n    def __init__(self, image_targets_dict, transform=None):\n        self.image_paths = list(image_targets_dict.keys())\n        self.targets = list(image_targets_dict.values())\n        self.transform = transform\n        \n    def __len__(self):\n        return len(self.image_paths)\n    \n    def __getitem__(self, idx):\n        img_path = self.image_paths[idx]\n        full_path = f\"/kaggle/input/csiro-biomass/{img_path}\"\n        \n        try:\n            image = Image.open(full_path).convert('RGB')\n        except Exception as e:\n            print(f\"Error loading image {full_path}: {e}\")\n            # Create blank image if file not found\n            image = Image.new('RGB', (500, 500), color='black')\n        \n        if self.transform:\n            image = self.transform(image)\n        \n        target_dict = self.targets[idx]\n        target_tensor = torch.tensor([\n            target_dict['Dry_Clover_g'],\n            target_dict['Dry_Dead_g'], \n            target_dict['Dry_Green_g']\n        ], dtype=torch.float32)\n        \n        return image, target_tensor\n\n# Augmentations\ntrain_transform = transforms.Compose([\n    transforms.Resize((500, 500)),\n    transforms.RandomHorizontalFlip(p=0.5),\n    transforms.RandomRotation(degrees=30),\n    transforms.ColorJitter(brightness=0.2, contrast=0.2, saturation=0.2),\n    transforms.ToTensor(),\n    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])\n])\n\n# Create dataset and dataloader\ndataset = BiomassDataset(image_targets, transform=train_transform)\ndataloader = DataLoader(dataset, batch_size=32, shuffle=True, num_workers=4, pin_memory=True)\n\n# Load EVA-02 model\nprint(\"Loading EVA-02 model...\")\ntry:\n    # Try to load the model directly\n    config = AutoConfig.from_pretrained(\"eva/eva02-base-patch14-224\")\n    model = AutoModelForImageClassification.from_config(config)\n    print(\"Model loaded from config\")\nexcept:\n    try:\n        # Alternative approach\n        from transformers import AutoModel\n        model = AutoModel.from_pretrained(\"eva/eva02-base-patch14-224\")\n        print(\"Model loaded as base model\")\n    except Exception as e:\n        print(f\"Error loading EVA-02: {e}\")\n        print(\"Falling back to a simpler model\")\n        # Fallback to a simpler model\n        import torchvision.models as models\n        model = models.resnet50(pretrained=True)\n        model.fc = nn.Linear(model.fc.in_features, 3)\n        print(\"Using ResNet50 as fallback\")\n\n# Modify the classifier head for regression\nif hasattr(model, 'classifier'):\n    model.classifier = nn.Sequential(\n        nn.Linear(model.config.hidden_size, 1024),\n        nn.ReLU(),\n        nn.Dropout(0.2),\n        nn.Linear(1024, 512),\n        nn.ReLU(),\n        nn.Dropout(0.2),\n        nn.Linear(512, 3)  # Output: Dry_Clover_g, Dry_Dead_g, Dry_Green_g\n    )\nelse:\n    # For ResNet fallback\n    if hasattr(model, 'fc'):\n        model.fc = nn.Sequential(\n            nn.Linear(model.fc.in_features, 1024),\n            nn.ReLU(),\n            nn.Dropout(0.2),\n            nn.Linear(1024, 512),\n            nn.ReLU(),\n            nn.Dropout(0.2),\n            nn.Linear(512, 3)\n        )\n\n# Use DataParallel for multiple GPUs\nif torch.cuda.device_count() > 1:\n    print(f\"Using {torch.cuda.device_count()} GPUs with DataParallel\")\n    model = nn.DataParallel(model)\n\nmodel = model.to(device)\n\n# Loss and optimizer\ncriterion = nn.MSELoss()\noptimizer = optim.AdamW(model.parameters(), lr=1e-4)\nscheduler = optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=10)\n\n# Training loop\nnum_epochs = 10\nprint(f\"Starting training for {num_epochs} epochs\")\n\nfor epoch in range(num_epochs):\n    model.train()\n    total_loss = 0\n    batch_count = 0\n    \n    for batch_idx, (images, targets) in enumerate(dataloader):\n        images = images.to(device)\n        targets = targets.to(device)\n        \n        optimizer.zero_grad()\n        \n        # Forward pass\n        if hasattr(model, 'module'):\n            # For DataParallel\n            outputs = model.module(images)\n        else:\n            outputs = model(images)\n            \n        # Get logits\n        if hasattr(outputs, 'logits'):\n            logits = outputs.logits\n        else:\n            logits = outputs\n            \n        loss = criterion(logits, targets)\n        loss.backward()\n        optimizer.step()\n        \n        total_loss += loss.item()\n        batch_count += 1\n        \n        if batch_idx % 20 == 0:\n            print(f\"Epoch {epoch+1}, Batch {batch_idx}, Loss: {loss.item():.4f}\")\n    \n    scheduler.step()\n    \n    avg_loss = total_loss / batch_count\n    print(f\"Epoch {epoch+1} completed. Average Loss: {avg_loss:.4f}\")\n    \n    # Save model checkpoint\n    if hasattr(model, 'module'):\n        torch.save(model.module.state_dict(), f'/kaggle/working/biomass_model_epoch_{epoch+1}.pth')\n    else:\n        torch.save(model.state_dict(), f'/kaggle/working/biomass_model_epoch_{epoch+1}.pth')\n\nprint(\"Training completed!\")\n# Save final model\nif hasattr(model, 'module'):\n    torch.save(model.module.state_dict(), '/kaggle/working/biomass_model_final.pth')\nelse:\n    torch.save(model.state_dict(), '/kaggle/working/biomass_model_final.pth')","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-12-02T08:18:56.445499Z","iopub.execute_input":"2025-12-02T08:18:56.445835Z","iopub.status.idle":"2025-12-02T08:22:44.660163Z","shell.execute_reply.started":"2025-12-02T08:18:56.445804Z","shell.execute_reply":"2025-12-02T08:22:44.659098Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}