{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.11","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":31040,"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\n\nimport matplotlib.pyplot as plt\nimport seaborn as sns\n\nimport sklearn\nimport torch\n\nfrom torch.utils.data import DataLoader, TensorDataset\nimport torch.nn as nn\nfrom torch.optim import Adam\n\nfrom sklearn.model_selection import train_test_split","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-06-29T17:34:35.590953Z","iopub.execute_input":"2025-06-29T17:34:35.591433Z","iopub.status.idle":"2025-06-29T17:34:38.417451Z","shell.execute_reply.started":"2025-06-29T17:34:35.591409Z","shell.execute_reply":"2025-06-29T17:34:38.416668Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df = pd.read_csv(\"/kaggle/input/digit-recognizer/train.csv\")\ntest = pd.read_csv(\"/kaggle/input/digit-recognizer/test.csv\")\nsample = pd.read_csv(\"/kaggle/input/digit-recognizer/sample_submission.csv\")\n\ntrain = df.drop([\"label\"], axis=1)\nlabel = df[[\"label\"]]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-29T17:34:38.418927Z","iopub.execute_input":"2025-06-29T17:34:38.419266Z","iopub.status.idle":"2025-06-29T17:34:43.171177Z","shell.execute_reply.started":"2025-06-29T17:34:38.419248Z","shell.execute_reply":"2025-06-29T17:34:43.170323Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train.shape, test.shape, sample.shape","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-29T17:34:43.172087Z","iopub.execute_input":"2025-06-29T17:34:43.172359Z","iopub.status.idle":"2025-06-29T17:34:43.179604Z","shell.execute_reply.started":"2025-06-29T17:34:43.172335Z","shell.execute_reply":"2025-06-29T17:34:43.178645Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"<b>there are 784 pixels, so 28 x 28 pixel image</b>","metadata":{}},{"cell_type":"code","source":"train.head(3)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-29T17:34:43.180638Z","iopub.execute_input":"2025-06-29T17:34:43.180891Z","iopub.status.idle":"2025-06-29T17:34:43.222695Z","shell.execute_reply.started":"2025-06-29T17:34:43.180870Z","shell.execute_reply":"2025-06-29T17:34:43.221999Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train.isnull().sum().sum(), test.isnull().sum().sum()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-29T17:34:43.224100Z","iopub.execute_input":"2025-06-29T17:34:43.224516Z","iopub.status.idle":"2025-06-29T17:34:43.281817Z","shell.execute_reply.started":"2025-06-29T17:34:43.224498Z","shell.execute_reply":"2025-06-29T17:34:43.281188Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train.duplicated().sum(), test.duplicated().sum()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-29T17:34:43.282555Z","iopub.execute_input":"2025-06-29T17:34:43.282778Z","iopub.status.idle":"2025-06-29T17:34:44.083181Z","shell.execute_reply.started":"2025-06-29T17:34:43.282760Z","shell.execute_reply":"2025-06-29T17:34:44.082418Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Exploratory Data Analysis","metadata":{}},{"cell_type":"code","source":"print(df.label.nunique())\ndf.label.unique()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-29T17:34:44.084014Z","iopub.execute_input":"2025-06-29T17:34:44.084357Z","iopub.status.idle":"2025-06-29T17:34:44.090747Z","shell.execute_reply.started":"2025-06-29T17:34:44.084325Z","shell.execute_reply":"2025-06-29T17:34:44.090149Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"fig, ax = plt.subplots(3,3, figsize=(6,6))\nax = ax.flatten()\n\nfor i in range(9):\n    X = train.iloc[i].values.reshape(28, 28)\n    ax[i].imshow(X, cmap=\"gray\")\n\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-29T17:34:44.091537Z","iopub.execute_input":"2025-06-29T17:34:44.092285Z","iopub.status.idle":"2025-06-29T17:34:44.788408Z","shell.execute_reply.started":"2025-06-29T17:34:44.092267Z","shell.execute_reply":"2025-06-29T17:34:44.787737Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Data Preprocessing","metadata":{}},{"cell_type":"code","source":"train_tensor = (torch.tensor(train.values, dtype=torch.float32)) / 255\nlabel_tensor = (torch.tensor(label.values, dtype=torch.long).reshape(-1))\n\ntest_tensor = (torch.tensor(test.values, dtype=torch.float32)) / 255\n\ntrain_tensor = train_tensor.reshape(-1, 1, 28, 28)\ntest_tensor = test_tensor.reshape(-1, 1, 28, 28)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-29T17:34:44.789107Z","iopub.execute_input":"2025-06-29T17:34:44.789356Z","iopub.status.idle":"2025-06-29T17:34:45.034101Z","shell.execute_reply.started":"2025-06-29T17:34:44.789334Z","shell.execute_reply":"2025-06-29T17:34:45.033262Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_tensor, val_tensor, train_label, val_label = train_test_split(train_tensor, label_tensor, test_size=0.2, random_state=42)\n\ntrain_dataset = TensorDataset(train_tensor, train_label)\nval_dataset = TensorDataset(val_tensor, val_label)\ntest_dataset = TensorDataset(test_tensor)\n\ntrain_loader = DataLoader(dataset=train_dataset, batch_size=64, shuffle=True)\nval_loader = DataLoader(dataset=val_dataset, batch_size=64, shuffle=True)\ntest_loader = DataLoader(dataset=test_dataset, batch_size=64, shuffle=False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-29T17:34:45.034938Z","iopub.execute_input":"2025-06-29T17:34:45.035153Z","iopub.status.idle":"2025-06-29T17:34:45.365999Z","shell.execute_reply.started":"2025-06-29T17:34:45.035138Z","shell.execute_reply":"2025-06-29T17:34:45.365168Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Modeling","metadata":{}},{"cell_type":"code","source":"import torch.nn.functional as F\n\nclass CNNModel(nn.Module):\n    def __init__(self):\n        super(CNNModel, self).__init__()\n\n        self.conv1 = nn.Conv2d(in_channels=1, out_channels=64, kernel_size=3, padding=1)  # (28x28)\n        self.conv2 = nn.Conv2d(in_channels=64, out_channels=64, kernel_size=3, padding=1)  # (28x28)\n        self.conv3 = nn.Conv2d(in_channels=64, out_channels=128, kernel_size=3, padding=1)  # (28x28)\n        self.pool1 = nn.MaxPool2d(kernel_size=2)  # (14x14)\n\n        self.conv4 = nn.Conv2d(in_channels=128, out_channels=128, kernel_size=3, padding=1)  # (14x14)\n        self.conv5 = nn.Conv2d(in_channels=128, out_channels=192, kernel_size=3, padding=1)  # (14x14)\n        self.pool2 = nn.MaxPool2d(kernel_size=2)  # (7x7)\n\n        self.conv6 = nn.Conv2d(in_channels=192, out_channels=192, kernel_size=5, padding=2)  # (7x7)\n        self.pool3 = nn.MaxPool2d(kernel_size=2, padding=1)  # (4x4), due to padding\n\n        self.flatten = nn.Flatten()\n        self.fc1 = nn.Linear(192 * 4 * 4, 256)\n        self.fc2 = nn.Linear(256, 10)\n\n    def forward(self, x):\n        x = F.relu(self.conv1(x))    # [B, 64, 28, 28]\n        x = F.relu(self.conv2(x))    # [B, 64, 28, 28]\n        x = F.relu(self.conv3(x))    # [B, 128, 28, 28]\n        x = self.pool1(x)            # [B, 128, 14, 14]\n\n        x = F.relu(self.conv4(x))    # [B, 128, 14, 14]\n        x = F.relu(self.conv5(x))    # [B, 192, 14, 14]\n        x = self.pool2(x)            # [B, 192, 7, 7]\n\n        x = F.relu(self.conv6(x))    # [B, 192, 7, 7]\n        x = self.pool3(x)            # [B, 192, 4, 4]\n\n        x = self.flatten(x)          # [B, 192*4*4]\n        x = F.relu(self.fc1(x))      # [B, 256]\n        x = self.fc2(x)              # [B, 10]\n\n        return x","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-29T17:35:57.529481Z","iopub.execute_input":"2025-06-29T17:35:57.530058Z","iopub.status.idle":"2025-06-29T17:35:57.537517Z","shell.execute_reply.started":"2025-06-29T17:35:57.530027Z","shell.execute_reply":"2025-06-29T17:35:57.536750Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"'''\nclass CNNModel(nn.Module):    \n    \n    def __init__(self):\n        super(CNNModel, self).__init__()\n\n        self.cnn1 = nn.Conv2d(in_channels=1, out_channels=32, kernel_size=10, stride=1, padding=0)\n        self.relu = nn.ReLU()\n        self.maxPool = nn.MaxPool2d(kernel_size=2, stride=2)\n\n        self.cnn2 = nn.Conv2d(in_channels=32, out_channels=64, kernel_size=6, stride=1, padding=0)\n\n        #self.cnn3 = nn.Conv2d(in_channels=64, out_channels=64, kernel_size=4, stride=1, padding=0)\n        \n        self.flatten = nn.Flatten()\n        self.fc = nn.Linear(in_features=64*2*2, out_features=10)\n    \n    def forward(self, x):\n        a = self.maxPool(self.relu(self.cnn1(x)))\n        b = self.maxPool(self.relu(self.cnn2(a)))\n        #c = self.maxPool(self.relu(self.cnn3(b)))\n        d = self.fc(self.flatten(b))\n        return d\n\n'''","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-29T16:46:18.571179Z","iopub.execute_input":"2025-06-29T16:46:18.571401Z","iopub.status.idle":"2025-06-29T16:46:18.577149Z","shell.execute_reply.started":"2025-06-29T16:46:18.571383Z","shell.execute_reply":"2025-06-29T16:46:18.576524Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def train_one_epoch(model, dataloader, loss_fn, optimizer, device):\n    model.train()\n    total_loss = 0\n    \n    for X_batch, y_batch in dataloader:\n        X_batch, y_batch = X_batch.to(device), y_batch.to(device)\n\n        optimizer.zero_grad()\n        \n        output = model(X_batch)\n        loss = loss_fn(output, y_batch)\n               \n        loss.backward()\n        optimizer.step()\n        \n        total_loss += loss.item()\n\n    return total_loss / len(dataloader)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-29T17:36:01.089915Z","iopub.execute_input":"2025-06-29T17:36:01.090188Z","iopub.status.idle":"2025-06-29T17:36:01.094954Z","shell.execute_reply.started":"2025-06-29T17:36:01.090168Z","shell.execute_reply":"2025-06-29T17:36:01.094202Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def evaluate(model, dataloader, loss_fn, device):\n    model.eval()\n    total_loss = 0\n    correct = 0\n    total = 0\n\n    with torch.no_grad():\n        for X_batch, y_batch in dataloader:\n            X_batch, y_batch = X_batch.to(device), y_batch.to(device)\n\n            output = model(X_batch)\n            loss = loss_fn(output, y_batch)\n            total_loss += loss.item()\n\n            preds = output.argmax(dim=1)\n            correct += (preds == y_batch).sum().item()\n            total += y_batch.size(0)\n\n    return (total_loss / len(dataloader)), (correct/total)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-29T17:36:01.329541Z","iopub.execute_input":"2025-06-29T17:36:01.330421Z","iopub.status.idle":"2025-06-29T17:36:01.335143Z","shell.execute_reply.started":"2025-06-29T17:36:01.330398Z","shell.execute_reply":"2025-06-29T17:36:01.334479Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def predict(model, dataloader, device):\n    model.eval()\n    predictions = []\n    \n    with torch.no_grad():\n        for (X_batch,) in dataloader:\n            X_batch = X_batch.to(device)\n            \n            output = model(X_batch)\n            preds = output.argmax(dim=1)\n            predictions.extend(preds.cpu().numpy())\n\n    return predictions","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-29T17:36:07.231598Z","iopub.execute_input":"2025-06-29T17:36:07.232072Z","iopub.status.idle":"2025-06-29T17:36:07.236342Z","shell.execute_reply.started":"2025-06-29T17:36:07.232047Z","shell.execute_reply":"2025-06-29T17:36:07.235623Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"device = (\"cuda\" if torch.cuda.is_available() else \"cpu\")\nnum_epochs = 25\nlearning_rate = 0.001\nloss_fn = nn.CrossEntropyLoss()\n\ncnn_model = CNNModel().to(device)\n\noptimizer = Adam(cnn_model.parameters(), lr=learning_rate)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-29T17:36:10.190806Z","iopub.execute_input":"2025-06-29T17:36:10.191536Z","iopub.status.idle":"2025-06-29T17:36:10.214560Z","shell.execute_reply.started":"2025-06-29T17:36:10.191510Z","shell.execute_reply":"2025-06-29T17:36:10.213827Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"best_model_state = None\nbest_accuracy = 0.0\ncheck_epoch = 0\n\nfor i in range(num_epochs):\n    train_loss = train_one_epoch(model=cnn_model, dataloader=train_loader, loss_fn=loss_fn, optimizer=optimizer, device=device)\n    val_loss, acc = evaluate(model=cnn_model, dataloader=val_loader, loss_fn=loss_fn, device=device)\n    print(f\"Epoch {i+1} -  Training Loss: {train_loss:.4f}  Validation Loss: {val_loss:.4f}  Accuracy: {(acc*100):.2f}\")\n\n    if acc > best_accuracy:\n        best_accuracy = acc\n        check_epoch = i+1\n        best_model_state = cnn_model.state_dict()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-29T17:55:35.605439Z","iopub.execute_input":"2025-06-29T17:55:35.605732Z","iopub.status.idle":"2025-06-29T17:59:54.891550Z","shell.execute_reply.started":"2025-06-29T17:55:35.605713Z","shell.execute_reply":"2025-06-29T17:59:54.890569Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(check_epoch)\n\ncnn_model.load_state_dict(best_model_state)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-29T17:59:54.892950Z","iopub.execute_input":"2025-06-29T17:59:54.893259Z","iopub.status.idle":"2025-06-29T17:59:54.900460Z","shell.execute_reply.started":"2025-06-29T17:59:54.893239Z","shell.execute_reply":"2025-06-29T17:59:54.899747Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test_labels = predict(model=cnn_model, dataloader=test_loader, device=device)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-29T17:59:54.901274Z","iopub.execute_input":"2025-06-29T17:59:54.901539Z","iopub.status.idle":"2025-06-29T17:59:57.680870Z","shell.execute_reply.started":"2025-06-29T17:59:54.901519Z","shell.execute_reply":"2025-06-29T17:59:57.680223Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test = pd.DataFrame(columns = [\"ImageId\", \"Label\"])\n\ntest[\"ImageId\"] = np.arange(1, 28001, 1)\ntest[\"Label\"] = test_labels\n\ntest.to_csv(\"submission.csv\", index=False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-29T17:59:57.682157Z","iopub.execute_input":"2025-06-29T17:59:57.682717Z","iopub.status.idle":"2025-06-29T17:59:57.765665Z","shell.execute_reply.started":"2025-06-29T17:59:57.682696Z","shell.execute_reply":"2025-06-29T17:59:57.765028Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"0.95714, 0.98728","metadata":{}},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}