{"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":"gpu","dataSources":[{"sourceId":21154,"databundleVersionId":1243559,"sourceType":"competition"}],"dockerImageVersionId":31155,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"!pip install tfrecord\nimport io\nimport os\nimport glob\nimport torch\nimport pandas as pd\nimport numpy as np\nfrom PIL import Image\nfrom torch.utils.data import IterableDataset, DataLoader\nfrom torchvision import transforms, models\nfrom tfrecord.torch.dataset import TFRecordDataset\nfrom torch.cuda.amp import autocast, GradScaler\nimport torch.nn as nn\nimport warnings\nwarnings.filterwarnings('ignore')\n\n# --------- CONFIGURATION ---------\nIMAGE_SIZE = 224  # Fast training with 224x224\nBASE_PATH = '/kaggle/input/tpu-getting-started'\nTFRECORD_DIR = f'{BASE_PATH}/tfrecords-jpeg-{IMAGE_SIZE}x{IMAGE_SIZE}'\n\nprint(\"=\"*70)\nprint(\"KAGGLE FLOWERS CLASSIFICATION - FAST TRAINING MODE\")\nprint(\"=\"*70)\nprint(f\"\\nConfiguration:\")\nprint(f\"  Image Size: {IMAGE_SIZE}x{IMAGE_SIZE}\")\n\n# Get .tfrec files\nTRAIN_TFRECORDS = sorted(glob.glob(f'{TFRECORD_DIR}/train/*.tfrec'))\nTEST_TFRECORDS = sorted(glob.glob(f'{TFRECORD_DIR}/test/*.tfrec'))\n\nprint(f\"\\nDataset:\")\nprint(f\"  Train files: {len(TRAIN_TFRECORDS)}\")\nprint(f\"  Test files:  {len(TEST_TFRECORDS)}\")\n\n# Optimized hyperparameters\nNUM_CLASSES = 104\nBATCH_SIZE = 64\nNUM_EPOCHS = 15\nLEARNING_RATE = 3e-4\nWEIGHT_DECAY = 1e-4\n\n# Device setup\nDEVICE = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nprint(f\"\\nDevice: {DEVICE}\")\nif torch.cuda.is_available():\n    print(f\"  GPU: {torch.cuda.get_device_name(0)}\")\n\nprint(f\"\\nHyperparameters:\")\nprint(f\"  Batch Size: {BATCH_SIZE}\")\nprint(f\"  Epochs: {NUM_EPOCHS}\")\nprint(f\"  Learning Rate: {LEARNING_RATE}\")\n\n# --------- DATASET WITH PROPER TENSOR CONVERSION ---------\nclass TFRecordIterableDataset(IterableDataset):\n    def __init__(self, tfrecord_paths, transform=None, is_test=False):\n        self.tfrecord_paths = tfrecord_paths\n        self.transform = transform\n        self.is_test = is_test\n        \n        if is_test:\n            self.description = {\"image\": \"byte\", \"id\": \"byte\"}\n        else:\n            self.description = {\"image\": \"byte\", \"class\": \"int\", \"id\": \"byte\"}\n    \n    def __iter__(self):\n        for tfr_path in self.tfrecord_paths:\n            try:\n                dataset = TFRecordDataset(tfr_path, index_path=None, \n                                         description=self.description)\n                \n                for example in dataset:\n                    try:\n                        img = Image.open(io.BytesIO(example[\"image\"])).convert('RGB')\n                        \n                        if self.transform:\n                            img = self.transform(img)\n                        \n                        if self.is_test:\n                            img_id = example[\"id\"].decode('utf-8')\n                            yield img, img_id\n                        else:\n                            # Convert label to Python int (not numpy array)\n                            label = int(example[\"class\"])\n                            yield img, label\n                    except Exception as e:\n                        continue\n            except Exception as e:\n                continue\n\n# --------- DATA TRANSFORMS ---------\ntrain_transform = transforms.Compose([\n    transforms.Resize((IMAGE_SIZE, IMAGE_SIZE)),\n    transforms.RandomHorizontalFlip(p=0.5),\n    transforms.RandomRotation(15),\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\ntest_transform = transforms.Compose([\n    transforms.Resize((IMAGE_SIZE, IMAGE_SIZE)),\n    transforms.ToTensor(),\n    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])\n])\n\n# --------- DATALOADERS (num_workers=0 to avoid multiprocessing issues) ---------\nprint(f\"\\n{'='*70}\")\nprint(\"CREATING DATALOADERS\")\nprint(\"=\"*70)\n\ntrain_dataset = TFRecordIterableDataset(TRAIN_TFRECORDS, transform=train_transform, is_test=False)\ntest_dataset = TFRecordIterableDataset(TEST_TFRECORDS, transform=test_transform, is_test=True)\ntrain_loader = DataLoader(\n    train_dataset, \n    batch_size=BATCH_SIZE,\n    num_workers=0,  # Fixed: avoid multiprocessing issues\n    pin_memory=True if torch.cuda.is_available() else False\n)\n\ntest_loader = DataLoader(\n    test_dataset, \n    batch_size=BATCH_SIZE,\n    num_workers=0,  # Fixed: avoid multiprocessing issues\n    pin_memory=True if torch.cuda.is_available() else False\n)\n\nprint(\"✓ Dataloaders ready\")\n\n# --------- MODEL ---------\nprint(f\"\\n{'='*70}\")\nprint(\"LOADING MODEL\")\nprint(\"=\"*70)\n\nmodel = models.efficientnet_b3(weights='IMAGENET1K_V1')\nmodel.classifier[1] = nn.Linear(model.classifier[1].in_features, NUM_CLASSES)\nmodel = model.to(DEVICE)\n\ntotal_params = sum(p.numel() for p in model.parameters())\nprint(f\"Model: EfficientNet-B3\")\nprint(f\"  Parameters: {total_params:,}\")\nprint(f\"  Output Classes: {NUM_CLASSES}\")\n\n# --------- TRAINING SETUP ---------\ncriterion = nn.CrossEntropyLoss(label_smoothing=0.1)\noptimizer = torch.optim.AdamW(model.parameters(), lr=LEARNING_RATE, weight_decay=WEIGHT_DECAY)\nscheduler = torch.optim.lr_scheduler.OneCycleLR(\n    optimizer,\n    max_lr=LEARNING_RATE * 10,\n    epochs=NUM_EPOCHS,\n    steps_per_epoch=200,\n    pct_start=0.3\n)\nscaler = GradScaler() if torch.cuda.is_available() else None\n\nprint(f\"\\n{'='*70}\")\nprint(\"TRAINING\")\nprint(\"=\"*70)\n\nbest_acc = 0.0\n\nfor epoch in range(NUM_EPOCHS):\n    model.train()\n    running_loss = 0.0\n    correct = 0\n    total = 0\n    batches = 0\n    \n    print(f\"\\nEpoch {epoch+1}/{NUM_EPOCHS}\")\n    print(\"-\"*70)\n    \n    for batch_idx, (images, labels) in enumerate(train_loader):\n        # Ensure labels are proper 1D tensor of long type\n        images = images.to(DEVICE)\n        labels = torch.tensor(labels, dtype=torch.long).to(DEVICE)\n        \n        optimizer.zero_grad()\n        \n        if scaler:\n            with autocast():\n                outputs = model(images)\n                loss = criterion(outputs, labels)\n            scaler.scale(loss).backward()\n            torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)\n            scaler.step(optimizer)\n            scaler.update()\n        else:\n            outputs = model(images)\n            loss = criterion(outputs, labels)\n            loss.backward()\n            torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)\n            optimizer.step()\n        \n        scheduler.step()\n        \n        running_loss += loss.item()\n        _, predicted = outputs.max(1)\n        total += labels.size(0)\n        correct += predicted.eq(labels).sum().item()\n        batches += 1\n        \n        if batch_idx % 50 == 0 and batch_idx > 0:\n            acc = 100. * correct / total\n            avg_loss = running_loss / batches\n            print(f'  [{batch_idx:3d}] Loss: {avg_loss:.4f} | Acc: {acc:.2f}% | Samples: {total:,}')\n    \n    avg_loss = running_loss / batches if batches > 0 else 0\n    acc = 100. * correct / total if total > 0 else 0\n    \n    print(f\"\\n{'─'*70}\")\n    print(f\"Epoch {epoch+1}: Loss={avg_loss:.4f} | Acc={acc:.2f}% | Samples={total:,}\")\n    print(f\"{'─'*70}\")\n    \n    if batches > 0 and acc > best_acc:\n        best_acc = acc\n        torch.save({\n            'epoch': epoch,\n            'model_state_dict': model.state_dict(),\n            'accuracy': acc,\n            'loss': avg_loss\n        }, '/kaggle/working/best_model.pth')\n        print(f\"✓ Best model saved (Acc: {acc:.2f}%)\")\n\n# --------- INFERENCE ---------\nprint(f\"\\n{'='*70}\")\nprint(\"INFERENCE\")\nprint(\"=\"*70)\n\nif os.path.exists('/kaggle/working/best_model.pth'):\n    checkpoint = torch.load('/kaggle/working/best_model.pth')\n    model.load_state_dict(checkpoint['model_state_dict'])\n    print(f\"✓ Loaded best model\")\n    print(f\"  Epoch: {checkpoint['epoch']+1}\")\n    print(f\"  Accuracy: {checkpoint['accuracy']:.2f}%\")\n    print(f\"  Loss: {checkpoint['loss']:.4f}\")\nelse:\n    print(\"Using final model\")\n\nmodel.eval()\npredictions = []\ntest_ids = []\nwith torch.no_grad():\n    for batch_idx, (images, ids) in enumerate(test_loader):\n        images = images.to(DEVICE)\n        \n        # Fast TTA with horizontal flip only\n        outputs = model(images)\n        outputs_flip = model(torch.flip(images, dims=[3]))\n        outputs = (outputs + outputs_flip) / 2\n        \n        preds = torch.argmax(outputs, dim=1)\n        predictions.extend(preds.cpu().numpy().tolist())\n        test_ids.extend(ids)\n        \n        if batch_idx % 30 == 0 and batch_idx > 0:\n            print(f'  Processed {len(test_ids):,} samples...')\n\nprint(f\"\\n✓ Generated {len(predictions):,} predictions\")\n\n# --------- SUBMISSION ---------\nprint(f\"\\n{'='*70}\")\nprint(\"CREATING SUBMISSION\")\nprint(\"=\"*70)\n\nif len(predictions) > 0:\n    submission = pd.DataFrame({\n        'id': test_ids,\n        'label': predictions\n    })\n    submission.to_csv('/kaggle/working/submission.csv', index=False)\n    \n    print(f\"\\n✓ SUCCESS!\")\n    print(f\"  Best Training Accuracy: {best_acc:.2f}%\")\n    print(f\"  Total Predictions: {len(predictions):,}\")\n    print(f\"  Unique Classes: {submission['label'].nunique()}/{NUM_CLASSES}\")\n    \n    print(f\"\\nFirst 10 predictions:\")\n    print(submission.head(10).to_string(index=False))\n    \n    print(f\"\\nTop 10 predicted classes:\")\n    for label, count in submission['label'].value_counts().head(10).items():\n        print(f\"  Class {label:3d}: {count:4d} ({count/len(submission)*100:.1f}%)\")\n    \n    print(f\"\\n{'='*70}\")\n    print(\"✓ COMPLETE! File: /kaggle/working/submission.csv\")\n    print(\"=\"*70)\nelse:\n    print(\"\\n⚠ ERROR: No predictions generated!\")\n    print(\"Make sure GPU is enabled: Settings → Accelerator → GPU\")\n\n\n","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"outputs":[],"execution_count":null}]}