{"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":"nvidiaTeslaT4","dataSources":[{"sourceId":3004,"databundleVersionId":861823,"sourceType":"competition"}],"dockerImageVersionId":31193,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"#Cell 1 – Imports\n\nimport numpy as np\nimport pandas as pd\nimport torch\nimport torch.nn as nn\nimport torch.optim as optim\nfrom torch.utils.data import Dataset, DataLoader\nfrom sklearn.model_selection import train_test_split\nimport torchvision.transforms as T\nfrom tqdm import tqdm","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-11-29T01:27:55.988100Z","iopub.execute_input":"2025-11-29T01:27:55.988424Z","iopub.status.idle":"2025-11-29T01:28:04.204895Z","shell.execute_reply.started":"2025-11-29T01:27:55.988372Z","shell.execute_reply":"2025-11-29T01:28:04.204112Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#CELL 2 — Seed\n\nDEVICE = \"cuda\"\n\ndef seed_everything(seed=42):\n    import random, os\n    random.seed(seed)\n    os.environ[\"PYTHONHASHSEED\"] = str(seed)\n    np.random.seed(seed)\n    torch.manual_seed(seed)\n    torch.cuda.manual_seed_all(seed)\n    torch.backends.cudnn.deterministic = True\n    torch.backends.cudnn.benchmark = False\n\nseed_everything()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T01:28:09.775679Z","iopub.execute_input":"2025-11-29T01:28:09.776098Z","iopub.status.idle":"2025-11-29T01:28:09.788174Z","shell.execute_reply.started":"2025-11-29T01:28:09.776075Z","shell.execute_reply":"2025-11-29T01:28:09.787550Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#CELL 3 — Load Data\n\ntrain_df = pd.read_csv(\"/kaggle/input/digit-recognizer/train.csv\")\ntest_df  = pd.read_csv(\"/kaggle/input/digit-recognizer/test.csv\")\n\nX = train_df.drop(\"label\", axis=1).values.reshape(-1, 1, 28, 28)\ny = train_df[\"label\"].values\nX_test = test_df.values.reshape(-1, 1, 28, 28)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T01:28:13.644922Z","iopub.execute_input":"2025-11-29T01:28:13.645433Z","iopub.status.idle":"2025-11-29T01:28:17.253799Z","shell.execute_reply.started":"2025-11-29T01:28:13.645411Z","shell.execute_reply":"2025-11-29T01:28:17.253209Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#Cell 4 – Show Sample Images\nimport matplotlib.pyplot as plt\n\nfig, axes = plt.subplots(2, 5, figsize=(10, 4))\nfor ax, (img, label) in zip(axes.flatten(), zip(X[:10], y[:10])):\n    ax.imshow(img[0], cmap=\"gray\")\n    ax.set_title(f\"Label: {label}\")\n    ax.axis(\"off\")\nplt.tight_layout()\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T01:28:55.571762Z","iopub.execute_input":"2025-11-29T01:28:55.572038Z","iopub.status.idle":"2025-11-29T01:28:56.107758Z","shell.execute_reply.started":"2025-11-29T01:28:55.572017Z","shell.execute_reply":"2025-11-29T01:28:56.107035Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#CELL 5 — Dataset\n\nclass MNIST_Dataset(Dataset):\n    def __init__(self, X, y=None, train=True):\n        self.X = torch.tensor(X, dtype=torch.float32) / 255.0\n        self.y = y\n        self.train = train\n\n        self.aug = T.Compose([\n            T.RandomRotation(10),\n            T.RandomAffine(0, translate=(0.12, 0.12)),\n        ])\n\n    def __len__(self):\n        return len(self.X)\n\n    def __getitem__(self, idx):\n        x = self.X[idx]\n\n        if self.train:\n            x = x.unsqueeze(0)\n            x = self.aug(x).squeeze(0)\n            return x, torch.tensor(self.y[idx], dtype=torch.long)\n\n        return x\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T01:29:00.724663Z","iopub.execute_input":"2025-11-29T01:29:00.724955Z","iopub.status.idle":"2025-11-29T01:29:00.731163Z","shell.execute_reply.started":"2025-11-29T01:29:00.724932Z","shell.execute_reply":"2025-11-29T01:29:00.730302Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#Cell 6 — Dataloaders\n\nX_train, X_val, y_train, y_val = train_test_split(\n    X, y, test_size=0.1, random_state=42, stratify=y\n)\n\ntrain_ds = MNIST_Dataset(X_train, y_train, train=True)\nval_ds   = MNIST_Dataset(X_val, y_val, train=True)\ntest_ds  = MNIST_Dataset(X_test, train=False)\n\ntrain_loader = DataLoader(train_ds, batch_size=256, shuffle=True)\nval_loader   = DataLoader(val_ds, batch_size=256, shuffle=False)\ntest_loader  = DataLoader(test_ds, batch_size=256, shuffle=False)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T01:29:04.863488Z","iopub.execute_input":"2025-11-29T01:29:04.863921Z","iopub.status.idle":"2025-11-29T01:29:05.427259Z","shell.execute_reply.started":"2025-11-29T01:29:04.863898Z","shell.execute_reply":"2025-11-29T01:29:05.426520Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#CELL 7 — STRONG CNN\n\nclass StrongMNIST(nn.Module):\n    def __init__(self):\n        super().__init__()\n\n        self.net = nn.Sequential(\n            nn.Conv2d(1, 64, 3, padding=1), nn.ReLU(),\n            nn.Conv2d(64, 64, 3, padding=1), nn.ReLU(),\n            nn.MaxPool2d(2),\n\n            nn.Conv2d(64, 128, 3, padding=1), nn.ReLU(),\n            nn.Conv2d(128, 128, 3, padding=1), nn.ReLU(),\n            nn.MaxPool2d(2),\n\n            nn.Flatten(),\n            nn.Linear(128*7*7, 512), nn.ReLU(),\n            nn.Dropout(0.2),\n            nn.Linear(512, 10)\n        )\n\n    def forward(self, x):\n        return self.net(x)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T01:29:08.674968Z","iopub.execute_input":"2025-11-29T01:29:08.675559Z","iopub.status.idle":"2025-11-29T01:29:08.680725Z","shell.execute_reply.started":"2025-11-29T01:29:08.675533Z","shell.execute_reply":"2025-11-29T01:29:08.680013Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# CELL 8 — Train (with loss tracking)\ndef train_model(model, epochs=25):\n    criterion = nn.CrossEntropyLoss(label_smoothing=0.05)\n    optimizer = optim.Adam(model.parameters(), lr=0.001)\n\n    train_losses = []\n\n    model.train()\n    for epoch in range(epochs):\n        running_loss = 0.0\n\n        for xb, yb in train_loader:\n            xb, yb = xb.to(DEVICE), yb.to(DEVICE)\n            optimizer.zero_grad()\n\n            out = model(xb)\n            loss = criterion(out, yb)\n\n            loss.backward()\n            optimizer.step()\n\n            running_loss += loss.item()\n\n        epoch_loss = running_loss / len(train_loader)\n        train_losses.append(epoch_loss)\n        print(f\"Epoch {epoch+1}/{epochs} - Training Loss: {epoch_loss:.4f}\")\n\n    return train_losses","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T01:29:12.679105Z","iopub.execute_input":"2025-11-29T01:29:12.679392Z","iopub.status.idle":"2025-11-29T01:29:12.685156Z","shell.execute_reply.started":"2025-11-29T01:29:12.679353Z","shell.execute_reply":"2025-11-29T01:29:12.684519Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# CELL 9 — Train 5-Model Ensemble and store losses\nall_train_losses = []\nmodels = []\n\nfor i in range(5):\n    print(f\"\\nTraining Model {i+1}/5...\")\n    m = StrongMNIST().to(DEVICE)\n    losses = train_model(m, epochs=25)\n    all_train_losses.append(losses)\n    models.append(m)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T01:29:17.900481Z","iopub.execute_input":"2025-11-29T01:29:17.900800Z","iopub.status.idle":"2025-11-29T02:49:54.942405Z","shell.execute_reply.started":"2025-11-29T01:29:17.900772Z","shell.execute_reply":"2025-11-29T02:49:54.941701Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# === VALIDATION EVALUATION ===\nfrom sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay\nimport matplotlib.pyplot as plt\n\ndef evaluate_model(model):\n    model.eval()\n    criterion = nn.CrossEntropyLoss()\n    val_loss = 0\n    correct = 0\n    total = 0\n\n    preds = []\n    targets = []\n\n    with torch.no_grad():\n        for xb, yb in val_loader:\n            xb, yb = xb.to(DEVICE), yb.to(DEVICE)\n            out = model(xb)\n\n            loss = criterion(out, yb)\n            val_loss += loss.item()\n\n            pred = out.argmax(1)\n            correct += (pred == yb).sum().item()\n            total += len(yb)\n\n            preds.extend(pred.cpu().numpy())\n            targets.extend(yb.cpu().numpy())\n\n    return val_loss / len(val_loader), correct / total, preds, targets\n\n\n# Use model 1 for validation plots\nval_loss, val_acc, preds, labels = evaluate_model(models[0])\n\nprint(\"Validation Loss:\", val_loss)\nprint(\"Validation Accuracy:\", val_acc)\n\n# === CONFUSION MATRIX ===\ncm = confusion_matrix(labels, preds)\n\nplt.figure(figsize=(7, 7))           # ← FIGURE SIZE GOES HERE\ndisp = ConfusionMatrixDisplay(confusion_matrix=cm)\n\ndisp.plot(cmap=\"Blues\", values_format=\"d\")\nplt.title(\"Confusion Matrix (Model 1)\")\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T02:51:43.582212Z","iopub.execute_input":"2025-11-29T02:51:43.582606Z","iopub.status.idle":"2025-11-29T02:51:47.854293Z","shell.execute_reply.started":"2025-11-29T02:51:43.582585Z","shell.execute_reply":"2025-11-29T02:51:47.853507Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# === TRAINING CURVES ===\n\nplt.figure(figsize=(14, 6))\n\nfor i, losses in enumerate(all_train_losses):\n    plt.plot(losses, label=f\"Model {i+1}\")\n\nplt.title(\"Training Loss Curve (All 5 Models)\")\nplt.xlabel(\"Epoch\")\nplt.ylabel(\"Loss\")\nplt.legend()\nplt.grid(True)\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T02:51:55.960039Z","iopub.execute_input":"2025-11-29T02:51:55.960796Z","iopub.status.idle":"2025-11-29T02:51:56.186883Z","shell.execute_reply.started":"2025-11-29T02:51:55.960770Z","shell.execute_reply":"2025-11-29T02:51:56.186116Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"preds_final = []\n\ntta = T.Compose([T.RandomRotation(10)])\n\nfor batch in test_loader:\n    batch = batch.to(DEVICE)\n    outputs = []\n\n    for m in models:\n        with torch.no_grad():                      # ← disable gradients\n            o1 = m(batch).softmax(dim=1).detach().cpu().numpy()\n            o2 = m(tta(batch)).softmax(dim=1).detach().cpu().numpy()\n        outputs.append((o1 + o2) / 2)\n\n    mean_output = np.mean(outputs, axis=0)\n    preds_final.extend(np.argmax(mean_output, axis=1))\n\nsubmission = pd.DataFrame({\n    \"ImageId\": np.arange(1, len(preds_final)+1),\n    \"Label\": preds_final\n})\n\nsubmission.to_csv(\"submission.csv\", index=False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-29T02:52:03.126790Z","iopub.execute_input":"2025-11-29T02:52:03.127063Z","iopub.status.idle":"2025-11-29T02:52:15.615830Z","shell.execute_reply.started":"2025-11-29T02:52:03.127044Z","shell.execute_reply":"2025-11-29T02:52:15.615220Z"}},"outputs":[],"execution_count":null}]}