{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.12.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceType":"competition","sourceId":16017,"databundleVersionId":708189,"isSourceIdPinned":false}],"dockerImageVersionId":31328,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-05-16T16:17:59.513808Z","iopub.execute_input":"2026-05-16T16:17:59.514671Z","iopub.status.idle":"2026-05-16T16:17:59.521935Z","shell.execute_reply.started":"2026-05-16T16:17:59.514637Z","shell.execute_reply":"2026-05-16T16:17:59.521137Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.preprocessing import OneHotEncoder\nimport matplotlib.pyplot as plt\nimport warnings\nwarnings.filterwarnings('ignore')\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-16T16:18:11.722590Z","iopub.execute_input":"2026-05-16T16:18:11.722998Z","iopub.status.idle":"2026-05-16T16:18:11.727999Z","shell.execute_reply.started":"2026-05-16T16:18:11.722965Z","shell.execute_reply":"2026-05-16T16:18:11.727101Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ============================================================\n# 1) LOAD DATA\n# ============================================================\ntrain = pd.read_csv('/kaggle/input/competitions/Kannada-MNIST/train.csv')\ntest = pd.read_csv('/kaggle/input/competitions/Kannada-MNIST/test.csv')\nsample_sub = pd.read_csv('/kaggle/input/competitions/Kannada-MNIST/sample_submission.csv')\n\nprint(\"Train shape:\", train.shape)\nprint(\"Test shape:\", test.shape)\nprint(\"Sample submission columns:\", sample_sub.columns.tolist())\nprint(sample_sub.head())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-16T16:19:27.376534Z","iopub.execute_input":"2026-05-16T16:19:27.377450Z","iopub.status.idle":"2026-05-16T16:19:31.658406Z","shell.execute_reply.started":"2026-05-16T16:19:27.377414Z","shell.execute_reply":"2026-05-16T16:19:31.657589Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ============================================================\n# 2) PREPROCESS TRAIN DATA\n# ============================================================\nX = train.drop(columns=['label']).values.astype(np.float32) / 255.0\ny = train['label'].values.astype(int)\n\nX_train, X_val, y_train, y_val = train_test_split(\n    X, y, test_size=0.2, random_state=42, stratify=y\n)\n\nencoder = OneHotEncoder(sparse_output=False)\ny_train_oh = encoder.fit_transform(y_train.reshape(-1, 1))\ny_val_oh = encoder.transform(y_val.reshape(-1, 1))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-16T16:19:43.108613Z","iopub.execute_input":"2026-05-16T16:19:43.109355Z","iopub.status.idle":"2026-05-16T16:19:43.935543Z","shell.execute_reply.started":"2026-05-16T16:19:43.109319Z","shell.execute_reply":"2026-05-16T16:19:43.934788Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ============================================================\n# 3) DEFINE NEURAL NETWORK\n# ============================================================\nclass NeuralNetwork:\n    def __init__(self, layer_sizes, learning_rate=0.001):\n        self.learning_rate = learning_rate\n        self.weights = []\n        self.biases = []\n\n        np.random.seed(42)\n        for i in range(len(layer_sizes) - 1):\n            limit = np.sqrt(6.0 / (layer_sizes[i] + layer_sizes[i + 1]))\n            w = np.random.uniform(-limit, limit, (layer_sizes[i], layer_sizes[i + 1]))\n            b = np.zeros((1, layer_sizes[i + 1]))\n            self.weights.append(w)\n            self.biases.append(b)\n\n    def relu(self, x):\n        return np.maximum(0, x)\n\n    def relu_derivative(self, x):\n        return (x > 0).astype(np.float32)\n\n    def softmax(self, x):\n        x = x - np.max(x, axis=1, keepdims=True)\n        exps = np.exp(x)\n        return exps / np.sum(exps, axis=1, keepdims=True)\n\n    def forward(self, X):\n        self.z_values = []\n        self.a_values = [X]\n\n        for i in range(len(self.weights) - 1):\n            z = np.dot(self.a_values[-1], self.weights[i]) + self.biases[i]\n            self.z_values.append(z)\n            a = self.relu(z)\n            self.a_values.append(a)\n\n        z = np.dot(self.a_values[-1], self.weights[-1]) + self.biases[-1]\n        self.z_values.append(z)\n        output = self.softmax(z)\n        self.a_values.append(output)\n        return output\n\n    def backward(self, X, y):\n        m = X.shape[0]\n        delta = self.a_values[-1] - y\n\n        for i in range(len(self.weights) - 1, -1, -1):\n            dw = np.dot(self.a_values[i].T, delta) / m\n            db = np.sum(delta, axis=0, keepdims=True) / m\n\n            if i > 0:\n                delta = np.dot(delta, self.weights[i].T) * self.relu_derivative(self.z_values[i - 1])\n\n            self.weights[i] -= self.learning_rate * dw\n            self.biases[i] -= self.learning_rate * db\n\n    def compute_loss(self, output, y):\n        return -np.sum(y * np.log(np.clip(output, 1e-10, 1.0))) / y.shape[0]\n\n    def train(self, X_train, y_train, X_val, y_val, epochs=100, batch_size=32):\n        train_losses, val_losses, val_accuracies = [], [], []\n        m = X_train.shape[0]\n        num_batches = m // batch_size\n\n        for epoch in range(epochs):\n            idx = np.random.permutation(m)\n            X_shuffled = X_train[idx]\n            y_shuffled = y_train[idx]\n            epoch_loss = 0\n\n            for batch in range(num_batches):\n                start = batch * batch_size\n                end = start + batch_size\n\n                xb = X_shuffled[start:end]\n                yb = y_shuffled[start:end]\n\n                out = self.forward(xb)\n                epoch_loss += self.compute_loss(out, yb)\n                self.backward(xb, yb)\n\n            train_losses.append(epoch_loss / num_batches)\n\n            val_out = self.forward(X_val)\n            val_loss = self.compute_loss(val_out, y_val)\n            val_losses.append(val_loss)\n\n            val_pred = np.argmax(val_out, axis=1)\n            val_true = np.argmax(y_val, axis=1)\n            val_acc = np.mean(val_pred == val_true)\n            val_accuracies.append(val_acc)\n\n            if (epoch + 1) % 20 == 0:\n                print(f\"Epoch {epoch+1}/{epochs} | Train Loss: {train_losses[-1]:.4f} | Val Loss: {val_loss:.4f} | Val Acc: {val_acc:.4f}\")\n\n        return train_losses, val_losses, val_accuracies\n\n    def predict(self, X):\n        return np.argmax(self.forward(X), axis=1)\n\n    def evaluate(self, X, y):\n        pred = self.predict(X)\n        true = np.argmax(y, axis=1)\n        acc = np.mean(pred == true)\n        return acc, pred\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-16T16:20:13.061017Z","iopub.execute_input":"2026-05-16T16:20:13.061532Z","iopub.status.idle":"2026-05-16T16:20:13.079377Z","shell.execute_reply.started":"2026-05-16T16:20:13.061498Z","shell.execute_reply":"2026-05-16T16:20:13.077841Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ============================================================\n# 4) TRAIN MODEL\n# ============================================================\nnn = NeuralNetwork([784, 256, 128, 10], learning_rate=0.001)\n\ntrain_losses, val_losses, val_accuracies = nn.train(\n    X_train, y_train_oh,\n    X_val, y_val_oh,\n    epochs=100,\n    batch_size=32\n)\n\ntrain_acc, _ = nn.evaluate(X_train, y_train_oh)\nval_acc, val_pred = nn.evaluate(X_val, y_val_oh)\n\nprint(\"\\nTraining Accuracy:\", round(train_acc, 4))\nprint(\"Validation Accuracy:\", round(val_acc, 4))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-16T16:20:31.995774Z","iopub.execute_input":"2026-05-16T16:20:31.996786Z","iopub.status.idle":"2026-05-16T16:26:46.074655Z","shell.execute_reply.started":"2026-05-16T16:20:31.996730Z","shell.execute_reply":"2026-05-16T16:26:46.073954Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ============================================================\n# 5) PLOT TRAINING CURVES\n# ============================================================\nplt.figure(figsize=(14, 5))\n\nplt.subplot(1, 2, 1)\nplt.plot(train_losses, label='Train Loss')\nplt.plot(val_losses, label='Val Loss')\nplt.title('Loss Over Epochs')\nplt.xlabel('Epoch')\nplt.ylabel('Loss')\nplt.legend()\nplt.grid(True)\n\nplt.subplot(1, 2, 2)\nplt.plot(val_accuracies, label='Val Accuracy', color='green')\nplt.title('Validation Accuracy Over Epochs')\nplt.xlabel('Epoch')\nplt.ylabel('Accuracy')\nplt.legend()\nplt.grid(True)\n\nplt.tight_layout()\nplt.savefig('/kaggle/working/training_results.png', dpi=300)\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-16T16:26:54.617257Z","iopub.execute_input":"2026-05-16T16:26:54.618422Z","iopub.status.idle":"2026-05-16T16:26:55.691437Z","shell.execute_reply.started":"2026-05-16T16:26:54.618383Z","shell.execute_reply":"2026-05-16T16:26:55.690410Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ============================================================\n# 6) PREPARE TEST PREDICTIONS\n# ============================================================\n# Kaggle test.csv has an id column and 784 pixel columns\ntest_ids = test['id'].values\nX_test = test.drop(columns=['id']).values.astype(np.float32) / 255.0\n\ntest_pred = nn.predict(X_test)\n\n# Match sample submission exactly: same column names, same row order\nsubmission = pd.DataFrame({\n    'id': test_ids,\n    'label': test_pred.astype(int)\n})\n\nprint(\"\\nSubmission preview:\")\nprint(submission.head())\nprint(\"Submission shape:\", submission.shape)\nprint(\"Any NaN values:\", submission.isnull().sum().sum())\n\n# Save submission\nsubmission.to_csv('/kaggle/working/submission.csv', index=False)\nprint(\"\\nSaved submission.csv to /kaggle/working/\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-16T16:27:01.607121Z","iopub.execute_input":"2026-05-16T16:27:01.608070Z","iopub.status.idle":"2026-05-16T16:27:01.706948Z","shell.execute_reply.started":"2026-05-16T16:27:01.608037Z","shell.execute_reply":"2026-05-16T16:27:01.706228Z"}},"outputs":[],"execution_count":null}]}