{"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":6799,"databundleVersionId":4225553,"sourceType":"competition"}],"dockerImageVersionId":31040,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os\nimport json\nfrom PIL import Image\n\ndef load_synset_mapping(synset_mapping_path):\n    mapping = {}\n    with open(synset_mapping_path, 'r') as f:\n        for line in f:\n            parts = line.strip().split(' ', 1)\n            if len(parts) == 2:\n                synset_id, label = parts\n                mapping[synset_id] = label\n    return mapping\n\ndef load_localization_annotations(annotation_csv_path):\n    import csv\n    annotations = []\n    with open(annotation_csv_path, 'r') as f:\n        reader = csv.DictReader(f)\n        for row in reader:\n            image_id = row['ImageId']\n            prediction_string = row['PredictionString']\n            if not prediction_string:\n                continue\n            parts = prediction_string.strip().split()\n            assert len(parts) % 5 == 0, f\"Invalid PredictionString length {len(parts)} for {image_id}\"\n            for i in range(0, len(parts), 5):\n                class_id = parts[i]\n                xmin = float(parts[i+1])\n                ymin = float(parts[i+2])\n                xmax = float(parts[i+3])\n                ymax = float(parts[i+4])\n                annotations.append({\n                    \"image_id\": image_id,\n                    \"class_id\": class_id,\n                    \"bbox\": [xmin, ymin, xmax, ymax]\n                })\n    return annotations\n\ndef convert_bbox_to_xywh(xmin, ymin, xmax, ymax, width, height):\n    x = xmin * width\n    y = ymin * height\n    w = (xmax - xmin) * width\n    h = (ymax - ymin) * height\n    return [round(x, 2), round(y, 2), round(w, 2), round(h, 2)]\n\ndef prepare_coco_dataset(\n    images_dir,\n    annotations,\n    selected_classes,\n    synset_mapping,\n    output_json_path\n):\n    # Map selected classes to consecutive IDs starting from 1\n    categories = {cls_name: idx + 1 for idx, cls_name in enumerate(sorted(selected_classes))}\n    images_info = {}\n    annotations_coco = []\n    annotation_id = 1\n\n    for ann in annotations:\n        class_id = ann[\"class_id\"]\n        if class_id not in selected_classes:\n            continue\n        # Filename with synset folder e.g. n01440764/n01440764_10026.JPEG\n        filename = ann[\"image_id\"] + \".JPEG\"\n        synset_folder = class_id\n        file_path = os.path.join(images_dir, synset_folder, filename)\n        if not os.path.exists(file_path):\n            # Skip missing images completely\n            continue\n        if ann[\"image_id\"] not in images_info:\n            try:\n                with Image.open(file_path) as img:\n                    width, height = img.size\n                    images_info[ann[\"image_id\"]] = {\n                        \"id\": len(images_info),\n                        \"file_name\": f\"{synset_folder}/{filename}\",\n                        \"width\": width,\n                        \"height\": height\n                    }\n            except Exception as e:\n                print(f\"Error loading image {file_path}: {e}\")\n                continue\n        img_info = images_info[ann[\"image_id\"]]\n        bbox_coco = convert_bbox_to_xywh(*ann[\"bbox\"], img_info[\"width\"], img_info[\"height\"])\n        annotations_coco.append({\n            \"id\": annotation_id,\n            \"image_id\": img_info[\"id\"],\n            \"category_id\": categories[class_id],\n            \"bbox\": bbox_coco,\n            \"area\": round(bbox_coco[2] * bbox_coco[3], 2),\n            \"iscrowd\": 0\n        })\n        annotation_id += 1\n\n    if len(images_info) == 0:\n        raise RuntimeError(\"No images found. Check dataset path and selected classes.\")\n\n    coco_dict = {\n        \"images\": list(images_info.values()),\n        \"annotations\": annotations_coco,\n        \"categories\": [{\"id\": idx, \"name\": synset_mapping.get(name, name)} for name, idx in categories.items()]\n    }\n\n    with open(output_json_path, 'w') as f:\n        json.dump(coco_dict, f, indent=2)\n    print(f\"COCO JSON saved to {output_json_path}\")\n    print(f\"Total images: {len(images_info)}\")\n    print(f\"Total annotations: {len(annotations_coco)}\")\n    print(f\"Selected categories: {sorted(selected_classes)}\")\n\nif __name__ == \"__main__\":\n    IMAGES_BASE_DIR = \"/kaggle/input/imagenet-object-localization-challenge/ILSVRC/Data/CLS-LOC/train\"\n    SYNSET_MAPPING_PATH = \"/kaggle/input/imagenet-object-localization-challenge/LOC_synset_mapping.txt\"\n    ANNOTATION_CSV_PATH = \"/kaggle/input/imagenet-object-localization-challenge/LOC_train_solution.csv\"\n    OUTPUT_JSON_PATH = \"/kaggle/working/coco_format_train_fixed.json\"\n\n    # Select your classes by synset ids (ensure this matches validation)\n    SELECTED_CLASSES = {\n        \"n02084071\",  # dog\n        \"n02124075\",  # Egyptian cat\n        \"n02219486\",  # ant\n        \"n02391049\",  # zebra\n        \"n02504458\"   # African elephant\n    }\n\n    print(\"Loading synset mapping...\")\n    synset_mapping = load_synset_mapping(SYNSET_MAPPING_PATH)\n    print(f\"Loaded {len(synset_mapping)} classes\")\n\n    print(\"Loading annotations...\")\n    annotations = load_localization_annotations(ANNOTATION_CSV_PATH)\n    print(f\"Loaded {len(annotations)} total annotations\")\n\n    print(\"Preparing COCO format dataset with fixed paths...\")\n    prepare_coco_dataset(IMAGES_BASE_DIR, annotations, SELECTED_CLASSES, synset_mapping, OUTPUT_JSON_PATH)\n","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-06-16T10:13:33.44586Z","iopub.execute_input":"2025-06-16T10:13:33.446144Z","iopub.status.idle":"2025-06-16T10:13:54.749367Z","shell.execute_reply.started":"2025-06-16T10:13:33.446123Z","shell.execute_reply":"2025-06-16T10:13:54.748769Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport json\nimport csv\nfrom PIL import Image\n\ndef load_synset_mapping(synset_mapping_path):\n    mapping = {}\n    with open(synset_mapping_path, 'r') as f:\n        for line in f:\n            parts = line.strip().split(' ', 1)\n            if len(parts) == 2:\n                synset_id, label = parts\n                mapping[synset_id] = label\n    return mapping\n\ndef load_localization_annotations(annotation_csv_path):\n    annotations = []\n    with open(annotation_csv_path, 'r') as f:\n        reader = csv.DictReader(f)\n        for row in reader:\n            image_id = row['ImageId']\n            prediction_string = row['PredictionString']\n            if not prediction_string:\n                continue\n            parts = prediction_string.strip().split()\n            assert len(parts) % 5 == 0, f\"Invalid PredictionString length {len(parts)} for {image_id}\"\n            for i in range(0, len(parts), 5):\n                class_id = parts[i]\n                xmin = float(parts[i+1])\n                ymin = float(parts[i+2])\n                xmax = float(parts[i+3])\n                ymax = float(parts[i+4])\n                annotations.append({\n                    \"image_id\": image_id,\n                    \"class_id\": class_id,\n                    \"bbox\": [xmin, ymin, xmax, ymax]\n                })\n    return annotations\n\ndef convert_bbox_to_xywh(xmin, ymin, xmax, ymax, width, height):\n    x = xmin * width\n    y = ymin * height\n    w = (xmax - xmin) * width\n    h = (ymax - ymin) * height\n    return [round(x, 2), round(y, 2), round(w, 2), round(h, 2)]\n\ndef prepare_coco_dataset(\n    images_dir,\n    annotations,\n    selected_classes,\n    synset_mapping,\n    output_json_path\n):\n    categories = {cls_name: idx + 1 for idx, cls_name in enumerate(sorted(selected_classes))}\n    images_info = {}\n    annotations_coco = []\n    annotation_id = 1\n\n    for ann in annotations:\n        class_id = ann[\"class_id\"]\n        if class_id not in selected_classes:\n            continue\n        # Filename with synset folder e.g. n01440764/n01440764_10026.JPEG\n        filename = ann[\"image_id\"] + \".JPEG\"\n        file_path = os.path.join(images_dir, filename)\n        if not os.path.exists(file_path):\n            # Skip missing images completely\n            continue\n        if ann[\"image_id\"] not in images_info:\n            with Image.open(file_path) as img:\n                width, height = img.size\n                images_info[ann[\"image_id\"]] = {\n                    \"id\": len(images_info),\n                    \"file_name\": filename,\n                    \"width\": width,\n                    \"height\": height\n                }\n        img_info = images_info[ann[\"image_id\"]]\n        bbox_coco = convert_bbox_to_xywh(*ann[\"bbox\"], img_info[\"width\"], img_info[\"height\"])\n        annotations_coco.append({\n            \"id\": annotation_id,\n            \"image_id\": img_info[\"id\"],\n            \"category_id\": categories[class_id],\n            \"bbox\": bbox_coco,\n            \"area\": round(bbox_coco[2] * bbox_coco[3], 2),\n            \"iscrowd\": 0\n        })\n        annotation_id += 1\n\n    if len(images_info) == 0:\n        raise RuntimeError(\"No images found. Check dataset path and selected classes.\")\n\n    coco_dict = {\n        \"images\": list(images_info.values()),\n        \"annotations\": annotations_coco,\n        \"categories\": [{\"id\": idx, \"name\": name} for name, idx in categories.items()]\n    }\n\n    with open(output_json_path, 'w') as f:\n        json.dump(coco_dict, f, indent=2)\n    print(f\"COCO JSON saved to {output_json_path}\")\n    print(f\"Total images: {len(images_info)}\")\n    print(f\"Total annotations: {len(annotations_coco)}\")\n    print(f\"Selected categories: {sorted(selected_classes)}\")\n\nif __name__ == \"__main__\":\n    # Update paths for validation dataset\n    IMAGES_BASE_DIR = \"/kaggle/input/imagenet-object-localization-challenge/ILSVRC/Data/CLS-LOC/val\"\n    SYNSET_MAPPING_PATH = \"/kaggle/input/imagenet-object-localization-challenge/LOC_synset_mapping.txt\"\n    ANNOTATION_CSV_PATH = \"/kaggle/input/imagenet-object-localization-challenge/LOC_val_solution.csv\"\n    OUTPUT_JSON_PATH = \"/kaggle/working/coco_format_val.json\"\n\n    # Select your classes by synset ids, example:\n    SELECTED_CLASSES = {\n        \"n02084071\",  # dog\n        \"n02124075\",  # Egyptian cat\n        \"n02219486\",  # ant\n        \"n02391049\",  # zebra\n        \"n02504458\"   # African elephant\n    }\n\n    print(\"Loading synset mapping...\")\n    synset_mapping = load_synset_mapping(SYNSET_MAPPING_PATH)\n    print(f\"Loaded {len(synset_mapping)} classes\")\n\n    print(\"Loading annotations...\")\n    annotations = load_localization_annotations(ANNOTATION_CSV_PATH)\n    print(f\"Loaded {len(annotations)} total annotations\")\n\n    print(\"Preparing COCO format dataset for validation...\")\n    prepare_coco_dataset(IMAGES_BASE_DIR, annotations, SELECTED_CLASSES, synset_mapping, OUTPUT_JSON_PATH)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-16T10:14:18.937192Z","iopub.execute_input":"2025-06-16T10:14:18.937942Z","iopub.status.idle":"2025-06-16T10:14:19.601306Z","shell.execute_reply.started":"2025-06-16T10:14:18.937919Z","shell.execute_reply":"2025-06-16T10:14:19.600503Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport json\nimport torch\nfrom torch.utils.data import Dataset, DataLoader\nfrom torchvision import transforms\nfrom PIL import Image\nfrom transformers import DetrImageProcessor, DetrForObjectDetection\nfrom tqdm import tqdm\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport random\nfrom torch.amp import autocast, GradScaler\nimport torch.nn as nn\n\nclass EnhancedCOCODataset(Dataset):\n    def __init__(self, images_dir, annotation_json, processor, transforms=None, \n                 use_augmentation=True, max_samples=None):\n        super().__init__()\n        self.images_dir = images_dir\n        self.transforms = transforms\n        self.processor = processor\n        self.use_augmentation = use_augmentation\n        \n        with open(annotation_json) as f:\n            coco = json.load(f)\n        \n        self.image_id_to_info = {img['id']: img for img in coco['images']}\n        self.categories = {cat['id']: cat['name'] for cat in coco['categories']}\n        print(f\"📊 Total categories in dataset: {len(self.categories)}\")\n        print(f\"🏷️ Categories: {list(self.categories.values())}\")\n        \n        # Build a mapping of image_id -> list of anns\n        self.image_to_annotations = {img_id: [] for img_id in self.image_id_to_info.keys()}\n        category_counts = {}\n        \n        for ann in coco['annotations']:\n            self.image_to_annotations[ann['image_id']].append(ann)\n            cat_id = ann['category_id']\n            category_counts[cat_id] = category_counts.get(cat_id, 0) + 1\n\n        # Filter images that have annotations\n        self.image_ids = [img_id for img_id in self.image_id_to_info.keys() \n                         if len(self.image_to_annotations[img_id]) > 0]\n        \n        # Limit samples if specified\n        if max_samples and len(self.image_ids) > max_samples:\n            self.image_ids = random.sample(self.image_ids, max_samples)\n        \n        # Count missing images\n        self.missing_images = 0\n        valid_image_ids = []\n        for img_id in self.image_ids:\n            img_info = self.image_id_to_info[img_id]\n            img_path = os.path.join(self.images_dir, img_info['file_name'])\n            if os.path.exists(img_path):\n                valid_image_ids.append(img_id)\n            else:\n                self.missing_images += 1\n        \n        self.image_ids = valid_image_ids\n        print(f\"Total missing images: {self.missing_images}\")\n        print(f\"📸 Valid images for training: {len(self.image_ids)}\")\n        \n        # Print category distribution\n        print(\"\\n🎯 Category distribution:\")\n        for cat_id, count in sorted(category_counts.items(), key=lambda x: x[1], reverse=True)[:20]:\n            if cat_id in self.categories:\n                print(f\"   {self.categories[cat_id]}: {count} annotations\")\n\n    def __len__(self):\n        return len(self.image_ids)\n\n    def get_enhanced_transforms(self, image):\n        \"\"\"Apply advanced data augmentation\"\"\"\n        if not self.use_augmentation:\n            return transforms.ToTensor()(image)\n        \n        # Advanced augmentation pipeline\n        augmentation_transforms = transforms.Compose([\n            transforms.RandomHorizontalFlip(0.5),\n            transforms.RandomRotation(degrees=15),\n            transforms.RandomResizedCrop(size=(800, 800), scale=(0.7, 1.0), ratio=(0.8, 1.2)),\n            transforms.ColorJitter(brightness=0.3, contrast=0.3, saturation=0.3, hue=0.15),\n            transforms.RandomAdjustSharpness(sharpness_factor=2, p=0.3),\n            transforms.RandomAutocontrast(p=0.3),\n            transforms.RandomEqualize(p=0.2),\n            transforms.ToTensor(),\n            transforms.RandomErasing(p=0.2, scale=(0.02, 0.2)),\n        ])\n        \n        return augmentation_transforms(image)\n\n    def __getitem__(self, idx):\n        image_id = self.image_ids[idx]\n        img_info = self.image_id_to_info[image_id]\n        filename = img_info['file_name']\n    \n        img_path = os.path.join(self.images_dir, filename)\n    \n        try:\n            image = Image.open(img_path).convert(\"RGB\")\n        except (FileNotFoundError, Exception) as e:\n            print(f\"Warning: Error loading image: {img_path} - {e}\")\n            return None\n    \n        # Apply enhanced transforms\n        if self.transforms:\n            image = self.transforms(image)\n        else:\n            image = self.get_enhanced_transforms(image)\n    \n        boxes = []\n        labels = []\n        areas = []\n        \n        for ann in self.image_to_annotations[image_id]:\n            # Skip invalid annotations\n            if ann['bbox'][2] <= 0 or ann['bbox'][3] <= 0:\n                continue\n            if ann.get('iscrowd', 0) == 1:\n                continue\n                \n            boxes.append(ann['bbox'])\n            labels.append(ann['category_id'])\n            areas.append(ann.get('area', ann['bbox'][2] * ann['bbox'][3]))\n    \n        if len(boxes) == 0:\n            return None\n    \n        boxes = torch.tensor(boxes, dtype=torch.float32)\n        labels = torch.tensor(labels, dtype=torch.int64)\n        areas = torch.tensor(areas, dtype=torch.float32)\n    \n        annotations = []\n        for box, label, area in zip(boxes, labels, areas):\n            x, y, w, h = box.tolist()\n            annotations.append({\n                \"bbox\": [x, y, w, h],\n                \"category_id\": label.item(),\n                \"area\": area.item(),\n                \"iscrowd\": 0\n            })\n    \n        try:\n            encoding = self.processor(\n                images=image,\n                annotations={\"image_id\": image_id, \"annotations\": annotations},\n                return_tensors=\"pt\",\n                do_pad=True,\n                do_resize=True,\n                size={\"shortest_edge\": 800, \"longest_edge\": 1333}\n            )\n        except Exception as e:\n            print(f\"Error processing image {filename}: {e}\")\n            return None\n\n        if 'labels' not in encoding or len(encoding['labels']) == 0:\n            return None\n\n        return {\n            'pixel_values': encoding['pixel_values'].squeeze(0),\n            'pixel_mask': encoding['pixel_mask'].squeeze(0) if 'pixel_mask' in encoding else None,\n            'labels': encoding['labels'][0]\n        }\n\ndef enhanced_collate_fn(batch):\n    \"\"\"Enhanced collate function with better error handling\"\"\"\n    batch = [item for item in batch if item is not None]\n    if len(batch) == 0:\n        return None\n\n    # Find max dimensions\n    max_h = max([item['pixel_values'].shape[1] for item in batch])\n    max_w = max([item['pixel_values'].shape[2] for item in batch])\n\n    pixel_values = []\n    pixel_mask = []\n    labels = []\n\n    for item in batch:\n        pv = item['pixel_values']\n        \n        # Pad to max dimensions\n        pad_h = max_h - pv.shape[1]\n        pad_w = max_w - pv.shape[2]\n\n        if pad_h > 0 or pad_w > 0:\n            pv = torch.nn.functional.pad(pv, (0, pad_w, 0, pad_h), value=0)\n\n        pixel_values.append(pv)\n        \n        # Handle pixel mask\n        if item['pixel_mask'] is not None:\n            pm = item['pixel_mask']\n            if pad_h > 0 or pad_w > 0:\n                pm = torch.nn.functional.pad(pm, (0, pad_w, 0, pad_h), value=0)\n            pixel_mask.append(pm)\n        else:\n            # Create default mask\n            mask = torch.ones((max_h, max_w), dtype=torch.bool)\n            pixel_mask.append(mask)\n            \n        labels.append(item['labels'])\n\n    pixel_values = torch.stack(pixel_values)\n    pixel_mask = torch.stack(pixel_mask)\n\n    return {\n        'pixel_values': pixel_values,\n        'pixel_mask': pixel_mask,\n        'labels': labels\n    }\n\ndef validate_model(model, val_loader, device):\n    \"\"\"Enhanced validation with better metrics\"\"\"\n    model.eval()\n    total_val_loss = 0.0\n    num_batches = 0\n    \n    with torch.no_grad():\n        for batch in tqdm(val_loader, desc=\"Validating\"):\n            if batch is None:\n                continue\n            \n            try:\n                pixel_values = batch['pixel_values'].to(device)\n                pixel_mask = batch['pixel_mask'].to(device)\n                labels = batch['labels']\n                \n                # Move labels to device\n                for label in labels:\n                    for k, v in label.items():\n                        if isinstance(v, torch.Tensor):\n                            label[k] = v.to(device)\n                \n                with autocast('cuda'):\n                    outputs = model(pixel_values=pixel_values, pixel_mask=pixel_mask, labels=labels)\n                    loss = outputs.loss\n                \n                total_val_loss += loss.item()\n                num_batches += 1\n                \n            except Exception as e:\n                print(f\"Validation batch error: {e}\")\n                continue\n    \n    model.train()\n    return total_val_loss / num_batches if num_batches > 0 else float('inf')\n\nclass AdvancedEarlyStopping:\n    \"\"\"Advanced early stopping with learning rate restoration\"\"\"\n    def __init__(self, patience=7, min_delta=0.0001, restore_best_weights=True, \n                 factor=0.1, cooldown=2):\n        self.patience = patience\n        self.min_delta = min_delta\n        self.restore_best_weights = restore_best_weights\n        self.factor = factor\n        self.cooldown = cooldown\n        self.best_loss = float('inf')\n        self.counter = 0\n        self.best_weights = None\n        self.best_epoch = 0\n        self.cooldown_counter = 0\n        \n    def __call__(self, val_loss, model, epoch):\n        if val_loss < self.best_loss - self.min_delta:\n            self.best_loss = val_loss\n            self.counter = 0\n            self.best_epoch = epoch\n            self.cooldown_counter = 0\n            if self.restore_best_weights:\n                self.best_weights = {k: v.cpu().clone() for k, v in model.state_dict().items()}\n        else:\n            if self.cooldown_counter <= 0:\n                self.counter += 1\n            else:\n                self.cooldown_counter -= 1\n            \n        if self.counter >= self.patience:\n            if self.restore_best_weights and self.best_weights:\n                model.load_state_dict({k: v.to(next(model.parameters()).device) \n                                     for k, v in self.best_weights.items()})\n            return True\n        return False\n\nclass FocalLoss(nn.Module):\n    \"\"\"Focal Loss for handling class imbalance\"\"\"\n    def __init__(self, alpha=1, gamma=2):\n        super(FocalLoss, self).__init__()\n        self.alpha = alpha\n        self.gamma = gamma\n\n    def forward(self, inputs, targets):\n        ce_loss = nn.CrossEntropyLoss()(inputs, targets)\n        pt = torch.exp(-ce_loss)\n        focal_loss = self.alpha * (1-pt)**self.gamma * ce_loss\n        return focal_loss\n\ndef get_enhanced_optimizer(model, config):\n    \"\"\"Get enhanced optimizer with different learning rates for different parts\"\"\"\n    # Different learning rates for backbone and head\n    backbone_params = []\n    head_params = []\n    \n    for name, param in model.named_parameters():\n        if 'backbone' in name:\n            backbone_params.append(param)\n        else:\n            head_params.append(param)\n    \n    optimizer = torch.optim.AdamW([\n        {'params': backbone_params, 'lr': config['backbone_lr']},\n        {'params': head_params, 'lr': config['head_lr']}\n    ], weight_decay=config['weight_decay'])\n    \n    return optimizer\n\ndef plot_enhanced_training_curves(train_losses, val_losses, learning_rates, detection_stats=None):\n    \"\"\"Enhanced plotting with more metrics\"\"\"\n    fig, axes = plt.subplots(2, 3, figsize=(20, 12))\n    \n    epochs = range(1, len(train_losses) + 1)\n    \n    # Loss curves\n    axes[0,0].plot(epochs, train_losses, 'b-', label='Training Loss', marker='o', linewidth=2)\n    axes[0,0].plot(epochs, val_losses, 'r-', label='Validation Loss', marker='s', linewidth=2)\n    axes[0,0].set_title('Training and Validation Loss', fontsize=14, fontweight='bold')\n    axes[0,0].set_xlabel('Epoch')\n    axes[0,0].set_ylabel('Loss')\n    axes[0,0].legend()\n    axes[0,0].grid(True, alpha=0.3)\n    \n    # Loss difference\n    loss_diff = [val - train for val, train in zip(val_losses, train_losses)]\n    axes[0,1].plot(epochs, loss_diff, 'g-', marker='^', linewidth=2)\n    axes[0,1].set_title('Validation - Training Loss', fontsize=14, fontweight='bold')\n    axes[0,1].set_xlabel('Epoch')\n    axes[0,1].set_ylabel('Loss Difference')\n    axes[0,1].grid(True, alpha=0.3)\n    axes[0,1].axhline(y=0, color='k', linestyle='--', alpha=0.5)\n    \n    # Learning rate\n    axes[0,2].plot(epochs, learning_rates, 'purple', marker='d', linewidth=2)\n    axes[0,2].set_title('Learning Rate Schedule', fontsize=14, fontweight='bold')\n    axes[0,2].set_xlabel('Epoch')\n    axes[0,2].set_ylabel('Learning Rate')\n    axes[0,2].set_yscale('log')\n    axes[0,2].grid(True, alpha=0.3)\n    \n    # Validation loss trend with moving average\n    if len(val_losses) > 3:\n        moving_avg = np.convolve(val_losses, np.ones(3)/3, mode='valid')\n        axes[1,0].plot(epochs, val_losses, 'r-', marker='s', alpha=0.7, label='Validation Loss')\n        axes[1,0].plot(epochs[1:-1], moving_avg, 'orange', linewidth=3, label='Moving Average')\n        axes[1,0].legend()\n    else:\n        axes[1,0].plot(epochs, val_losses, 'r-', marker='s', linewidth=2)\n    axes[1,0].set_title('Validation Loss Trend', fontsize=14, fontweight='bold')\n    axes[1,0].set_xlabel('Epoch')\n    axes[1,0].set_ylabel('Validation Loss')\n    axes[1,0].grid(True, alpha=0.3)\n    \n    # Training efficiency\n    if len(train_losses) > 1:\n        efficiency = [(train_losses[0] - loss) / train_losses[0] * 100 for loss in train_losses]\n        axes[1,1].plot(epochs, efficiency, 'brown', marker='*', linewidth=2)\n        axes[1,1].set_title('Training Efficiency (%)', fontsize=14, fontweight='bold')\n        axes[1,1].set_xlabel('Epoch')\n        axes[1,1].set_ylabel('Loss Reduction (%)')\n        axes[1,1].grid(True, alpha=0.3)\n    \n    # Loss ratio\n    loss_ratio = [val/train if train > 0 else 1 for val, train in zip(val_losses, train_losses)]\n    axes[1,2].plot(epochs, loss_ratio, 'teal', marker='h', linewidth=2)\n    axes[1,2].set_title('Validation/Training Loss Ratio', fontsize=14, fontweight='bold')\n    axes[1,2].set_xlabel('Epoch')\n    axes[1,2].set_ylabel('Ratio')\n    axes[1,2].grid(True, alpha=0.3)\n    axes[1,2].axhline(y=1, color='k', linestyle='--', alpha=0.5, label='Ideal Ratio')\n    axes[1,2].legend()\n    \n    plt.tight_layout()\n    plt.savefig('training_curves.png', dpi=300, bbox_inches='tight')\n    plt.show()\n\ndef main():\n    # Enhanced configuration\n    config = {\n        'device': torch.device('cuda' if torch.cuda.is_available() else 'cpu'),\n        'backbone_lr': 5e-6,  # Lower LR for backbone\n        'head_lr': 2e-5,      # Higher LR for head\n        'weight_decay': 1e-4,\n        'batch_size': 8,      # Increased batch size\n        'num_epochs': 30,     # More epochs\n        'warmup_epochs': 3,\n        'max_train_samples': None,  # Use all samples\n        'max_val_samples': 2000,    # Limit validation for speed\n        'gradient_clip': 1.0,\n        'use_mixed_precision': True,\n    }\n    \n    print(f\"🚀 Using device: {config['device']}\")\n    print(f\"🔧 Configuration: {config}\")\n\n    # Paths\n    TRAIN_IMAGES_DIR = \"/kaggle/input/imagenet-object-localization-challenge/ILSVRC/Data/CLS-LOC/train\"\n    VAL_IMAGES_DIR = \"/kaggle/input/imagenet-object-localization-challenge/ILSVRC/Data/CLS-LOC/val\"\n    TRAIN_COCO_JSON = \"/kaggle/working/coco_format_train_fixed.json\"\n    VAL_COCO_JSON = \"/kaggle/working/coco_format_val.json\"\n\n    # Enhanced processor with better settings\n    processor = DetrImageProcessor.from_pretrained(\n        \"facebook/detr-resnet-50\",\n        size={\"shortest_edge\": 800, \"longest_edge\": 1333},\n        do_resize=True,\n        do_normalize=True,\n        do_pad=True,\n        do_rescale=True,\n    )\n    \n    # Load model with all classes\n    model = DetrForObjectDetection.from_pretrained(\n        \"facebook/detr-resnet-50\",\n        num_labels=91,  # COCO has 91 classes (including background)\n        ignore_mismatched_sizes=True\n    )\n    model.to(config['device'])\n    \n    print(f\"📊 Model initialized with {model.config.num_labels} classes\")\n\n    # Enhanced datasets\n    train_dataset = EnhancedCOCODataset(\n        images_dir=TRAIN_IMAGES_DIR, \n        annotation_json=TRAIN_COCO_JSON, \n        processor=processor,\n        use_augmentation=True,\n        max_samples=config['max_train_samples']\n    )\n    \n    val_dataset = EnhancedCOCODataset(\n        images_dir=VAL_IMAGES_DIR, \n        annotation_json=VAL_COCO_JSON, \n        processor=processor,\n        use_augmentation=False,\n        max_samples=config['max_val_samples']\n    )\n\n    # Enhanced data loaders\n    train_loader = DataLoader(\n        train_dataset, \n        batch_size=config['batch_size'], \n        shuffle=True, \n        collate_fn=enhanced_collate_fn,\n        num_workers=2,\n        pin_memory=True,\n        drop_last=True\n    )\n    \n    val_loader = DataLoader(\n        val_dataset, \n        batch_size=config['batch_size'], \n        shuffle=False, \n        collate_fn=enhanced_collate_fn,\n        num_workers=2,\n        pin_memory=True\n    )\n\n    # Enhanced optimizer and scheduler\n    optimizer = get_enhanced_optimizer(model, config)\n    \n    # Cosine annealing with warm restarts\n    scheduler = torch.optim.lr_scheduler.CosineAnnealingWarmRestarts(\n        optimizer, T_0=10, T_mult=2, eta_min=1e-7\n    )\n    \n    # Advanced early stopping\n    early_stopping = AdvancedEarlyStopping(\n        patience=8, \n        min_delta=0.0001, \n        restore_best_weights=True\n    )\n    \n    # Training state\n    train_losses = []\n    val_losses = []\n    learning_rates = []\n    best_val_loss = float('inf')\n    start_epoch = 0\n\n    # Mixed precision scaler\n    scaler = GradScaler('cuda') if config['use_mixed_precision'] else None\n\n    print(\"🎯 Starting enhanced training with validation monitoring...\")\n    print(f\"📊 Training samples: {len(train_dataset)}\")\n    print(f\"📊 Validation samples: {len(val_dataset)}\")\n    print(f\"🔄 Batches per epoch: {len(train_loader)}\")\n\n    for epoch in range(start_epoch, config['num_epochs']):\n        model.train()\n        running_loss = 0.0\n        num_train_batches = 0\n        \n        # Warmup learning rate\n        if epoch < config['warmup_epochs']:\n            warmup_factor = (epoch + 1) / config['warmup_epochs']\n            for param_group in optimizer.param_groups:\n                param_group['lr'] = param_group['lr'] * warmup_factor\n        \n        progress_bar = tqdm(train_loader, desc=f\"Epoch {epoch+1}/{config['num_epochs']} [Training]\")\n        \n        for batch_idx, batch in enumerate(progress_bar):\n            if batch is None:\n                continue\n\n            try:\n                optimizer.zero_grad()\n\n                pixel_values = batch['pixel_values'].to(config['device'])\n                pixel_mask = batch['pixel_mask'].to(config['device'])\n                labels = batch['labels']\n\n                # Move labels to device\n                for label in labels:\n                    for k, v in label.items():\n                        if isinstance(v, torch.Tensor):\n                            label[k] = v.to(config['device'])\n\n                # Forward pass with mixed precision\n                if config['use_mixed_precision']:\n                    with autocast('cuda'):\n                        outputs = model(pixel_values=pixel_values, pixel_mask=pixel_mask, labels=labels)\n                        loss = outputs.loss\n\n                    scaler.scale(loss).backward()\n                    \n                    # Gradient clipping\n                    if config['gradient_clip'] > 0:\n                        scaler.unscale_(optimizer)\n                        torch.nn.utils.clip_grad_norm_(model.parameters(), config['gradient_clip'])\n                    \n                    scaler.step(optimizer)\n                    scaler.update()\n                else:\n                    outputs = model(pixel_values=pixel_values, pixel_mask=pixel_mask, labels=labels)\n                    loss = outputs.loss\n                    loss.backward()\n                    \n                    if config['gradient_clip'] > 0:\n                        torch.nn.utils.clip_grad_norm_(model.parameters(), config['gradient_clip'])\n                    \n                    optimizer.step()\n\n                running_loss += loss.item()\n                num_train_batches += 1\n                \n                # Update progress bar\n                if batch_idx % 5 == 0:\n                    avg_loss = running_loss / num_train_batches\n                    current_lr = optimizer.param_groups[0]['lr']\n                    progress_bar.set_postfix({\n                        'Loss': f'{avg_loss:.4f}',\n                        'LR': f'{current_lr:.2e}'\n                    })\n                    \n            except Exception as e:\n                print(f\"Training batch error: {e}\")\n                continue\n\n        # Calculate average training loss\n        avg_train_loss = running_loss / num_train_batches if num_train_batches > 0 else float('inf')\n        train_losses.append(avg_train_loss)\n\n        print(f\"\\n📊 Epoch {epoch+1} - Training Loss: {avg_train_loss:.4f}\")\n\n        # Validation\n        val_loss = validate_model(model, val_loader, config['device'])\n        val_losses.append(val_loss)\n\n        print(f\"📊 Epoch {epoch+1} - Validation Loss: {val_loss:.4f}\")\n\n        # Learning rate tracking\n        current_lr = optimizer.param_groups[0]['lr']\n        learning_rates.append(current_lr)\n        print(f\"📊 Epoch {epoch+1} - Learning Rate: {current_lr:.2e}\")\n\n        # Update scheduler\n        if epoch >= config['warmup_epochs']:\n            scheduler.step()\n\n        # Save best model\n        if val_loss < best_val_loss:\n            best_val_loss = val_loss\n            torch.save({\n                'epoch': epoch,\n                'model_state_dict': model.state_dict(),\n                'optimizer_state_dict': optimizer.state_dict(),\n                'scheduler_state_dict': scheduler.state_dict(),\n                'best_val_loss': best_val_loss,\n                'train_losses': train_losses,\n                'val_losses': val_losses,\n            }, 'best_model_enhanced.pth')\n            print(f\"✅ New best validation loss: {best_val_loss:.4f} - Model saved!\")\n\n        # Monitor overfitting\n        if epoch > 0:\n            loss_diff = val_loss - avg_train_loss\n            print(f\"📊 Epoch {epoch+1} - Loss Difference (Val-Train): {loss_diff:.4f}\")\n            if loss_diff > 0.15:\n                print(\"⚠️  Strong overfitting detected!\")\n            elif loss_diff > 0.05:\n                print(\"⚠️  Mild overfitting detected\")\n\n        # Early stopping check\n        if early_stopping(val_loss, model, epoch):\n            print(f\"🛑 Early stopping triggered after epoch {epoch+1}\")\n            break\n\n        print(\"-\" * 70)\n\n    print(\"🎉 Training completed!\")\n    print(f\"🏆 Best validation loss: {best_val_loss:.4f}\")\n\n    # Plot enhanced training curves\n    plot_enhanced_training_curves(train_losses, val_losses, learning_rates)\n\n    # Load best model for final evaluation\n    if os.path.exists('best_model_enhanced.pth'):\n        checkpoint = torch.load('best_model_enhanced.pth')\n        model.load_state_dict(checkpoint['model_state_dict'])\n        print(\"✅ Best model loaded for final evaluation.\")\n\n    # Save final model in different formats\n    torch.save(model.state_dict(), 'final_model_weights.pth')\n    print(\"💾 Final model weights saved!\")\n\nif __name__ == \"__main__\":\n    main()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-16T10:14:30.748794Z","iopub.execute_input":"2025-06-16T10:14:30.7495Z","iopub.status.idle":"2025-06-16T11:32:46.187801Z","shell.execute_reply.started":"2025-06-16T10:14:30.74947Z","shell.execute_reply":"2025-06-16T11:32:46.18708Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport torch\nfrom torchvision import transforms\nfrom PIL import Image\nfrom transformers import DetrImageProcessor, DetrForObjectDetection\nfrom tqdm import tqdm\n\n# إعداد النموذج\ndevice = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n\n# تحميل النموذج المدرب\nmodel = DetrForObjectDetection.from_pretrained(\n    \"facebook/detr-resnet-50\",\n    num_labels=91,  # COCO has 91 classes (including background)\n    ignore_mismatched_sizes=True\n)\nmodel.load_state_dict(torch.load('best_model_enhanced.pth')['model_state_dict'])\nmodel.to(device)\nmodel.eval()  # وضع النموذج في وضع التقييم\n\n# إعداد المعالج\nprocessor = DetrImageProcessor.from_pretrained(\n    \"facebook/detr-resnet-50\",\n    size={\"shortest_edge\": 800, \"longest_edge\": 1333},\n    do_resize=True,\n    do_normalize=True,\n    do_pad=True,\n    do_rescale=True,\n)\n\n# مسار مجلد الاختبار\ntest_images_dir = '/kaggle/input/imagenet-object-localization-challenge/ILSVRC/Data/CLS-LOC/test/'\n\n# قائمة لتخزين النتائج\nresults = []\n\n# اختبار الصور\nfor image_name in os.listdir(test_images_dir):\n    if image_name.endswith('.JPEG'):\n        image_path = os.path.join(test_images_dir, image_name)\n        image = Image.open(image_path).convert(\"RGB\")  # فتح الصورة\n\n        # معالجة الصورة\n        encoding = processor(images=image, return_tensors=\"pt\", do_pad=True, do_resize=True)\n        pixel_values = encoding['pixel_values'].to(device)\n\n        # إجراء التنبؤ\n        with torch.no_grad():\n            outputs = model(pixel_values=pixel_values)\n\n        # معالجة النتائج\n        logits = outputs.logits\n        bboxes = outputs.pred_boxes\n\n        # الحصول على الفئات المتوقعة\n        predicted_classes = logits.argmax(-1).cpu().numpy()\n        predicted_boxes = bboxes.cpu().numpy()\n\n        # تخزين النتائج\n        results.append((image_name, predicted_classes, predicted_boxes))\n\n# طباعة النتائج\nfor image_name, predicted_classes, predicted_boxes in results:\n    print(f\"Image: {image_name}, Predicted Classes: {predicted_classes}, Predicted Boxes: {predicted_boxes}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-16T12:15:02.133757Z","iopub.execute_input":"2025-06-16T12:15:02.134488Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\nimport os\nimport json\nimport torch\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport matplotlib.patches as patches\nfrom torch.utils.data import Dataset, DataLoader\nfrom PIL import Image\nfrom transformers import DetrImageProcessor, DetrForObjectDetection\nfrom tqdm import tqdm\nimport glob\nimport warnings\nwarnings.filterwarnings('ignore')\n\nclass ImageNetTestDataset(Dataset):\n    \"\"\"Dataset for test images without annotations\"\"\"\n    \n    def __init__(self, test_images_dir, processor, transforms=None):\n        super().__init__()\n        self.test_images_dir = test_images_dir\n        self.processor = processor\n        self.transforms = transforms\n        \n        # Get all test images\n        self.image_paths = glob.glob(os.path.join(test_images_dir, \"*.JPEG\"))\n        self.image_paths.sort()  # Ensure consistent ordering\n        \n        print(f\"Found {len(self.image_paths)} test images\")\n        \n        # Limit to first 100 images for testing (remove this line to process all)\n        self.image_paths = self.image_paths[:100]\n        print(f\"Processing first {len(self.image_paths)} images for demo\")\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        image_name = os.path.basename(img_path)\n        \n        try:\n            image = Image.open(img_path).convert(\"RGB\")\n        except Exception as e:\n            print(f\"Error loading image {img_path}: {e}\")\n            return None\n        \n        if self.transforms:\n            image = self.transforms(image)\n        \n        # Process image for DETR\n        encoding = self.processor(\n            images=image,\n            return_tensors=\"pt\",\n            do_pad=True,\n            pad_and_return_pixel_mask=True,\n        )\n        \n        return {\n            'pixel_values': encoding['pixel_values'].squeeze(0),\n            'pixel_mask': encoding['pixel_mask'].squeeze(0),\n            'image_name': image_name,\n            'image_path': img_path\n        }\n\ndef test_collate_fn(batch):\n    \"\"\"Collate function for test dataset\"\"\"\n    batch = [item for item in batch if item is not None]\n    if len(batch) == 0:\n        return None\n\n    max_h = max([item['pixel_values'].shape[1] for item in batch])\n    max_w = max([item['pixel_values'].shape[2] for item in batch])\n\n    pixel_values = []\n    pixel_mask = []\n    image_names = []\n    image_paths = []\n\n    for item in batch:\n        pv = item['pixel_values']\n        pm = item['pixel_mask']\n\n        pad_h = max_h - pv.shape[1]\n        pad_w = max_w - pv.shape[2]\n\n        if pad_h > 0 or pad_w > 0:\n            pv = torch.nn.functional.pad(pv, (0, pad_w, 0, pad_h), value=0)\n            pm = torch.nn.functional.pad(pm, (0, pad_w, 0, pad_h), value=0)\n\n        pixel_values.append(pv)\n        pixel_mask.append(pm)\n        image_names.append(item['image_name'])\n        image_paths.append(item['image_path'])\n\n    pixel_values = torch.stack(pixel_values)\n    pixel_mask = torch.stack(pixel_mask)\n\n    return {\n        'pixel_values': pixel_values,\n        'pixel_mask': pixel_mask,\n        'image_names': image_names,\n        'image_paths': image_paths\n    }\n\nclass TestInferenceEngine:\n    \"\"\"Engine for running inference on test images and generating visualizations\"\"\"\n    \n    def __init__(self, model, processor, device, confidence_threshold=0.3):\n        self.model = model\n        self.processor = processor\n        self.device = device\n        self.confidence_threshold = confidence_threshold\n        \n        # Your class names mapping (update with your actual classes)\n        self.class_names = {\n            1: \"dog\", \n            2: \"cat\", \n            3: \"ant\", \n            4: \"zebra\", \n            5: \"elephant\"\n        }\n        \n    def run_inference(self, test_loader, save_predictions=True, visualize_samples=True):\n        \"\"\"Run inference on test dataset\"\"\"\n        print(f\"🔍 Running inference with confidence threshold: {self.confidence_threshold}\")\n        print(\"🔮 Starting model predictions...\")\n        \n        self.model.eval()\n        all_predictions = []\n        \n        with torch.no_grad():\n            for batch_idx, batch in enumerate(tqdm(test_loader, desc=\"Processing test images\")):\n                if batch is None:\n                    continue\n                \n                pixel_values = batch['pixel_values'].to(self.device)\n                pixel_mask = batch['pixel_mask'].to(self.device)\n                image_names = batch['image_names']\n                image_paths = batch['image_paths']\n                \n                # Get model predictions\n                outputs = self.model(pixel_values=pixel_values, pixel_mask=pixel_mask)\n                \n                # Process predictions for each image in batch\n                for i in range(len(image_names)):\n                    predictions = self._process_single_prediction(\n                        outputs, i, image_names[i], image_paths[i]\n                    )\n                    all_predictions.append(predictions)\n                    \n                    # Print progress for first few predictions\n                    if len(all_predictions) <= 10:\n                        print(f\"✅ Processed {image_names[i]}: {predictions['num_detections']} detections\")\n                \n                # Visualize first few batches\n                if visualize_samples and batch_idx < 3:\n                    self._visualize_batch_predictions(batch, outputs, batch_idx)\n        \n        if save_predictions:\n            self._save_predictions(all_predictions)\n        \n        # Generate summary statistics\n        self._generate_inference_summary(all_predictions)\n        \n        return all_predictions\n    \n    def _process_single_prediction(self, outputs, batch_idx, image_name, image_path):\n        \"\"\"Process predictions for a single image\"\"\"\n        # Extract predictions for this image\n        logits = outputs.logits[batch_idx]\n        boxes = outputs.pred_boxes[batch_idx]\n        \n        # Apply softmax to get probabilities\n        probabilities = torch.nn.functional.softmax(logits, -1)\n        \n        # Get confidence scores and predicted classes\n        scores, predicted_classes = probabilities[:, :-1].max(-1)\n        \n        # Filter by confidence threshold\n        keep = scores > self.confidence_threshold\n        \n        filtered_scores = scores[keep].cpu().numpy()\n        filtered_classes = predicted_classes[keep].cpu().numpy()\n        filtered_boxes = boxes[keep].cpu().numpy()\n        \n        # Convert boxes from normalized to pixel coordinates\n        image = Image.open(image_path)\n        img_w, img_h = image.size\n        \n        # Convert DETR box format (center_x, center_y, width, height) to (x1, y1, x2, y2)\n        converted_boxes = []\n        for box in filtered_boxes:\n            center_x, center_y, width, height = box\n            x1 = (center_x - width/2) * img_w\n            y1 = (center_y - height/2) * img_h\n            x2 = (center_x + width/2) * img_w\n            y2 = (center_y + height/2) * img_h\n            converted_boxes.append([float(x1), float(y1), float(x2), float(y2)])\n        \n        return {\n            'image_name': image_name,\n            'image_path': image_path,\n            'image_size': (img_w, img_h),\n            'predictions': {\n                'boxes': converted_boxes,\n                'scores': filtered_scores.tolist(),\n                'classes': filtered_classes.tolist(),\n                'class_names': [self.class_names.get(int(cls), f'class_{cls}') for cls in filtered_classes]\n            },\n            'num_detections': len(filtered_scores)\n        }\n    \n    def _visualize_batch_predictions(self, batch, outputs, batch_idx):\n        \"\"\"Visualize predictions for a batch\"\"\"\n        print(f\"🎨 Creating visualization for batch {batch_idx + 1}...\")\n        \n        fig, axes = plt.subplots(1, min(len(batch['image_names']), 4), figsize=(20, 5))\n        if not isinstance(axes, np.ndarray):\n            axes = [axes]\n        \n        for i, ax in enumerate(axes):\n            if i >= len(batch['image_names']):\n                break\n                \n            # Load and display image\n            image = Image.open(batch['image_paths'][i])\n            ax.imshow(image)\n            \n            # Get predictions for this image\n            logits = outputs.logits[i]\n            boxes = outputs.pred_boxes[i]\n            probabilities = torch.nn.functional.softmax(logits, -1)\n            scores, predicted_classes = probabilities[:, :-1].max(-1)\n            \n            # Filter and draw boxes\n            keep = scores > self.confidence_threshold\n            num_detections = keep.sum().item()\n            \n            if num_detections > 0:\n                filtered_boxes = boxes[keep].cpu().numpy()\n                filtered_scores = scores[keep].cpu().numpy()\n                filtered_classes = predicted_classes[keep].cpu().numpy()\n                \n                img_w, img_h = image.size\n                \n                colors = ['red', 'blue', 'green', 'yellow', 'purple', 'orange']\n                \n                for idx, (box, score, cls) in enumerate(zip(filtered_boxes, filtered_scores, filtered_classes)):\n                    center_x, center_y, width, height = box\n                    x1 = (center_x - width/2) * img_w\n                    y1 = (center_y - height/2) * img_h\n                    box_w = width * img_w\n                    box_h = height * img_h\n                    \n                    color = colors[idx % len(colors)]\n                    \n                    # Draw bounding box\n                    rect = patches.Rectangle(\n                        (x1, y1), box_w, box_h,\n                        linewidth=3, edgecolor=color, facecolor='none'\n                    )\n                    ax.add_patch(rect)\n                    \n                    # Add label\n                    class_name = self.class_names.get(int(cls), f'class_{cls}')\n                    ax.text(x1, y1-5, f'{class_name}: {score:.2f}',\n                           bbox=dict(boxstyle=\"round,pad=0.3\", facecolor=color, alpha=0.7),\n                           fontsize=10, color='white', weight='bold')\n            \n            ax.set_title(f'{batch[\"image_names\"][i]}\\n{num_detections} detections', fontsize=12)\n            ax.axis('off')\n        \n        plt.suptitle(f'Batch {batch_idx + 1} - Model Predictions', fontsize=16, fontweight='bold')\n        plt.tight_layout()\n        \n        # Save the plot\n        filename = f'batch_{batch_idx + 1}_predictions.png'\n        plt.savefig(filename, dpi=300, bbox_inches='tight')\n        print(f\"📸 Saved visualization: {filename}\")\n        plt.show()\n    \n    def _save_predictions(self, all_predictions):\n        \"\"\"Save all predictions to JSON file\"\"\"\n        output_file = 'test_predictions.json'\n        \n        with open(output_file, 'w') as f:\n            json.dump(all_predictions, f, indent=2)\n        \n        print(f\"💾 Predictions saved to: {output_file}\")\n    \n    def _generate_inference_summary(self, all_predictions):\n        \"\"\"Generate summary statistics from inference results\"\"\"\n        print(\"\\n\" + \"=\"*60)\n        print(\"📊 INFERENCE SUMMARY\")\n        print(\"=\"*60)\n        \n        total_images = len(all_predictions)\n        total_detections = sum(pred['num_detections'] for pred in all_predictions)\n        images_with_detections = sum(1 for pred in all_predictions if pred['num_detections'] > 0)\n        \n        print(f\"🖼️  Total images processed: {total_images}\")\n        print(f\"✅ Images with detections: {images_with_detections} ({images_with_detections/total_images*100:.1f}%)\")\n        print(f\"🎯 Total detections: {total_detections}\")\n        print(f\"📈 Average detections per image: {total_detections/total_images:.2f}\")\n        \n        # Class distribution\n        class_counts = {}\n        confidence_scores = []\n        \n        for pred in all_predictions:\n            for cls, score in zip(pred['predictions']['classes'], pred['predictions']['scores']):\n                class_name = self.class_names.get(cls, f'class_{cls}')\n                class_counts[class_name] = class_counts.get(class_name, 0) + 1\n                confidence_scores.append(score)\n        \n        if class_counts:\n            print(f\"\\n🏷️  Class Distribution:\")\n            for class_name, count in sorted(class_counts.items(), key=lambda x: x[1], reverse=True):\n                print(f\"   {class_name}: {count} detections\")\n        \n        if confidence_scores:\n            print(f\"\\n📊 Confidence Statistics:\")\n            print(f\"   Mean confidence: {np.mean(confidence_scores):.3f}\")\n            print(f\"   Std confidence: {np.std(confidence_scores):.3f}\")\n            print(f\"   Min confidence: {np.min(confidence_scores):.3f}\")\n            print(f\"   Max confidence: {np.max(confidence_scores):.3f}\")\n            \n            # Plot confidence distribution\n            print(\"📊 Creating confidence distribution plot...\")\n            plt.figure(figsize=(12, 6))\n            plt.hist(confidence_scores, bins=50, alpha=0.7, edgecolor='black', color='skyblue')\n            plt.axvline(np.mean(confidence_scores), color='red', linestyle='--', linewidth=2,\n                       label=f'Mean: {np.mean(confidence_scores):.3f}')\n            plt.axvline(self.confidence_threshold, color='orange', linestyle='--', linewidth=2,\n                       label=f'Threshold: {self.confidence_threshold}')\n            plt.xlabel('Confidence Score', fontsize=12)\n            plt.ylabel('Frequency', fontsize=12)\n            plt.title('Test Set Confidence Score Distribution', fontsize=14, fontweight='bold')\n            plt.legend(fontsize=12)\n            plt.grid(True, alpha=0.3)\n            plt.savefig('test_confidence_distribution.png', dpi=300, bbox_inches='tight')\n            print(\"📸 Saved: test_confidence_distribution.png\")\n            plt.show()\n\ndef main():\n    \"\"\"Main function to run evaluation on test dataset\"\"\"\n    \n    print(\"🚀 STARTING TEST DATASET EVALUATION\")\n    print(\"=\"*60)\n    print(\"🎯 This script will:\")\n    print(\"   1. Load your trained DETR model\")\n    print(\"   2. Process test images\")\n    print(\"   3. Generate predictions and visualizations\")\n    print(\"   4. Create summary statistics\")\n    print(\"=\"*60)\n    \n    # Setup device\n    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n    print(f\"🔧 Using device: {device}\")\n    \n    # IMPORTANT: Update these paths!\n    TEST_IMAGES_DIR = \"/kaggle/input/imagenet-object-localization-challenge/ILSVRC/Data/CLS-LOC/test\"\n    MODEL_PATH = \"/kaggle/working/best_model.pth\"  # Update this path!\n    \n    print(f\"📂 Test images directory: {TEST_IMAGES_DIR}\")\n    print(f\"🤖 Model path: {MODEL_PATH}\")\n    \n    # Check if paths exist\n    if not os.path.exists(TEST_IMAGES_DIR):\n        print(f\"❌ ERROR: Test images directory not found: {TEST_IMAGES_DIR}\")\n        return\n    \n    # Load processor and model\n    print(\"🔄 Loading DETR processor and model...\")\n    processor = DetrImageProcessor.from_pretrained(\n        \"facebook/detr-resnet-50\",\n        size={\"shortest_edge\": 800, \"longest_edge\": 1333},\n        do_resize=True,\n    )\n    model = DetrForObjectDetection.from_pretrained(\"facebook/detr-resnet-50\")\n    num_labels=len(SELECTED_CLASSES) + 1,  # Adjust for your selected classes + background\n      ignore_mismatched_sizes=True\n    \n    # Load trained weights if available\n    if os.path.exists(MODEL_PATH):\n        print(f\"✅ Loading trained weights from: {MODEL_PATH}\")\n        model.load_state_dict(torch.load(MODEL_PATH, map_location=device))\n        print(\"✅ Successfully loaded trained model!\")\n    else:\n        print(f\"⚠️  Model weights not found at {MODEL_PATH}\")\n        print(\"📝 Using pretrained DETR weights (not fine-tuned)\")\n        print(\"💡 Make sure your model path is correct!\")\n    \n    model.to(device)\n    model.eval()\n    print(\"🎯 Model ready for inference!\")\n    \n    # Create test dataset\n    print(\"📁 Creating test dataset...\")\n    test_dataset = ImageNetTestDataset(TEST_IMAGES_DIR, processor)\n    \n    if len(test_dataset) == 0:\n        print(\"❌ ERROR: No test images found!\")\n        return\n    \n    test_loader = DataLoader(\n        test_dataset, \n        batch_size=2,  # Small batch size to avoid memory issues\n        shuffle=False, \n        collate_fn=test_collate_fn,\n        num_workers=0  # Set to 0 to avoid multiprocessing issues in Kaggle\n    )\n    \n    print(f\"✅ Test dataset ready with {len(test_dataset)} images\")\n    \n    # Run inference\n    print(\"🚀 Starting inference...\")\n    inference_engine = TestInferenceEngine(\n        model, processor, device, confidence_threshold=0.3  # Lower threshold to see more detections\n    )\n    \n    try:\n        predictions = inference_engine.run_inference(\n            test_loader, \n            save_predictions=True, \n            visualize_samples=True\n        )\n        \n        print(\"\\n🎉 EVALUATION COMPLETED SUCCESSFULLY!\")\n        print(\"📁 Check the following files:\")\n        print(\"   - test_predictions.json (all predictions)\")\n        print(\"   - batch_*_predictions.png (visualizations)\")\n        print(\"   - test_confidence_distribution.png (confidence analysis)\")\n        \n    except Exception as e:\n        print(f\"❌ ERROR during inference: {e}\")\n        import traceback\n        traceback.print_exc()\n\nif __name__ == \"__main__\":\n    main()\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-16T08:53:34.999762Z","iopub.execute_input":"2025-06-16T08:53:35.000014Z","iopub.status.idle":"2025-06-16T08:53:57.330507Z","shell.execute_reply.started":"2025-06-16T08:53:34.999986Z","shell.execute_reply":"2025-06-16T08:53:57.329756Z"}},"outputs":[],"execution_count":null}]}