{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.11","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":106372,"databundleVersionId":12864984,"sourceType":"competition"}],"dockerImageVersionId":31040,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nimport warnings\nimport os\nimport sys\nimport subprocess\nfrom PIL import Image\nfrom tqdm import tqdm\n\n# Suppress warnings\nwarnings.filterwarnings('ignore')\n\n# Install and import timm for state-of-the-art models\ntry:\n    import timm\n    print(f\"timm version: {timm.__version__} successfully imported.\")\nexcept ImportError:\n    print(\"timm not found. Installing...\")\n    subprocess.check_call([sys.executable, \"-m\", \"pip\", \"install\", \"timm\"])\n    import timm\n    print(f\"timm version: {timm.__version__} installed and imported.\")\n\n\nimport torch\nimport torch.nn as nn\nimport torch.optim as optim\nfrom torch.utils.data import Dataset, DataLoader\nfrom torchvision import transforms, models\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.metrics import classification_report, confusion_matrix, accuracy_score\nfrom sklearn.preprocessing import LabelEncoder\n\n# --- Configuration ---\nclass CFG:\n    # General\n    SEED = 42\n    DEVICE = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n    \n    # Data paths\n    DATA_DIR = \"/kaggle/input/2025-bamboo-summer-competiton-dl-pr/\"\n    \n    # Model parameters\n    MODEL_NAME = 'efficientnet_b4'\n    IMG_SIZE = 384\n    NUM_CLASSES = 75\n    \n    # Training parameters\n    BATCH_SIZE = 16 # Reduced for larger image size\n    EPOCHS = 30 # Can be adjusted based on validation performance\n    LEARNING_RATE = 1e-4\n    WEIGHT_DECAY = 1e-5\n    LABEL_SMOOTHING = 0.1\n    \n    # TTA (Test-Time Augmentation)\n    TTA_STEPS = 5\n\n# Set random seeds for reproducibility\nnp.random.seed(CFG.SEED)\ntorch.manual_seed(CFG.SEED)\nif torch.cuda.is_available():\n    torch.cuda.manual_seed_all(CFG.SEED)\n\n# --- Data Loading and Preparation ---\ntrain_df = pd.read_csv(os.path.join(CFG.DATA_DIR, \"train.csv\"))\ntest_df = pd.read_csv(os.path.join(CFG.DATA_DIR, \"test.csv\"))\nsample_submission = pd.read_csv(os.path.join(CFG.DATA_DIR, \"sample_submission.csv\"))\n\n# Encode labels\nlabel_encoder = LabelEncoder()\ntrain_df['label_encoded'] = label_encoder.fit_transform(train_df['label'])\n\nX_train, X_val, y_train, y_val = train_test_split(\n    train_df['filename'],\n    train_df['label_encoded'],\n    test_size=0.15, # Using a slightly smaller validation set\n    random_state=CFG.SEED,\n    stratify=train_df['label_encoded']\n)\n\n# --- Data Augmentation and Datasets ---\ndef get_transforms(is_train=True):\n    if is_train:\n        return transforms.Compose([\n            transforms.Resize((CFG.IMG_SIZE, CFG.IMG_SIZE)),\n            transforms.TrivialAugmentWide(), # Advanced augmentation\n            transforms.ToTensor(),\n            transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])\n        ])\n    else:\n        return transforms.Compose([\n            transforms.Resize((CFG.IMG_SIZE, CFG.IMG_SIZE)),\n            transforms.ToTensor(),\n            transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])\n        ])\n\nclass ButterflyDataset(Dataset):\n    def __init__(self, filenames, labels, root_dir, transform=None, is_test=False):\n        self.filenames = filenames\n        self.labels = labels\n        self.root_dir = root_dir\n        self.transform = transform\n        self.is_test = is_test\n    \n    def __len__(self):\n        return len(self.filenames)\n    \n    def __getitem__(self, idx):\n        img_path = os.path.join(self.root_dir, self.filenames[idx])\n        image = Image.open(img_path).convert('RGB')\n        \n        if self.transform:\n            image = self.transform(image)\n        \n        if self.is_test:\n            return image\n        else:\n            return image, torch.tensor(self.labels[idx], dtype=torch.long)\n\n# Create datasets and dataloaders\ntrain_dataset = ButterflyDataset(X_train.values, y_train.values, os.path.join(CFG.DATA_DIR, 'train'), transform=get_transforms(is_train=True))\nval_dataset = ButterflyDataset(X_val.values, y_val.values, os.path.join(CFG.DATA_DIR, 'train'), transform=get_transforms(is_train=False))\ntest_dataset = ButterflyDataset(test_df['filename'].values, None, os.path.join(CFG.DATA_DIR, 'test'), transform=get_transforms(is_train=False), is_test=True)\n\ntrain_loader = DataLoader(train_dataset, batch_size=CFG.BATCH_SIZE, shuffle=True, num_workers=4, pin_memory=True)\nval_loader = DataLoader(val_dataset, batch_size=CFG.BATCH_SIZE, shuffle=False, num_workers=4, pin_memory=True)\ntest_loader = DataLoader(test_dataset, batch_size=CFG.BATCH_SIZE, shuffle=False, num_workers=4, pin_memory=True)\n\n\n# --- Model Definition ---\nclass ButterflyClassifier(nn.Module):\n    def __init__(self, model_name, num_classes, pretrained=True):\n        super(ButterflyClassifier, self).__init__()\n        self.model = timm.create_model(model_name, pretrained=pretrained, num_classes=num_classes)\n        \n    def forward(self, x):\n        return self.model(x)\n\n# --- Training and Evaluation Loop ---\ndef run_training(model, train_loader, val_loader, epochs):\n    criterion = nn.CrossEntropyLoss(label_smoothing=CFG.LABEL_SMOOTHING)\n    optimizer = optim.AdamW(model.parameters(), lr=CFG.LEARNING_RATE, weight_decay=CFG.WEIGHT_DECAY)\n    scheduler = optim.lr_scheduler.CosineAnnealingWarmRestarts(optimizer, T_0=epochs//3, T_mult=1, eta_min=1e-6)\n    \n    best_val_acc = 0.0\n    best_model_state = None\n    \n    for epoch in range(epochs):\n        model.train()\n        train_loss = 0.0\n        train_corrects = 0\n        \n        pbar = tqdm(train_loader, desc=f\"Epoch {epoch+1}/{epochs} [Training]\")\n        for inputs, labels in pbar:\n            inputs, labels = inputs.to(CFG.DEVICE), labels.to(CFG.DEVICE)\n            \n            optimizer.zero_grad()\n            outputs = model(inputs)\n            loss = criterion(outputs, labels)\n            loss.backward()\n            optimizer.step()\n            \n            train_loss += loss.item() * inputs.size(0)\n            _, preds = torch.max(outputs, 1)\n            train_corrects += torch.sum(preds == labels.data)\n            pbar.set_postfix(loss=loss.item())\n\n        scheduler.step()\n        \n        model.eval()\n        val_loss = 0.0\n        val_corrects = 0\n        with torch.no_grad():\n            for inputs, labels in tqdm(val_loader, desc=f\"Epoch {epoch+1}/{epochs} [Validation]\"):\n                inputs, labels = inputs.to(CFG.DEVICE), labels.to(CFG.DEVICE)\n                outputs = model(inputs)\n                loss = criterion(outputs, labels)\n                val_loss += loss.item() * inputs.size(0)\n                _, preds = torch.max(outputs, 1)\n                val_corrects += torch.sum(preds == labels.data)\n\n        train_acc = train_corrects.double() / len(train_loader.dataset)\n        val_acc = val_corrects.double() / len(val_loader.dataset)\n        \n        print(f\"Epoch {epoch+1}: Train Acc: {train_acc:.4f}, Val Acc: {val_acc:.4f}\")\n        \n        if val_acc > best_val_acc:\n            best_val_acc = val_acc\n            best_model_state = model.state_dict()\n            print(f\"✅ New best validation accuracy: {best_val_acc:.4f}\")\n            torch.save(best_model_state, f\"{CFG.MODEL_NAME}_best.pth\")\n\n    print(f\"Training finished. Best Val Acc: {best_val_acc:.4f}\")\n    model.load_state_dict(best_model_state)\n    return model\n\n# --- Prediction with Test-Time Augmentation (TTA) ---\ndef predict_with_tta(model, test_loader, tta_steps):\n    model.eval()\n    tta_transforms = transforms.Compose([\n        transforms.RandomHorizontalFlip(),\n        transforms.RandomRotation(15),\n        transforms.ColorJitter(brightness=0.1, contrast=0.1, saturation=0.1),\n    ])\n    \n    all_preds = []\n    \n    with torch.no_grad():\n        for images in tqdm(test_loader, desc=\"Predicting with TTA\"):\n            images = images.to(CFG.DEVICE)\n            batch_probs = torch.zeros((images.size(0), CFG.NUM_CLASSES), device=CFG.DEVICE)\n            \n            # Original image\n            outputs = model(images)\n            batch_probs += torch.softmax(outputs, dim=1)\n            \n            # Augmented images\n            for _ in range(tta_steps - 1):\n                aug_images = tta_transforms(images)\n                outputs = model(aug_images)\n                batch_probs += torch.softmax(outputs, dim=1)\n            \n            batch_probs /= tta_steps\n            _, preds = torch.max(batch_probs, 1)\n            all_preds.extend(preds.cpu().numpy())\n            \n    return np.array(all_preds)\n\n# --- Main Execution ---\nprint(f\"Using device: {CFG.DEVICE}\")\nprint(f\"Training model: {CFG.MODEL_NAME}\")\n\n# Initialize and train the model\nmodel = ButterflyClassifier(CFG.MODEL_NAME, CFG.NUM_CLASSES).to(CFG.DEVICE)\nmodel = run_training(model, train_loader, val_loader, epochs=CFG.EPOCHS)\n\n# Get predictions using TTA\npredictions = predict_with_tta(model, test_loader, tta_steps=CFG.TTA_STEPS)\npredicted_labels = label_encoder.inverse_transform(predictions)\n\n# --- Create Submission File ---\nsubmission_df = pd.DataFrame({\n    'filename': test_df['filename'],\n    'label': predicted_labels\n})\nsubmission_df.to_csv('submission.csv', index=False)\n\nprint(\"\\nSubmission file 'submission.csv' created successfully!\")\nprint(\"Top 10 predictions:\")\nprint(submission_df.head(10))\n\n# Display prediction distribution\nplt.figure(figsize=(12, 6))\nsns.countplot(y=submission_df['label'], order=submission_df['label'].value_counts().iloc[:25].index, palette='viridis')\nplt.title('Top 25 Predicted Butterfly Species')\nplt.xlabel('Count')\nplt.ylabel('Species')\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-08T03:31:28.517090Z","iopub.execute_input":"2025-07-08T03:31:28.517296Z"}},"outputs":[],"execution_count":null}]}