{"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":"none","dataSources":[{"sourceId":3649,"databundleVersionId":46718,"sourceType":"competition"},{"sourceId":14073953,"sourceType":"datasetVersion","datasetId":8958855}],"dockerImageVersionId":31192,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import torch\nimport torch.nn as nn\nimport torch.optim as optim\nfrom torch.utils.data import DataLoader\nimport torchvision\nimport torchvision.transforms as transforms\nimport numpy as np\nfrom time import time\nimport os\nimport pandas as pd\nfrom PIL import Image\nfrom torch.utils.data import Dataset\n\n!apt-get install -y p7zip-full\n\n!7z x /kaggle/input/cifar-10/train.7z -o/kaggle/working/train\n\nclass KaggleCIFAR10(Dataset):\n    def __init__(self, csv_file, img_dir, transform=None):\n        self.df = pd.read_csv(csv_file)\n        self.img_dir = img_dir\n        self.transform = transform\n\n        classes = sorted(self.df[\"label\"].unique())\n        self.class_to_idx = {c: i for i, c in enumerate(classes)}\n\n    def __len__(self):\n        return len(self.df)\n\n    def __getitem__(self, idx):\n        row = self.df.iloc[idx]\n        img_id = row[\"id\"]\n        img_path = os.path.join(self.img_dir, f\"{img_id}.png\")\n        label = self.class_to_idx[row[\"label\"]]\n\n        image = Image.open(img_path).convert(\"RGB\")\n\n        if self.transform:\n            image = self.transform(image)\n\n        return image, label\n\n\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nprint(\"Using device:\", device)\n\n# Helper: compute accuracy\ndef accuracy(outputs, targets):\n    \"\"\"\n    Compute classification accuracy.\n    outputs: raw logits from the model (batch_size, num_classes)\n    targets: ground-truth labels (batch_size,)\n    \"\"\"\n    _, preds = torch.max(outputs, dim=1)\n    correct = (preds == targets).sum().item()\n    total = targets.size(0)\n    return correct / total\n\ndef train_one_epoch(model, dataloader, criterion, optimizer, device):\n    \"\"\"\n    Train the model for one epoch.\n    Returns average loss and accuracy over the epoch.\n    \"\"\"\n    model.train()\n    running_loss = 0.0\n    running_acc = 0.0\n\n    for images, labels in dataloader:\n        images = images.to(device)\n        labels = labels.to(device)\n\n        # 1. Forward pass\n        outputs = model(images)\n        loss = criterion(outputs, labels)\n\n        # 2. Backward pass\n        optimizer.zero_grad()\n        loss.backward()\n        optimizer.step()\n\n        # 3. Track metrics\n        running_loss += loss.item() * images.size(0)\n        running_acc += accuracy(outputs, labels) * images.size(0)\n\n    epoch_loss = running_loss / len(dataloader.dataset)\n    epoch_acc = running_acc / len(dataloader.dataset)\n    return epoch_loss, epoch_acc\n\ndef evaluate(model, dataloader, criterion, device):\n    \"\"\"\n    Evaluate the model on validation data.\n    \"\"\"\n    model.eval()\n    running_loss = 0.0\n    running_acc = 0.0\n\n    with torch.no_grad():\n        for images, labels in dataloader:\n            images = images.to(device)\n            labels = labels.to(device)\n\n            outputs = model(images)\n            loss = criterion(outputs, labels)\n\n            running_loss += loss.item() * images.size(0)\n            running_acc += accuracy(outputs, labels) * images.size(0)\n\n    epoch_loss = running_loss / len(dataloader.dataset)\n    epoch_acc = running_acc / len(dataloader.dataset)\n    return epoch_loss, epoch_acc\n\n\n# Simple transforms\nbaseline_transform = transforms.Compose([\n    transforms.ToTensor(),\n    transforms.Normalize((0.4914, 0.4822, 0.4465),  # mean of CIFAR-10\n                         (0.2470, 0.2435, 0.2616))  # std of CIFAR-10\n])\n\n# Kaggle dataset paths\nTRAIN_CSV = \"/kaggle/input/cifar-10/trainLabels.csv\"\nTRAIN_DIR = \"/kaggle/working/train\"\n\n# Load training dataset\ntrain_dataset_baseline = KaggleCIFAR10(\n    csv_file=TRAIN_CSV,\n    img_dir=TRAIN_DIR,\n    transform=baseline_transform\n)\n\n# Create validation split\nval_ratio = 0.1\nnum_train = int(len(train_dataset_baseline) * (1 - val_ratio))\nnum_val   = len(train_dataset_baseline) - num_train\n\ntrain_dataset_baseline, val_dataset_baseline = torch.utils.data.random_split(\n    train_dataset_baseline,\n    [num_train, num_val],\n    generator=torch.Generator().manual_seed(42)\n)","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-12-09T10:36:32.282767Z","iopub.execute_input":"2025-12-09T10:36:32.283014Z","iopub.status.idle":"2025-12-09T10:43:45.024299Z","shell.execute_reply.started":"2025-12-09T10:36:32.282998Z","shell.execute_reply":"2025-12-09T10:43:45.023073Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**PART - 1**","metadata":{}},{"cell_type":"code","source":"# Kaggle dataset paths\nTRAIN_CSV = \"/kaggle/input/cifar-10/trainLabels.csv\"\nTRAIN_DIR = \"/kaggle/working/train/train\"\n\n# Load training dataset\ntrain_dataset_baseline = KaggleCIFAR10(\n    csv_file=TRAIN_CSV,\n    img_dir=TRAIN_DIR,\n    transform=baseline_transform\n)\n\n# Create validation split\nval_ratio = 0.1\nnum_train = int(len(train_dataset_baseline) * (1 - val_ratio))\nnum_val   = len(train_dataset_baseline) - num_train\n\ntrain_dataset_baseline, val_dataset_baseline = torch.utils.data.random_split(\n    train_dataset_baseline,\n    [num_train, num_val],\n    generator=torch.Generator().manual_seed(42)\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-09T10:49:24.576813Z","iopub.execute_input":"2025-12-09T10:49:24.577103Z","iopub.status.idle":"2025-12-09T10:49:24.598341Z","shell.execute_reply.started":"2025-12-09T10:49:24.577081Z","shell.execute_reply":"2025-12-09T10:49:24.597476Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"batch_size = 128\n\ntrain_loader_baseline = DataLoader(train_dataset_baseline, batch_size=batch_size, shuffle=True, num_workers=2)\nval_loader_baseline = DataLoader(val_dataset_baseline, batch_size=batch_size, shuffle=False, num_workers=2)\n\n\nclass SimpleCNN(nn.Module):\n    \"\"\"\n    Very simple CNN for CIFAR-10 (baseline).\n    - Conv -> ReLU -> MaxPool\n    \"\"\"\n    def __init__(self, num_classes=10):\n        super(SimpleCNN, self).__init__()\n\n        self.features = nn.Sequential(\n            # Input: (3, 32, 32)\n            nn.Conv2d(3, 32, kernel_size=3, padding=1),\n            nn.ReLU(inplace=True),\n            nn.MaxPool2d(2),  \n\n            nn.Conv2d(32, 64, kernel_size=3, padding=1),\n            nn.ReLU(inplace=True),\n            nn.MaxPool2d(2),  \n        )\n\n        self.classifier = nn.Sequential(\n            nn.Flatten(),\n            nn.Linear(64 * 8 * 8, 256),\n            nn.ReLU(inplace=True),\n            nn.Linear(256, num_classes)\n        )\n\n    def forward(self, x):\n        x = self.features(x)\n        x = self.classifier(x)\n        return x\n\n\ndef run_baseline(num_epochs=10, lr=0.01):\n    model = SimpleCNN(num_classes=10).to(device)\n\n    criterion = nn.CrossEntropyLoss()\n    optimizer = optim.SGD(model.parameters(), lr=lr, momentum=0.9)\n\n    best_val_acc = 0.0\n\n    for epoch in range(num_epochs):\n        start = time()\n        train_loss, train_acc = train_one_epoch(model, train_loader_baseline,\n                                                criterion, optimizer, device)\n        val_loss, val_acc = evaluate(model, val_loader_baseline,\n                                     criterion, device)\n        elapsed = time() - start\n\n        print(f\"[Baseline][Epoch {epoch+1}/{num_epochs}] \"\n              f\"Train Loss: {train_loss:.4f} | Train Acc: {train_acc:.4f} \"\n              f\"| Val Loss: {val_loss:.4f} | Val Acc: {val_acc:.4f} \"\n              f\"| Time: {elapsed:.1f}s\")\n\n        if val_acc > best_val_acc:\n            best_val_acc = val_acc\n            torch.save(model.state_dict(), \"baseline_best.pth\")\n\n    print(f\"[Baseline] Best Val Acc: {best_val_acc:.4f}\")\n    return model\n\n\n# To run baseline:\nbaseline_model = run_baseline(num_epochs=10, lr=0.01)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-09T10:49:34.057271Z","iopub.execute_input":"2025-12-09T10:49:34.057598Z","iopub.status.idle":"2025-12-09T10:55:37.137658Z","shell.execute_reply.started":"2025-12-09T10:49:34.057580Z","shell.execute_reply":"2025-12-09T10:55:37.136131Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Built a simple CNN from scratch with minimal preprocessing to establish a performance baseline.\n\nResults:\n\nTraining Accuracy: 93.92%\n\nValidation Accuracy: 73.24%","metadata":{}},{"cell_type":"markdown","source":"**PART - 2**","metadata":{}},{"cell_type":"code","source":"# PART 2: IMPROVED MODEL \n\n# Data augmentation + better normalization\nimproved_train_transform = transforms.Compose([\n    transforms.RandomCrop(32, padding=4),   \n    transforms.RandomHorizontalFlip(),       \n    transforms.ToTensor(),\n    transforms.Normalize((0.4914, 0.4822, 0.4465),\n                         (0.2470, 0.2435, 0.2616))\n])\n\n\nimproved_val_transform = transforms.Compose([\n    transforms.ToTensor(),\n    transforms.Normalize((0.4914, 0.4822, 0.4465),\n                         (0.2470, 0.2435, 0.2616))\n])\n\nval_ratio = 0.1\n\n\nfull_dataset_for_split = KaggleCIFAR10(\n    csv_file=TRAIN_CSV,\n    img_dir=TRAIN_DIR,\n    transform=None\n)\n\nnum_samples = len(full_dataset_for_split)\nnum_train = int(num_samples * (1 - val_ratio))\nnum_val = num_samples - num_train\n\n# Reproducible random split\ng = torch.Generator().manual_seed(42)\nindices = torch.randperm(num_samples, generator=g).tolist()\ntrain_indices = indices[:num_train]\nval_indices = indices[num_train:]\n\n# Two datasets with different transforms\ntrain_dataset_improved_base = KaggleCIFAR10(\n    csv_file=TRAIN_CSV,\n    img_dir=TRAIN_DIR,\n    transform=improved_train_transform\n)\n\nval_dataset_improved_base = KaggleCIFAR10(\n    csv_file=TRAIN_CSV,\n    img_dir=TRAIN_DIR,\n    transform=improved_val_transform\n)\n\nfrom torch.utils.data import Subset\n\ntrain_dataset_improved = Subset(train_dataset_improved_base, train_indices)\nval_dataset_improved = Subset(val_dataset_improved_base, val_indices)\n\nbatch_size_improved = 128\n\ntrain_loader_improved = DataLoader(\n    train_dataset_improved,\n    batch_size=batch_size_improved,\n    shuffle=True,\n    num_workers=2\n)\n\nval_loader_improved = DataLoader(\n    val_dataset_improved,\n    batch_size=batch_size_improved,\n    shuffle=False,\n    num_workers=2\n)\n\n\nclass BetterCNN(nn.Module):\n    \"\"\"\n    Improved CNN:\n    - More conv layers\n    - BatchNorm after each conv\n    - Dropout in the classifier\n    This increases model capacity and adds regularization.\n    \"\"\"\n    def __init__(self, num_classes=10):\n        super(BetterCNN, self).__init__()\n\n        self.features = nn.Sequential(\n            # Block 1\n            nn.Conv2d(3, 64, kernel_size=3, padding=1),\n            nn.BatchNorm2d(64),\n            nn.ReLU(inplace=True),\n\n            nn.Conv2d(64, 64, kernel_size=3, padding=1),\n            nn.BatchNorm2d(64),\n            nn.ReLU(inplace=True),\n            nn.MaxPool2d(2),   # (64, 16, 16)\n\n            # Block 2\n            nn.Conv2d(64, 128, kernel_size=3, padding=1),\n            nn.BatchNorm2d(128),\n            nn.ReLU(inplace=True),\n\n            nn.Conv2d(128, 128, kernel_size=3, padding=1),\n            nn.BatchNorm2d(128),\n            nn.ReLU(inplace=True),\n            nn.MaxPool2d(2),   # (128, 8, 8)\n\n            # Block 3\n            nn.Conv2d(128, 256, kernel_size=3, padding=1),\n            nn.BatchNorm2d(256),\n            nn.ReLU(inplace=True),\n            nn.MaxPool2d(2),   # (256, 4, 4)\n        )\n\n        self.classifier = nn.Sequential(\n            nn.Flatten(),\n            nn.Linear(256 * 4 * 4, 512),\n            nn.ReLU(inplace=True),\n            nn.Dropout(0.5),          # dropout regularization\n            nn.Linear(512, num_classes)\n        )\n\n    def forward(self, x):\n        x = self.features(x)\n        x = self.classifier(x)\n        return x\n\n\ndef run_improved(num_epochs=20, lr=0.1):\n    \"\"\"\n    Train the improved CNN:\n    - Uses data augmentation\n    - Uses weight decay (L2)\n    - Uses MultiStepLR scheduler\n    \"\"\"\n    model = BetterCNN(num_classes=10).to(device)\n\n    criterion = nn.CrossEntropyLoss()\n    optimizer = optim.SGD(\n        model.parameters(),\n        lr=lr,\n        momentum=0.9,\n        weight_decay=5e-4    # L2 regularization\n    )\n\n    # Reduce LR at epochs 10 and 15\n    scheduler = optim.lr_scheduler.MultiStepLR(\n        optimizer,\n        milestones=[10, 15],\n        gamma=0.1\n    )\n\n    best_val_acc = 0.0\n\n    for epoch in range(num_epochs):\n        start = time()\n\n        train_loss, train_acc = train_one_epoch(\n            model, train_loader_improved, criterion, optimizer, device\n        )\n\n        val_loss, val_acc = evaluate(\n            model, val_loader_improved, criterion, device\n        )\n\n        scheduler.step()\n        elapsed = time() - start\n\n        current_lr = scheduler.get_last_lr()[0]\n\n        print(f\"[Improved][Epoch {epoch+1}/{num_epochs}] \"\n              f\"Train Loss: {train_loss:.4f} | Train Acc: {train_acc:.4f} \"\n              f\"| Val Loss: {val_loss:.4f} | Val Acc: {val_acc:.4f} \"\n              f\"| Time: {elapsed:.1f}s | LR: {current_lr:.5f}\")\n\n        if val_acc > best_val_acc:\n            best_val_acc = val_acc\n            torch.save(model.state_dict(), \"improved_best.pth\")\n\n    print(f\"[Improved] Best Val Acc: {best_val_acc:.4f}\")\n    return model\n\n\n# Run improved model:\nimproved_model = run_improved(num_epochs=20, lr=0.1)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-09T10:59:39.215828Z","iopub.execute_input":"2025-12-09T10:59:39.216132Z","iopub.status.idle":"2025-12-09T12:43:53.931330Z","shell.execute_reply.started":"2025-12-09T10:59:39.216108Z","shell.execute_reply":"2025-12-09T12:43:53.930161Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Part 2 — Improved Model**\n\nTo address the shortcomings of the baseline, I introduced the following enhancements:\n\n1) **Data Augmentation**\n\ni. Random crop\n\nii. Random horizontal flip\n\nAugmentation artificially increases dataset variability, helping the model generalize better instead of memorizing training samples.\n\n\n2) **Better Architecture**\n\ni. Added more convolution layers\n\nii. Included Batch Normalization\n\niii. Added Dropout inside fully connected layers\n\niv. Increased model depth and capacity\n\nBatchNorm stabilizes training and allows higher learning rates.\n\nDropout reduces overfitting by preventing co-adaptation of neurons.\n\nMore layers means more expressive power to learn complex features.\n\n\n3) **Better Optimization Strategy**\n\ni. Learning rate scheduler (ReduceLROnPlateau)\n\nii. Weight decay (L2 regularization)\n\nScheduled learning rate allows the model to converge smoothly.\n\nWeight decay prevents weights from becoming too large (controlling overfitting).\n\n\nResults:\n\nTraining Accuracy: 88.01%\n\nValidation Accuracy: 87.46%\n\n\nValidation accuracy increased from 73.2% to 87.46%, which is a +14% increase.\n\nHence the improvements made were effective and since the gap between training and validation accuracy is now much smaller, it implies better generalization.","metadata":{}},{"cell_type":"markdown","source":"**PART - 3**","metadata":{}},{"cell_type":"code","source":"# PART 3: TRANSFER LEARNING using ResNet18\n\nfrom torchvision.models import resnet18, ResNet18_Weights\n\n# ImageNet normalization\nimagenet_mean = (0.485, 0.456, 0.406)\nimagenet_std  = (0.229, 0.224, 0.225)\n\ntransfer_train_transform = transforms.Compose([\n    transforms.Resize(128),\n    transforms.RandomCrop(112),\n    transforms.RandomHorizontalFlip(),\n    transforms.ToTensor(),\n    transforms.Normalize(imagenet_mean, imagenet_std)\n])\n\ntransfer_val_transform = transforms.Compose([\n    transforms.Resize(128),\n    transforms.CenterCrop(112),\n    transforms.ToTensor(),\n    transforms.Normalize(imagenet_mean, imagenet_std)\n])\n\n# Create train/val split\nval_ratio = 0.1\n\nfull_dataset_for_split_tf = KaggleCIFAR10(\n    csv_file=TRAIN_CSV,\n    img_dir=TRAIN_DIR,\n    transform=None\n)\n\nnum_samples_tf = len(full_dataset_for_split_tf)\nnum_train_tf = int(num_samples_tf * (1 - val_ratio))\nnum_val_tf = num_samples_tf - num_train_tf\n\ng_tf = torch.Generator().manual_seed(42)\nindices_tf = torch.randperm(num_samples_tf, generator=g_tf).tolist()\ntrain_indices_tf = indices_tf[:num_train_tf]\nval_indices_tf = indices_tf[num_train_tf:]\n\n# Two datasets with different transforms\ntrain_dataset_transfer_base = KaggleCIFAR10(\n    csv_file=TRAIN_CSV,\n    img_dir=TRAIN_DIR,\n    transform=transfer_train_transform\n)\n\nval_dataset_transfer_base = KaggleCIFAR10(\n    csv_file=TRAIN_CSV,\n    img_dir=TRAIN_DIR,\n    transform=transfer_val_transform\n)\n\ntrain_dataset_transfer = torch.utils.data.Subset(\n    train_dataset_transfer_base, train_indices_tf\n)\nval_dataset_transfer = torch.utils.data.Subset(\n    val_dataset_transfer_base, val_indices_tf\n)\n\nbatch_size_transfer = 64\n\ntrain_loader_transfer = DataLoader(\n    train_dataset_transfer,\n    batch_size=batch_size_transfer,\n    shuffle=True,\n    num_workers=2\n)\n\nval_loader_transfer = DataLoader(\n    val_dataset_transfer,\n    batch_size=batch_size_transfer,\n    shuffle=False,\n    num_workers=2\n)\n\n\ndef create_resnet18_transfer(num_classes=10,\n                             freeze_features=True,\n                             unfreeze_last_block=True):\n    \"\"\"\n    Load ResNet18 with pretrained weights from a local .pth file.\n    \"\"\"\n\n    weight_path = \"/kaggle/input/resnet18-weights/resnet18-f37072fd.pth\"\n\n    try:\n        print(\"Loading local pretrained weights...\")\n        \n        model = resnet18(weights=None)\n        \n        state_dict = torch.load(weight_path, map_location=\"cpu\")\n        model.load_state_dict(state_dict)\n        print(\"Successfully loaded pretrained weights.\")\n    except Exception as e:\n        print(\"ERROR loading pretrained weights:\", e)\n        print(\"Using randomly initialized ResNet18 instead.\")\n        model = resnet18(weights=None)\n\n\n    in_features = model.fc.in_features\n    model.fc = nn.Linear(in_features, num_classes)\n\n    # Freeze earlier layers\n    if freeze_features:\n        for param in model.parameters():\n            param.requires_grad = False\n\n        for param in model.fc.parameters():\n            param.requires_grad = True\n\n        if unfreeze_last_block:\n            for param in model.layer4.parameters():\n                param.requires_grad = True\n\n    return model\n\n\ndef run_transfer(num_epochs=10, lr=1e-3):\n    \"\"\"\n    Train ResNet18 with transfer learning.\n    - Uses pretrained weights\n    - Freezes most layers, fine-tunes last block + classifier\n    \"\"\"\n    model = create_resnet18_transfer(\n        num_classes=10,\n        freeze_features=True,\n        unfreeze_last_block=True\n    ).to(device)\n\n    criterion = nn.CrossEntropyLoss()\n\n    \n    optimizer = optim.Adam(\n        filter(lambda p: p.requires_grad, model.parameters()),\n        lr=lr\n    )\n\n    # Scheduler: reduce LR if val loss plateaus\n    scheduler = optim.lr_scheduler.ReduceLROnPlateau(\n        optimizer,\n        mode=\"min\",\n        factor=0.1,\n        patience=2,\n        verbose=True\n    )\n\n    best_val_acc = 0.0\n\n    for epoch in range(num_epochs):\n        start = time()\n\n        train_loss, train_acc = train_one_epoch(\n            model, train_loader_transfer, criterion, optimizer, device\n        )\n        val_loss, val_acc = evaluate(\n            model, val_loader_transfer, criterion, device\n        )\n\n        scheduler.step(val_loss)\n        elapsed = time() - start\n\n        print(f\"[Transfer][Epoch {epoch+1}/{num_epochs}] \"\n              f\"Train Loss: {train_loss:.4f} | Train Acc: {train_acc:.4f} \"\n              f\"| Val Loss: {val_loss:.4f} | Val Acc: {val_acc:.4f} \"\n              f\"| Time: {elapsed:.1f}s\")\n\n        if val_acc > best_val_acc:\n            best_val_acc = val_acc\n            torch.save(model.state_dict(), \"transfer_best.pth\")\n\n    print(f\"[Transfer] Best Val Acc: {best_val_acc:.4f}\")\n    return model\n\n\n# Run transfer learning:\ntransfer_model = run_transfer(num_epochs=3, lr=1e-3)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-09T15:05:11.784607Z","iopub.execute_input":"2025-12-09T15:05:11.784922Z","iopub.status.idle":"2025-12-09T15:37:57.047445Z","shell.execute_reply.started":"2025-12-09T15:05:11.784898Z","shell.execute_reply":"2025-12-09T15:37:57.046480Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Part 3 — Transfer Learning (ResNet18)**\n\nUsed ResNet18 pretrained on ImageNet\n\nFroze most layers and fine-tuned final layer and residual block (layer4)\n\nResults:\n\nTraining Accuracy: 96.05%\n\nValidation Accuracy: 90.04%\n\nWhy pretrained models perform Works Better:\n\n1. **Feature Reuse**\n\nResNet18 trained on ImageNet has already learned:\n\ni. edges\n\nii. shapes\n\niii. textures\n\niv. object components\n\nThese features generalize to CIFAR-10, so the model starts from a strong point rather than learning from scratch.\n\n2. **Faster Convergence**\n\nEven with three epochs, validation accuracy reached 90%.\nFrom scratch, this would require much more data and many more epochs.\n\n3. **Better Generalization**\n\nPretrained weights encode knowledge gained from over 1 million images, giving better performance even with limited CIFAR-10 data.\n\nTrade-offs of Transfer Learning:\n\n**Advantages:**\n\nMuch higher accuracy\n\nMuch faster convergence\n\nRobust generalization\n\nWorks well even with small data\n\n**Disadvantages:**\n\nMuch heavier model- ResNet18 is far larger than our custom CNN\n\nTraining time is longer\n\n**Conclusion:**\n\nAs model complexity and use of prior knowledge increase, the performance on CIFAR-10 improves significantly. Transfer learning provides the best results because pretrained models reuse rich features learned from large-scale datasets.","metadata":{}}]}