{"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":31090,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"### Step 1: Import Libraries","metadata":{}},{"cell_type":"code","source":"import torch\nimport torch.nn as nn\nfrom torch.autograd import Variable\nfrom torch.utils.data import DataLoader\nimport pandas as pd\nfrom sklearn.model_selection import train_test_split\nimport numpy as np\nimport matplotlib.pyplot as plt","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-07-30T13:05:52.548810Z","iopub.execute_input":"2025-07-30T13:05:52.549073Z","iopub.status.idle":"2025-07-30T13:05:52.553584Z","shell.execute_reply.started":"2025-07-30T13:05:52.549055Z","shell.execute_reply":"2025-07-30T13:05:52.552746Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Step 2: Dataset Preparation and Preprocessing\nIn this step, we prepare the dataset for training and validation:\n- We load the MNIST dataset from `train.csv` and `test.csv` using `pandas`.\n- The **targets** (labels) are separated from the **features** (pixel values).\n- We normalize the features by dividing by 255, scaling them to the range [0, 1].\n- The dataset is then split into training and validation sets using `train_test_split`.\n- Finally, we convert the datasets into PyTorch tensors, which are required for training in PyTorch.\n- The test dataset is loaded and normalized similarly.\n","metadata":{}},{"cell_type":"code","source":"# Prepare Dataset\n# load data\ntrain = pd.read_csv(r\"/kaggle/input/digit-recognizer/train.csv\",dtype = np.float32)\ntest = pd.read_csv(r\"/kaggle/input/digit-recognizer/test.csv\",dtype = np.float32)\n\ntargets_numpy = train.label.values\nfeatures_numpy = train.loc[:,train.columns != \"label\"].values/255 # normalization\n\ntest_features_numpy = test.values/255\n\nfeatures_train, features_validate, targets_train, targets_validate = train_test_split(features_numpy,\n                                                                             targets_numpy,\n                                                                             test_size = 0.2,\n                                                                             random_state = 42) \n\nfeaturesTrain = torch.from_numpy(features_train)\ntargetsTrain = torch.from_numpy(targets_train).type(torch.LongTensor)\n\nfeaturesValidate = torch.from_numpy(features_validate)\ntargetsValidate = torch.from_numpy(targets_validate).type(torch.LongTensor)\n\nfeaturesTest = torch.from_numpy(test_features_numpy)\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-30T13:05:52.554857Z","iopub.execute_input":"2025-07-30T13:05:52.555397Z","iopub.status.idle":"2025-07-30T13:05:56.127310Z","shell.execute_reply.started":"2025-07-30T13:05:52.555371Z","shell.execute_reply":"2025-07-30T13:05:56.126542Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Step 3: Define CNN Model\nIn this step, we define the **CNN (Convolutional Neural Network)** model architecture:\n- **Convolutional layers** are used to automatically extract features from images.\n- **Activation functions** like ReLU are applied after each convolutional layer to introduce non-linearity.\n- **Fully connected layers** are added at the end of the network to output class probabilities for digit classification.\n- CNNs are well-suited for image classification tasks, which is why we use this architecture.\n","metadata":{}},{"cell_type":"code","source":"class CNNModel(nn.Module):\n    def __init__(self):\n        super(CNNModel, self).__init__()\n        \n        # Convolution 1\n        self.cnn1 = nn.Conv2d(in_channels=1, out_channels=16, kernel_size=5, stride=1, padding=0)\n        self.relu1 = nn.ReLU()\n        \n        # Max pool 1\n        self.maxpool1 = nn.MaxPool2d(kernel_size=2)\n     \n        # Convolution 2\n        self.cnn2 = nn.Conv2d(in_channels=16, out_channels=32, kernel_size=5, stride=1, padding=0)\n        self.relu2 = nn.ReLU()\n        \n        # Max pool 2\n        self.maxpool2 = nn.MaxPool2d(kernel_size=2)\n        \n        # Fully connected 1\n        self.fc1 = nn.Linear(32 * 4 * 4, 10) \n    \n    def forward(self, x):\n        # Convolution 1\n        out = self.cnn1(x)\n        out = self.relu1(out)\n        \n        # Max pool 1\n        out = self.maxpool1(out)\n        \n        # Convolution 2 \n        out = self.cnn2(out)\n        out = self.relu2(out)\n        \n        # Max pool 2 \n        out = self.maxpool2(out)\n        \n        # flatten\n        out = out.view(out.size(0), -1)\n\n        # Linear function (readout)\n        out = self.fc1(out)\n        \n        return out\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-30T13:05:56.128129Z","iopub.execute_input":"2025-07-30T13:05:56.128396Z","iopub.status.idle":"2025-07-30T13:05:56.135090Z","shell.execute_reply.started":"2025-07-30T13:05:56.128373Z","shell.execute_reply":"2025-07-30T13:05:56.134448Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Step 4: DataLoader Setup and Model Initialization\n\nIn this step, we set up the datasets, DataLoaders, and model:\n\n1. **Batch Size and Epochs:**\n   - `batch_size = 64`: Number of samples per batch.\n   - `num_epochs = 1000`: Total training epochs.\n\n2. **Device Setup:**\n   - `device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")`: Use GPU if available, otherwise CPU.\n\n3. **Dataset Preparation:**\n   - Create PyTorch **TensorDatasets** for training, validation, and test sets.\n   - The **training** and **validation** datasets contain features and labels, while the **test** dataset contains only features.\n\n4. **DataLoaders:**\n   - `train_loader`, `validate_loader`, and `test_loader` load the datasets in batches for efficient training and evaluation.\n\n5. **Model Initialization:**\n   - `model = CNNModel()`: Initialize the CNN model.\n   - `model.to(device)`: Move the model to the selected device (GPU or CPU).\n\nThis prepares the data and model for training and evaluation.\n","metadata":{}},{"cell_type":"code","source":"batch_size = 64\nnum_epochs = 1000\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n\n# Pytorch train and validate sets\ntrain = torch.utils.data.TensorDataset(featuresTrain,targetsTrain)\nvalidate = torch.utils.data.TensorDataset(featuresValidate,targetsValidate)\ntest = torch.utils.data.TensorDataset(featuresTest)\n\n# data loader\ntrain_loader = torch.utils.data.DataLoader(train, batch_size = batch_size, shuffle = False)\nvalidate_loader = torch.utils.data.DataLoader(validate, batch_size = batch_size, shuffle = False)\ntest_loader = torch.utils.data.DataLoader(test, batch_size = batch_size, shuffle = False)\n    \n# Create CNN\nmodel = CNNModel()\nmodel.to(device)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-30T13:05:56.136762Z","iopub.execute_input":"2025-07-30T13:05:56.137763Z","iopub.status.idle":"2025-07-30T13:05:56.162763Z","shell.execute_reply.started":"2025-07-30T13:05:56.137740Z","shell.execute_reply":"2025-07-30T13:05:56.161975Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Step 5: Loss Function, Optimizer, and Training Loop with Early Stopping\n\nIn this step, we define the **loss function** and **optimizer** and implement the **training loop** with **early stopping**:\n\n1. **Loss Function and Optimizer:**\n   - `CrossEntropyLoss`: Used for multi-class classification.\n   - `Adam optimizer`: Adaptive learning rate optimizer for efficient training.\n\n2. **Training Loop:**\n   - For each batch, perform forward pass, calculate loss, and update model parameters.\n   - Every 50 iterations, evaluate the model on the validation set to calculate accuracy.\n\n3. **Early Stopping:**\n   - If the validation accuracy doesn’t improve for `early_stopping_patience` epochs, training stops early.\n\n4. **Logging:**\n   - Track and print loss and accuracy every 500 iterations for monitoring progress.\n\nThis ensures efficient training and prevents overfitting by stopping early if performance plateaus.\n","metadata":{}},{"cell_type":"code","source":"# Loss function and optimizer\nerror = nn.CrossEntropyLoss()\noptimizer = torch.optim.Adam(model.parameters(), lr=0.001)  \n\n# Training loop with early stopping\ncount = 0\nloss_list = []\niteration_list = []\naccuracy_list = []\n\n# Track the best accuracy for early stopping\nbest_accuracy = 0\nearly_stopping_patience = 5\npatience_counter = 0\n\nfor epoch in range(num_epochs):\n    model.train()\n    for i, (images, labels) in enumerate(train_loader):\n        images, labels = images.to(device), labels.to(device)\n\n        optimizer.zero_grad()\n\n        outputs = model(images.view(-1, 1, 28, 28))\n\n        loss = error(outputs, labels)\n\n        loss.backward()\n\n        optimizer.step()\n\n        count += 1\n\n        if count % 50 == 0:\n            model.eval() \n            correct = 0\n            total = 0\n            with torch.no_grad(): \n                for images, labels in validate_loader:\n                    images, labels = images.to(device), labels.to(device)\n                    outputs = model(images.view(-1, 1, 28, 28))\n                    predicted = torch.max(outputs.data, 1)[1]\n                    total += len(labels)\n                    correct += (predicted == labels).sum()\n\n            accuracy = 100 * correct / float(total)\n\n            loss_list.append(loss.item())\n            iteration_list.append(count)\n            accuracy_list.append(accuracy)\n\n            if count % 500 == 0:\n                print(f'Iteration: {count}  Loss: {loss.item()}  Accuracy: {accuracy} %')\n\n            # Early stopping check\n            if accuracy > best_accuracy:\n                best_accuracy = accuracy\n                patience_counter = 0\n            else:\n                patience_counter += 1\n\n            if patience_counter >= early_stopping_patience:\n                print(\"Early stopping triggered.\")\n                break\n    if patience_counter >= early_stopping_patience:\n        break\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-30T13:05:56.163473Z","iopub.execute_input":"2025-07-30T13:05:56.163669Z","iopub.status.idle":"2025-07-30T13:06:01.784387Z","shell.execute_reply.started":"2025-07-30T13:05:56.163653Z","shell.execute_reply":"2025-07-30T13:06:01.783694Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Step 6: Evaluate on validate data\nThis is final step, where we evaluate the trained model on given validate dataset. And then export the result into .csv file for submitting.","metadata":{}},{"cell_type":"code","source":"model.eval()\nprediction_labels = []\n\nfor images in test_loader:\n    images = images[0].to(device)\n    input_image = Variable(images.view(-1,1,28,28))\n    \n    outputs = model(input_image)\n    predicted = torch.max(outputs.data, 1)[1]\n    predicted = [int(x) for x in predicted]\n    prediction_labels.extend(predicted)\n    \ndf = pd.DataFrame({\n    'ImageId': list(range(1,len(prediction_labels)+1)),\n    'Label': prediction_labels\n})\n    ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-30T13:06:01.785055Z","iopub.execute_input":"2025-07-30T13:06:01.785316Z","iopub.status.idle":"2025-07-30T13:06:02.552030Z","shell.execute_reply.started":"2025-07-30T13:06:01.785291Z","shell.execute_reply":"2025-07-30T13:06:02.551497Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-30T13:06:02.552702Z","iopub.execute_input":"2025-07-30T13:06:02.552877Z","iopub.status.idle":"2025-07-30T13:06:02.560700Z","shell.execute_reply.started":"2025-07-30T13:06:02.552863Z","shell.execute_reply":"2025-07-30T13:06:02.560108Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df.to_csv(\"submission.csv\", index = False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-30T13:06:02.561334Z","iopub.execute_input":"2025-07-30T13:06:02.561561Z","iopub.status.idle":"2025-07-30T13:06:02.591097Z","shell.execute_reply.started":"2025-07-30T13:06:02.561546Z","shell.execute_reply":"2025-07-30T13:06:02.590635Z"}},"outputs":[],"execution_count":null}]}