{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.12.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[],"dockerImageVersionId":28755,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"\"\"\"\nDEEP LEARNING ASSIGNMENT #1\nPhase 3: Convolutional Neural Networks - CIFAR-10 Classification\n\nStudent Name: Sara\nSubmitted To: Ma'am Tanzeela\nCourse: Deep Learning\nDate: June 2026\n\nDataset: CIFAR-10 (10 object categories)\nFramework: PyTorch\nGPU: Kaggle P100\n\nBuilding a CNN from scratch for image classification\n\"\"\"\n\n# ============================================\n# IMPORTS\n# ============================================\n\nimport torch\nimport torch.nn as nn\nimport torch.optim as optim\nfrom torch.utils.data import DataLoader, Dataset\nfrom torchvision import datasets, transforms\nimport matplotlib.pyplot as plt\nimport numpy as np\nfrom tqdm import tqdm\nimport time\nimport os\nimport pandas as pd\nfrom PIL import Image\nfrom sklearn.metrics import confusion_matrix, classification_report\nimport seaborn as sns\nimport warnings\nwarnings.filterwarnings('ignore')\n\n# GPU configuration\ndevice = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\nprint(f\"Using device: {device}\")\nif torch.cuda.is_available():\n    print(f\"GPU: {torch.cuda.get_device_name(0)}\")\n    print(f\"Memory: {torch.cuda.get_device_properties(0).total_memory / 1e9:.2f} GB\")\nprint(\"=\"*60)\nprint(\"PHASE 3: CNN FROM SCRATCH - CIFAR-10\")\nprint(\"Student: Sara\")\nprint(\"Submitted To: Ma'am Tanzeela\")\nprint(\"=\"*60)\n\n# Set random seeds\ntorch.manual_seed(42)\nnp.random.seed(42)\n\n# ============================================\n# 1. DATA LOADING FROM KAGGLE PATH\n# ============================================\n\ndataset_path = '/kaggle/input/competitions/cifar-10'\n\nprint(f\"\\nLoading dataset from: {dataset_path}\")\n\ntry:\n    # List files in directory\n    file_list = os.listdir(dataset_path)\n    print(f\"Files found: {file_list}\")\n    \n    # Check if CSV files exist (Kaggle competition format)\n    if 'train.csv' in file_list and 'test.csv' in file_list:\n        print(\"\\nLoading CSV format dataset...\")\n        \n        # Load training data\n        train_df = pd.read_csv(os.path.join(dataset_path, 'train.csv'))\n        print(f\"Training data shape: {train_df.shape}\")\n        \n        # Load test data\n        test_df = pd.read_csv(os.path.join(dataset_path, 'test.csv'))\n        print(f\"Test data shape: {test_df.shape}\")\n        \n        # Create custom dataset class\n        class CIFAR10KaggleDataset(Dataset):\n            def __init__(self, dataframe, transform=None, is_train=True):\n                self.dataframe = dataframe\n                self.transform = transform\n                self.is_train = is_train\n                \n                # Separate features and labels if training\n                if is_train:\n                    self.labels = dataframe['label'].values\n                    self.images = dataframe.drop('label', axis=1).values\n                else:\n                    self.images = dataframe.values\n                    self.labels = None\n            \n            def __len__(self):\n                return len(self.images)\n            \n            def __getitem__(self, idx):\n                # Reshape from 1024 to 32x32x3\n                img = self.images[idx].reshape(32, 32, 3).astype(np.uint8)\n                img = Image.fromarray(img)\n                \n                if self.transform:\n                    img = self.transform(img)\n                \n                if self.is_train:\n                    label = self.labels[idx]\n                    return img, label\n                else:\n                    return img\n        \n        # Define transformations\n        transform_train = transforms.Compose([\n            transforms.RandomHorizontalFlip(),\n            transforms.RandomCrop(32, padding=4),\n            transforms.ToTensor(),\n            transforms.Normalize(mean=[0.4914, 0.4822, 0.4465], \n                                std=[0.2023, 0.1994, 0.2010])\n        ])\n        \n        transform_test = transforms.Compose([\n            transforms.ToTensor(),\n            transforms.Normalize(mean=[0.4914, 0.4822, 0.4465], \n                                std=[0.2023, 0.1994, 0.2010])\n        ])\n        \n        # Create datasets\n        train_dataset = CIFAR10KaggleDataset(train_df, transform=transform_train, is_train=True)\n        test_dataset = CIFAR10KaggleDataset(test_df, transform=transform_test, is_train=False)\n        \n        # Class names\n        classes = ['airplane', 'automobile', 'bird', 'cat', 'deer', \n                   'dog', 'frog', 'horse', 'ship', 'truck']\n        \n        print(f\"\\n✅ Dataset loaded successfully from Kaggle!\")\n        print(f\"Training samples: {len(train_dataset)}\")\n        print(f\"Test samples: {len(test_dataset)}\")\n        print(f\"Number of classes: {len(classes)}\")\n        \n    else:\n        # If CSV files not found, use torchvision\n        print(\"\\nCSV files not found. Using torchvision to download CIFAR-10...\")\n        transform_train = transforms.Compose([\n            transforms.RandomHorizontalFlip(),\n            transforms.RandomCrop(32, padding=4),\n            transforms.ToTensor(),\n            transforms.Normalize(mean=[0.4914, 0.4822, 0.4465], \n                                std=[0.2023, 0.1994, 0.2010])\n        ])\n        \n        transform_test = transforms.Compose([\n            transforms.ToTensor(),\n            transforms.Normalize(mean=[0.4914, 0.4822, 0.4465], \n                                std=[0.2023, 0.1994, 0.2010])\n        ])\n        \n        train_dataset = datasets.CIFAR10(\n            root='./data', \n            train=True, \n            download=True, \n            transform=transform_train\n        )\n        \n        test_dataset = datasets.CIFAR10(\n            root='./data', \n            train=False, \n            download=True, \n            transform=transform_test\n        )\n        \n        classes = ['airplane', 'automobile', 'bird', 'cat', 'deer', \n                   'dog', 'frog', 'horse', 'ship', 'truck']\n        \n        print(f\"\\n✅ Dataset loaded via torchvision!\")\n        print(f\"Training samples: {len(train_dataset)}\")\n        print(f\"Test samples: {len(test_dataset)}\")\n        print(f\"Number of classes: {len(classes)}\")\n\nexcept Exception as error:\n    print(f\"Error loading dataset: {error}\")\n    print(\"\\nUsing torchvision as fallback...\")\n    \n    transform_train = transforms.Compose([\n        transforms.RandomHorizontalFlip(),\n        transforms.RandomCrop(32, padding=4),\n        transforms.ToTensor(),\n        transforms.Normalize(mean=[0.4914, 0.4822, 0.4465], \n                            std=[0.2023, 0.1994, 0.2010])\n    ])\n    \n    transform_test = transforms.Compose([\n        transforms.ToTensor(),\n        transforms.Normalize(mean=[0.4914, 0.4822, 0.4465], \n                            std=[0.2023, 0.1994, 0.2010])\n    ])\n    \n    train_dataset = datasets.CIFAR10(\n        root='./data', \n        train=True, \n        download=True, \n        transform=transform_train\n    )\n    \n    test_dataset = datasets.CIFAR10(\n        root='./data', \n        train=False, \n        download=True, \n        transform=transform_test\n    )\n    \n    classes = ['airplane', 'automobile', 'bird', 'cat', 'deer', \n               'dog', 'frog', 'horse', 'ship', 'truck']\n    \n    print(f\"\\n✅ Dataset loaded via torchvision!\")\n    print(f\"Training samples: {len(train_dataset)}\")\n    print(f\"Test samples: {len(test_dataset)}\")\n    print(f\"Number of classes: {len(classes)}\")\n\n# ============================================\n# 2. CREATE DATA LOADERS\n# ============================================\n\nBATCH_SIZE = 128\ntrain_loader = DataLoader(train_dataset, batch_size=BATCH_SIZE, \n                          shuffle=True, num_workers=2, pin_memory=True)\ntest_loader = DataLoader(test_dataset, batch_size=BATCH_SIZE, \n                         shuffle=False, num_workers=2, pin_memory=True)\n\nprint(f\"\\nTraining batches: {len(train_loader)}\")\nprint(f\"Test batches: {len(test_loader)}\")\n\n# ============================================\n# 3. VISUALIZE SAMPLE IMAGES\n# ============================================\n\ndef show_sample_images(dataset, classes, num_samples=10):\n    \"\"\"Display sample images from dataset\"\"\"\n    fig, axes = plt.subplots(2, 5, figsize=(15, 6))\n    axes = axes.flatten()\n    \n    for idx in range(num_samples):\n        # Get image and label\n        img, label = dataset[idx]\n        \n        # Denormalize for display\n        mean = np.array([0.4914, 0.4822, 0.4465])\n        std = np.array([0.2023, 0.1994, 0.2010])\n        img_np = img.numpy().transpose(1, 2, 0)\n        img_np = std * img_np + mean\n        img_np = np.clip(img_np, 0, 1)\n        \n        axes[idx].imshow(img_np)\n        axes[idx].set_title(f'{classes[label]}', fontsize=10)\n        axes[idx].axis('off')\n    \n    plt.suptitle('CIFAR-10 Sample Images', fontsize=14, fontweight='bold')\n    plt.tight_layout()\n    plt.savefig('cifar10_samples.png', dpi=200, bbox_inches='tight')\n    plt.show()\n\nprint(\"\\nDisplaying sample images...\")\nshow_sample_images(train_dataset, classes, num_samples=10)\n\n# ============================================\n# 4. CNN MODEL ARCHITECTURE\n# ============================================\n\nclass CIFAR10CNN(nn.Module):\n    \"\"\"\n    Simple CNN for CIFAR-10 classification\n    \n    Architecture:\n    - Conv Layer 1: 3→32 channels, 3x3 kernel\n    - ReLU Activation\n    - Max Pooling: 2x2\n    - Conv Layer 2: 32→64 channels, 3x3 kernel\n    - ReLU Activation\n    - Max Pooling: 2x2\n    - Fully Connected: 64*8*8 → 512\n    - ReLU Activation\n    - Fully Connected: 512 → 10 (output classes)\n    \"\"\"\n    \n    def __init__(self, num_classes=10):\n        super(CIFAR10CNN, self).__init__()\n        \n        # Convolutional layers\n        self.conv1 = nn.Conv2d(in_channels=3, out_channels=32, \n                               kernel_size=3, padding=1)\n        self.conv2 = nn.Conv2d(in_channels=32, out_channels=64, \n                               kernel_size=3, padding=1)\n        \n        # Pooling layer\n        self.pool = nn.MaxPool2d(kernel_size=2, stride=2)\n        \n        # Activation\n        self.relu = nn.ReLU()\n        \n        # Dropout for regularization\n        self.dropout = nn.Dropout(0.25)\n        \n        # Fully connected layers\n        self.fc1 = nn.Linear(64 * 8 * 8, 512)\n        self.fc2 = nn.Linear(512, num_classes)\n        \n    def forward(self, x):\n        # First conv block\n        x = self.conv1(x)           # 32x32x32\n        x = self.relu(x)\n        x = self.pool(x)            # 16x16x32\n        \n        # Second conv block\n        x = self.conv2(x)           # 16x16x64\n        x = self.relu(x)\n        x = self.pool(x)            # 8x8x64\n        \n        # Flatten\n        x = x.view(-1, 64 * 8 * 8)\n        \n        # Fully connected layers\n        x = self.fc1(x)\n        x = self.relu(x)\n        x = self.dropout(x)\n        x = self.fc2(x)\n        \n        return x\n\n# Initialize model\nmodel = CIFAR10CNN(num_classes=10).to(device)\nprint(\"\\n\" + \"=\"*60)\nprint(\"MODEL ARCHITECTURE\")\nprint(\"=\"*60)\nprint(model)\n\n# Count parameters\ntotal_params = sum(p.numel() for p in model.parameters())\ntrainable_params = sum(p.numel() for p in model.parameters() if p.requires_grad)\nprint(f\"\\nTotal parameters: {total_params:,}\")\nprint(f\"Trainable parameters: {trainable_params:,}\")\n\n# ============================================\n# 5. TRAINING FUNCTIONS\n# ============================================\n\ndef train_epoch(model, dataloader, criterion, optimizer, device):\n    \"\"\"Train for one epoch\"\"\"\n    model.train()\n    running_loss = 0.0\n    correct = 0\n    total = 0\n    \n    progress_bar = tqdm(dataloader, desc='Training')\n    for inputs, labels in progress_bar:\n        inputs, labels = inputs.to(device), labels.to(device)\n        \n        optimizer.zero_grad()\n        outputs = model(inputs)\n        loss = criterion(outputs, labels)\n        loss.backward()\n        optimizer.step()\n        \n        running_loss += loss.item() * inputs.size(0)\n        _, predicted = torch.max(outputs, 1)\n        total += labels.size(0)\n        correct += (predicted == labels).sum().item()\n        \n        progress_bar.set_postfix({\n            'Loss': f'{loss.item():.4f}',\n            'Acc': f'{100.*correct/total:.2f}%'\n        })\n    \n    return running_loss / total, correct / total\n\ndef validate_epoch(model, dataloader, criterion, device):\n    \"\"\"Validate for one epoch\"\"\"\n    model.eval()\n    running_loss = 0.0\n    correct = 0\n    total = 0\n    \n    with torch.no_grad():\n        progress_bar = tqdm(dataloader, desc='Validating')\n        for inputs, labels in progress_bar:\n            inputs, labels = inputs.to(device), labels.to(device)\n            outputs = model(inputs)\n            loss = criterion(outputs, labels)\n            \n            running_loss += loss.item() * inputs.size(0)\n            _, predicted = torch.max(outputs, 1)\n            total += labels.size(0)\n            correct += (predicted == labels).sum().item()\n            \n            progress_bar.set_postfix({\n                'Loss': f'{loss.item():.4f}',\n                'Acc': f'{100.*correct/total:.2f}%'\n            })\n    \n    return running_loss / total, correct / total\n\ndef train_model(model, train_loader, test_loader, epochs=10, lr=0.001):\n    \"\"\"Complete training pipeline\"\"\"\n    \n    criterion = nn.CrossEntropyLoss()\n    optimizer = optim.Adam(model.parameters(), lr=lr)\n    scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=5, gamma=0.1)\n    \n    history = {\n        'train_loss': [],\n        'train_acc': [],\n        'val_loss': [],\n        'val_acc': []\n    }\n    \n    best_acc = 0.0\n    start_time = time.time()\n    \n    print(\"\\n\" + \"=\"*60)\n    print(f\"STARTING TRAINING FOR {epochs} EPOCHS\")\n    print(\"=\"*60)\n    \n    for epoch in range(1, epochs + 1):\n        print(f\"\\nEpoch {epoch}/{epochs}\")\n        print(\"-\" * 50)\n        \n        # Train\n        train_loss, train_acc = train_epoch(model, train_loader, criterion, optimizer, device)\n        \n        # Validate\n        val_loss, val_acc = validate_epoch(model, test_loader, criterion, device)\n        \n        # Update scheduler\n        scheduler.step()\n        \n        # Store history\n        history['train_loss'].append(train_loss)\n        history['train_acc'].append(train_acc)\n        history['val_loss'].append(val_loss)\n        history['val_acc'].append(val_acc)\n        \n        # Save best model\n        if val_acc > best_acc:\n            best_acc = val_acc\n            torch.save(model.state_dict(), 'best_cnn_model.pth')\n            print(f\"✅ New best model saved! Accuracy: {best_acc:.4f}\")\n        \n        print(f\"\\n📊 Epoch {epoch} Summary:\")\n        print(f\"  Train Loss: {train_loss:.4f}, Train Acc: {train_acc*100:.2f}%\")\n        print(f\"  Val Loss: {val_loss:.4f}, Val Acc: {val_acc*100:.2f}%\")\n        print(f\"  Best Val Acc: {best_acc*100:.2f}%\")\n    \n    training_time = time.time() - start_time\n    print(f\"\\n✅ Training completed in {training_time:.2f} seconds\")\n    print(f\"✅ Best Validation Accuracy: {best_acc*100:.2f}%\")\n    \n    # Load best model\n    model.load_state_dict(torch.load('best_cnn_model.pth'))\n    \n    return model, history, best_acc\n\n# ============================================\n# 6. TRAIN THE MODEL\n# ============================================\n\nprint(\"\\n\" + \"=\"*60)\nprint(\"TRAINING CNN FROM SCRATCH\")\nprint(\"=\"*60)\n\nmodel, history, best_acc = train_model(\n    model, train_loader, test_loader, \n    epochs=10, lr=0.001\n)\n\n# ============================================\n# 7. PLOT TRAINING VS VALIDATION LOSS\n# ============================================\n\ndef plot_training_history(history):\n    \"\"\"Plot training and validation loss/accuracy\"\"\"\n    \n    fig, axes = plt.subplots(1, 2, figsize=(15, 5))\n    \n    # Loss plot\n    axes[0].plot(history['train_loss'], label='Training Loss', \n                 marker='o', color='#2E86C1', linewidth=2)\n    axes[0].plot(history['val_loss'], label='Validation Loss', \n                 marker='s', color='#E74C3C', linewidth=2)\n    axes[0].set_xlabel('Epoch', fontsize=12)\n    axes[0].set_ylabel('Loss', fontsize=12)\n    axes[0].set_title('Training vs Validation Loss', fontsize=13, fontweight='bold')\n    axes[0].legend(fontsize=11)\n    axes[0].grid(True, alpha=0.3)\n    \n    # Accuracy plot\n    axes[1].plot(history['train_acc'], label='Training Accuracy', \n                 marker='o', color='#2E86C1', linewidth=2)\n    axes[1].plot(history['val_acc'], label='Validation Accuracy', \n                 marker='s', color='#E74C3C', linewidth=2)\n    axes[1].set_xlabel('Epoch', fontsize=12)\n    axes[1].set_ylabel('Accuracy', fontsize=12)\n    axes[1].set_title('Training vs Validation Accuracy', fontsize=13, fontweight='bold')\n    axes[1].legend(fontsize=11)\n    axes[1].grid(True, alpha=0.3)\n    \n    plt.suptitle('CIFAR-10 CNN Training Progress', fontsize=14, fontweight='bold', y=1.02)\n    plt.tight_layout()\n    plt.savefig('training_history.png', dpi=300, bbox_inches='tight')\n    plt.show()\n\nprint(\"\\n📊 Plotting training history...\")\nplot_training_history(history)\n\n# ============================================\n# 8. FINAL TEST ACCURACY\n# ============================================\n\ndef evaluate_model(model, dataloader, device):\n    \"\"\"Evaluate model and return predictions\"\"\"\n    model.eval()\n    all_preds = []\n    all_labels = []\n    correct = 0\n    total = 0\n    \n    with torch.no_grad():\n        for inputs, labels in tqdm(dataloader, desc='Evaluating'):\n            inputs, labels = inputs.to(device), labels.to(device)\n            outputs = model(inputs)\n            _, predicted = torch.max(outputs, 1)\n            \n            total += labels.size(0)\n            correct += (predicted == labels).sum().item()\n            \n            all_preds.extend(predicted.cpu().numpy())\n            all_labels.extend(labels.cpu().numpy())\n    \n    accuracy = 100. * correct / total\n    return accuracy, all_preds, all_labels\n\n# Evaluate on test set\nprint(\"\\n🔍 Evaluating on test set...\")\ntest_accuracy, test_preds, test_labels = evaluate_model(model, test_loader, device)\n\nprint(\"\\n\" + \"=\"*60)\nprint(\"FINAL TEST RESULTS\")\nprint(\"=\"*60)\nprint(f\"✅ Test Accuracy: {test_accuracy:.2f}%\")\n\n# ============================================\n# 9. CONFUSION MATRIX\n# ============================================\n\ndef plot_confusion_matrix(true_labels, pred_labels, classes):\n    \"\"\"Plot confusion matrix\"\"\"\n    cm = confusion_matrix(true_labels, pred_labels)\n    \n    plt.figure(figsize=(12, 10))\n    sns.heatmap(cm, annot=True, fmt='d', cmap='Blues',\n                xticklabels=classes, yticklabels=classes,\n                linewidths=0.5, linecolor='white')\n    plt.xlabel('Predicted', fontsize=12)\n    plt.ylabel('True', fontsize=12)\n    plt.title('Confusion Matrix - CIFAR-10 Classification', fontsize=14, fontweight='bold')\n    plt.tight_layout()\n    plt.savefig('confusion_matrix.png', dpi=300, bbox_inches='tight')\n    plt.show()\n\nprint(\"\\n📊 Generating confusion matrix...\")\nplot_confusion_matrix(test_labels, test_preds, classes)\n\n# ============================================\n# 10. CLASSIFICATION REPORT\n# ============================================\n\nprint(\"\\n\" + \"=\"*60)\nprint(\"CLASSIFICATION REPORT\")\nprint(\"=\"*60)\n\n# Per-class accuracy\nclass_correct = []\nclass_total = []\nfor i in range(10):\n    mask = np.array(test_labels) == i\n    class_correct.append(np.sum(np.array(test_preds)[mask] == i))\n    class_total.append(np.sum(mask))\n\nprint(\"\\nPer-Class Accuracy:\")\nprint(\"-\" * 50)\nfor i, class_name in enumerate(classes):\n    acc = 100. * class_correct[i] / class_total[i] if class_total[i] > 0 else 0\n    print(f\"{class_name:>12}: {acc:>6.2f}% ({class_correct[i]:>4}/{class_total[i]:>4})\")\n\n# Classification report\nprint(\"\\nClassification Report:\")\nprint(\"-\" * 60)\nprint(classification_report(test_labels, test_preds, target_names=classes, digits=4))\n\n# ============================================\n# 11. SHOW SAMPLE PREDICTIONS\n# ============================================\n\ndef show_sample_predictions(model, dataloader, classes, num_samples=10):\n    \"\"\"Display sample predictions\"\"\"\n    model.eval()\n    \n    # Get a batch\n    inputs, labels = next(iter(dataloader))\n    inputs, labels = inputs[:num_samples].to(device), labels[:num_samples]\n    \n    with torch.no_grad():\n        outputs = model(inputs)\n        _, preds = torch.max(outputs, 1)\n    \n    # Denormalize for display\n    mean = np.array([0.4914, 0.4822, 0.4465])\n    std = np.array([0.2023, 0.1994, 0.2010])\n    \n    fig, axes = plt.subplots(2, 5, figsize=(15, 6))\n    axes = axes.flatten()\n    \n    for idx in range(num_samples):\n        # Denormalize\n        img = inputs[idx].cpu().numpy().transpose(1, 2, 0)\n        img = std * img + mean\n        img = np.clip(img, 0, 1)\n        \n        axes[idx].imshow(img)\n        axes[idx].axis('off')\n        \n        true_label = classes[labels[idx].item()]\n        pred_label = classes[preds[idx].item()]\n        is_correct = preds[idx] == labels[idx]\n        color = 'green' if is_correct else 'red'\n        \n        axes[idx].set_title(f'True: {true_label}\\nPred: {pred_label}', \n                           color=color, fontsize=9, fontweight='bold')\n    \n    plt.suptitle('CIFAR-10 Sample Predictions', fontsize=14, fontweight='bold')\n    plt.tight_layout()\n    plt.savefig('sample_predictions.png', dpi=200, bbox_inches='tight')\n    plt.show()\n\nprint(\"\\n🖼️ Displaying sample predictions...\")\nshow_sample_predictions(model, test_loader, classes, num_samples=10)\n\n# ============================================\n# 12. MODEL SUMMARY TABLE\n# ============================================\n\nprint(\"\\n\" + \"=\"*60)\nprint(\"MODEL SUMMARY\")\nprint(\"=\"*60)\n\nprint(f\"\"\"\n┌─────────────────────────────────────────────────────────────┐\n│                    CNN MODEL SUMMARY                        │\n├─────────────────────────────────────────────────────────────┤\n│                                                             │\n│  Architecture:                                              │\n│  ┌─────────────────────────────────────────────────────────┐│\n│  │ Layer               │ Input Size    │ Output Size      ││\n│  ├─────────────────────┼──────────────┼──────────────────┤│\n│  │ Conv1 (3→32, 3x3)   │ 32x32x3      │ 32x32x32         ││\n│  │ ReLU                │ 32x32x32     │ 32x32x32         ││\n│  │ MaxPool (2x2)       │ 32x32x32     │ 16x16x32         ││\n│  │ Conv2 (32→64, 3x3)  │ 16x16x32     │ 16x16x64         ││\n│  │ ReLU                │ 16x16x64     │ 16x16x64         ││\n│  │ MaxPool (2x2)       │ 16x16x64     │ 8x8x64           ││\n│  │ Flatten             │ 8x8x64       │ 4096             ││\n│  │ FC1 (4096→512)      │ 4096         │ 512              ││\n│  │ ReLU                │ 512          │ 512              ││\n│  │ Dropout (0.25)      │ 512          │ 512              ││\n│  │ FC2 (512→10)        │ 512          │ 10               ││\n│  └─────────────────────────────────────────────────────────┘│\n│                                                             │\n│  Total Parameters: {total_params:,}                                    │\n│  Trainable Parameters: {trainable_params:,}                              │\n│                                                             │\n│  Training Results:                                           │\n│  ┌─────────────────────────────────────────────────────────┐│\n│  │ Metric              │ Value                            ││\n│  ├─────────────────────┼──────────────────────────────────┤│\n│  │ Best Validation Acc │ {best_acc*100:>6.2f}%                           ││\n│  │ Test Accuracy       │ {test_accuracy:>6.2f}%                           ││\n│  │ Training Epochs     │ 10                               ││\n│  │ Batch Size          │ {BATCH_SIZE}                                 ││\n│  │ Optimizer           │ Adam                             ││\n│  │ Learning Rate       │ 0.001                            ││\n│  └─────────────────────────────────────────────────────────┘│\n│                                                             │\n└─────────────────────────────────────────────────────────────┘\n\"\"\")\n\n# ============================================\n# 13. SAVE MODEL\n# ============================================\n\ntorch.save({\n    'model_state_dict': model.state_dict(),\n    'architecture': 'CIFAR10CNN',\n    'num_classes': 10,\n    'test_accuracy': test_accuracy,\n    'best_accuracy': best_acc,\n    'history': history,\n    'classes': classes,\n    'student': 'Sara',\n}, 'cifar10_cnn_model.pth')\n\nprint(\"\\n✅ Model saved as 'cifar10_cnn_model.pth'\")\n\n# ============================================\n# 14. FINAL SUMMARY\n# ============================================\n\nprint(\"\\n\" + \"=\"*60)\nprint(\"ASSIGNMENT 1 - PHASE 3 COMPLETE!\")\nprint(\"=\"*60)\nprint(f\"\"\"\n✅ ALL TASKS COMPLETED:\n\n1. ✅ Loaded CIFAR-10 dataset with DataLoader\n2. ✅ Visualized sample images\n3. ✅ Built 2-layer CNN with ReLU and MaxPooling\n4. ✅ Trained for 10 epochs\n5. ✅ Plotted training vs validation loss\n6. ✅ Reported final test accuracy: {test_accuracy:.2f}%\n7. ✅ Displayed 10 sample predictions\n\n📁 Generated Files:\n   - cifar10_samples.png (Sample images)\n   - training_history.png (Loss & accuracy curves)\n   - confusion_matrix.png (Confusion matrix)\n   - sample_predictions.png (Sample predictions)\n   - cifar10_cnn_model.pth (Trained model)\n\n📊 Performance Summary:\n   - Best Validation Accuracy: {best_acc*100:.2f}%\n   - Test Accuracy: {test_accuracy:.2f}%\n   - Total Parameters: {total_params:,}\n   - Training Time: 10 epochs\n\n🎓 Student: Sara\n👩‍🏫 Submitted To: Ma'am Tanzeela\n\"\"\")\nprint(\"=\"*60)","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-06-24T13:15:44.821146Z","iopub.execute_input":"2026-06-24T13:15:44.821823Z","iopub.status.idle":"2026-06-24T13:18:24.657956Z","shell.execute_reply.started":"2026-06-24T13:15:44.821783Z","shell.execute_reply":"2026-06-24T13:18:24.657141Z"}},"outputs":[],"execution_count":null}]}