{"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":3004,"databundleVersionId":861823,"sourceType":"competition"}],"dockerImageVersionId":31089,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.metrics import confusion_matrix\nimport torch\nimport torch.nn as nn\nimport torch.optim as optim\nfrom torch.utils.data import DataLoader, TensorDataset, Dataset\nimport torchvision.transforms as transforms\n\nsample_submission_df = pd.read_csv(\"/kaggle/input/digit-recognizer/sample_submission.csv\")\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)\nY = train_df[\"label\"]\nX = X.values/255.0\n\nX_img = X.reshape(-1,28,28)\ntest = test_df.values/255.0\ntest_img = test.reshape(-1,28,28)\n\nX_train, X_val, Y_train, Y_val = train_test_split(X_img, Y.values, test_size=0.1)\n\nclass MNISTDataset(Dataset):\n    def __init__(self, images, labels = None, train = True):\n        self.images = images\n        self.labels = labels\n        self.train = train\n\n        if train:\n            self.transform = transforms.Compose([transforms.RandomAffine(10, translate = (0.2, 0.2),scale = (0.8, 1.2), shear = 5), transforms.ToTensor(), ])\n        \n        else:\n            self.transform = transforms.Compose([transforms.ToTensor()])\n\n    def __len__(self):\n        return len(self.images)\n\n    def __getitem__(self, idx):\n        img = self.images[idx]\n        img = transforms.functional.to_pil_image(img)\n        img = self.transform(img)\n        \n        if self.labels is not None:\n            label = self.labels[idx]\n            return img, label\n\n        return img\n        \n        \ntrain_dataset = MNISTDataset(X_train, Y_train, train = True)\nval_dataset = MNISTDataset(X_val, Y_val, train = False)\ntest_dataset = MNISTDataset(test_img, labels = None, train = False)\n\ntrain_loader = DataLoader(train_dataset, batch_size = 32, shuffle = True)\nval_loader = DataLoader(val_dataset, batch_size = 32, shuffle = False)\ntest_loader = DataLoader(test_dataset, batch_size = 32, shuffle = False)\n\nclass CNN(nn.Module):\n    def __init__(self):\n        super().__init__()\n        self.conv = nn.Sequential(\n            nn.Conv2d(1, 32, kernel_size = 3, padding = 1),\n            nn.BatchNorm2d(32),\n            nn.ReLU(), \n            nn.MaxPool2d(2,2),\n            nn.Dropout(0.25),\n                nn.Conv2d(32, 64, kernel_size = 3, padding = 1),\n            nn.BatchNorm2d(64),\n            nn.ReLU(),\n            nn.MaxPool2d(2,2),\n            nn.Dropout(0.2),\n            nn.Conv2d(64,128, kernel_size = 3, padding = 1),\n            nn.BatchNorm2d(128),\n            nn.ReLU(),\n            nn.MaxPool2d(2,2),\n            nn.Dropout(0.2)\n            \n        )\n        self.fc = nn.Sequential(\n            nn.Linear(128*3*3,128),\n            nn.ReLU(),\n            nn.Dropout(0.3),\n            nn.Linear(128, 10)\n            \n        )\n\n    def forward(self, x):\n        x = self.conv(x)\n        x = x.view(x.size(0), -1)\n        x = self.fc(x)\n        return x\n\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nmodel = CNN().to(device)\n\ncriterion = nn.CrossEntropyLoss()\noptimizer = optim.Adam(model.parameters(), lr = 0.001)\n\ndef evaluate(model, dataloader, device, num_classes = 10):\n    model.eval()\n    all_preds = []\n    all_labels = []\n    with torch.no_grad():\n        for imgs, labels in dataloader:\n            imgs, labels = imgs.to(device), labels.to(device)\n            outputs = model(imgs)\n            preds = outputs.argmax(dim = 1)\n            all_preds.extend(preds.cpu().numpy())\n            all_labels.extend(labels.cpu().numpy())\n    all_preds, all_labels = np.array(all_preds), np.array(all_labels)\n    cm = confusion_matrix(all_labels, all_preds,labels = list(range(num_classes)))\n    acc = np.trace(cm)/np.sum(cm)\n    print(f\"Overall Accuracy: {acc: .4f}\")\n    print(\"Per Class Accuracies: \")\n    for cls in range(num_classes):\n        acc = cm[cls, cls]/cm[cls].sum() if cm[cls].sum() > 0 else 0\n        row = cm[cls].copy()\n        row[cls] = -1\n        most_confused_with = row.argmax()\n        print(f\"Digit {cls}: acc = {acc:.4f}, Most confused with: {most_confused_with}\")\n    return cm\n        \nif __name__ == \"__main__\":\n    best_val = 0\n    for epoch in range(20):\n        model.train()\n        total_loss = 0\n        for imgs, labels in train_loader:\n            imgs, labels = imgs.to(device), labels.to(device)\n            optimizer.zero_grad()\n            outputs = model(imgs)\n            loss = criterion(outputs, labels)\n            loss.backward()\n            optimizer.step()\n            total_loss += loss.item()\n\n        avg_loss = total_loss/len(train_loader)\n        print(\"\\n\")\n        print(f\"Epoch {epoch+1}: Training Loss = {avg_loss}\")\n        cm_train = evaluate(model, train_loader, device)\n        print(\"\\n Validation Set: \")\n        cm_val = evaluate(model, val_loader, device)\n        val_acc = np.trace(cm_val)/np.sum(cm_val)\n        if val_acc > best_val:\n            best_val = val_acc\n            torch.save(model.state_dict(), \"best_model.pth\")\n            print(\"Saved Best model !\")\n\n    model.load_state_dict(torch.load(\"best_model.pth\"))\n    model.eval()\n    preds = []\n    with torch.no_grad():\n        for imgs in test_loader:\n            imgs = imgs.to(device)\n            outputs = model(imgs)\n            predicted = outputs.argmax(dim=1)\n            preds.extend(predicted.cpu().numpy())\n    \n    # 3. Assign predictions and save\n    sample_submission_df[\"Label\"] = preds\n    sample_submission_df.to_csv(\"submission.csv\", index=False)","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-09-14T09:41:55.585112Z","iopub.execute_input":"2025-09-14T09:41:55.585421Z","iopub.status.idle":"2025-09-14T09:49:01.902757Z","shell.execute_reply.started":"2025-09-14T09:41:55.585398Z","shell.execute_reply":"2025-09-14T09:49:01.902022Z"}},"outputs":[],"execution_count":null}]}