{"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":3004,"databundleVersionId":861823,"isSourceIdPinned":false},{"sourceType":"competition","sourceId":96416,"databundleVersionId":11456366,"isSourceIdPinned":false},{"sourceType":"kernelVersion","sourceId":122858469,"isSourceIdPinned":false}],"dockerImageVersionId":31286,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os\nimport torch\nimport torch.nn as nn\nimport torch.optim as optim\nimport matplotlib.pyplot as plt\nimport pandas as pd\nfrom torch.utils.data import Dataset, DataLoader\n\n# ==========================\n# Hyperparameters\n# ==========================\ninput_size = 784       \nnum_classes = 10       \nnum_epochs = 20        \nbatch_size = 200       \nlearning_rate = 0.001\nhidden_layers_default = [400, 400]\n\n# ==========================\n# Device\n# ==========================\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nprint(f\"Using device: {device}\")\n\n# ==========================\n# Load Kaggle MNIST CSV\n# ==========================\ntrain_path = \"/kaggle/input/competitions/digit-recognizer/train.csv\"\ndata = pd.read_csv(train_path)\n\n# Split into 80% train, 20% validation\ntrain_size = int(0.8 * len(data))\ntrain_df = data[:train_size]\nval_df = data[train_size:]\n\n# ==========================\n# Dataset Class\n# ==========================\nclass MNISTDataset(Dataset):\n    def __init__(self, dataframe):\n        self.X = torch.tensor(dataframe.iloc[:, 1:].values, dtype=torch.float32) / 255.0\n        self.y = torch.tensor(dataframe.iloc[:, 0].values, dtype=torch.long)\n\n    def __len__(self):\n        return len(self.X)\n\n    def __getitem__(self, idx):\n        return self.X[idx], self.y[idx]\n\ntrain_dataset = MNISTDataset(train_df)\nval_dataset = MNISTDataset(val_df)\n\ntrain_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)\nval_loader = DataLoader(val_dataset, batch_size=batch_size, shuffle=False)\n\n# ==========================\n# MLP Model\n# ==========================\nclass MLP(nn.Module):\n    def __init__(self, hidden_layers):\n        super(MLP, self).__init__()\n        layers = []\n        in_features = input_size\n        for h in hidden_layers:\n            layers.append(nn.Linear(in_features, h))\n            layers.append(nn.ReLU())\n            in_features = h\n        layers.append(nn.Linear(in_features, num_classes))\n        self.model = nn.Sequential(*layers)\n\n    def forward(self, x):\n        return self.model(x)\n\n# ==========================\n# Weight Initialization\n# ==========================\ndef initialize_weights(model, method=\"xavier\"):\n    for m in model.modules():\n        if isinstance(m, nn.Linear):\n            if method == \"zero\":\n                nn.init.constant_(m.weight, 0)\n            elif method == \"uniform\":\n                nn.init.uniform_(m.weight, 0, 1)\n            elif method == \"normal\":\n                nn.init.normal_(m.weight, 0, 1)\n            elif method == \"xavier\":\n                nn.init.xavier_normal_(m.weight)\n            nn.init.constant_(m.bias, 0)\n\n# ==========================\n# Evaluation Function\n# ==========================\ndef evaluate(model, loader):\n    model.eval()\n    correct = 0\n    total = 0\n    with torch.no_grad():\n        for images, labels in loader:\n            images, labels = images.to(device), labels.to(device)\n            outputs = model(images)\n            _, predicted = torch.max(outputs.data, 1)\n            total += labels.size(0)\n            correct += (predicted == labels).sum().item()\n    return 100 * correct / total\n\n# ==========================\n# Training Function\n# ==========================\ndef train_model(hidden_layers=hidden_layers_default, init_method=\"xavier\", optimizer_name=\"Adam\", verbose=False):\n    model = MLP(hidden_layers).to(device)\n    initialize_weights(model, init_method)\n    criterion = nn.CrossEntropyLoss()\n\n    if optimizer_name == \"SGD\":\n        optimizer = optim.SGD(model.parameters(), lr=learning_rate)\n    elif optimizer_name == \"RMSProp\":\n        optimizer = optim.RMSprop(model.parameters(), lr=learning_rate)\n    elif optimizer_name == \"Adagrad\":\n        optimizer = optim.Adagrad(model.parameters(), lr=learning_rate)\n    elif optimizer_name == \"Adam\":\n        optimizer = optim.Adam(model.parameters(), lr=learning_rate)\n    else:\n        raise ValueError(f\"Unsupported optimizer: {optimizer_name}\")\n\n    train_acc_list = []\n    val_acc_list = []\n\n    for epoch in range(num_epochs):\n        model.train()\n        for images, labels in train_loader:\n            images, labels = images.to(device), labels.to(device)\n            outputs = model(images)\n            loss = criterion(outputs, labels)\n            \n            optimizer.zero_grad()\n            loss.backward()\n            optimizer.step()\n\n        train_acc = evaluate(model, train_loader)\n        val_acc = evaluate(model, val_loader)\n        train_acc_list.append(train_acc)\n        val_acc_list.append(val_acc)\n\n        if verbose:\n            print(f\"Epoch {epoch+1}/{num_epochs} | Train Acc: {train_acc:.2f}% | Val Acc: {val_acc:.2f}%\")\n\n    return train_acc_list, val_acc_list, model\n\n# ==========================\n# Run Experiments\n# ==========================\n\n# ---------- Task A: Basic MLP ----------\ntrain_acc, val_acc, best_model = train_model([400,400],\"xavier\",\"Adam\")\nplt.figure(figsize=(7,4))\nplt.plot(train_acc, label=\"Train\")\nplt.plot(val_acc, label=\"Validation\")\nplt.title(\"A) Basic MLP (400-400) - Xavier + Adam\")\nplt.xlabel(\"Epoch\")\nplt.ylabel(\"Accuracy (%)\")\nplt.legend()\nplt.show()\n\n# ---------- Task B: Weight Initialization ----------\nprint(\"\\nB) Weight Initialization Comparison:\")\ninits = [\"zero\",\"uniform\",\"normal\",\"xavier\"]\nfor init in inits:\n    train_acc, val_acc, _ = train_model([400,400], init, \"Adam\")\n    print(f\"{init}: Train Acc={train_acc[-1]:.2f}% | Val Acc={val_acc[-1]:.2f}%\")\n\n# ---------- Task C: Architecture Comparison ----------\nprint(\"\\nC) Architecture Comparison:\")\narchitectures = {\"100\":[100],\"400\":[400],\"100-100\":[100,100]}\nfor name, arch in architectures.items():\n    train_acc, val_acc, _ = train_model(arch, \"xavier\", \"Adam\")\n    print(f\"{name}: Train Acc={train_acc[-1]:.2f}% | Val Acc={val_acc[-1]:.2f}%\")\n\n# ---------- Task D: Optimizer Comparison ----------\nprint(\"\\nD) Optimizer Comparison:\")\noptimizers = [\"SGD\",\"RMSProp\",\"Adagrad\",\"Adam\"]\nfor opt in optimizers:\n    train_acc, val_acc, _ = train_model([400,400], \"xavier\", opt)\n    plt.figure(figsize=(7,4))\n    plt.plot(train_acc, label=\"Train\")\n    plt.plot(val_acc, label=\"Validation\")\n    plt.title(f\"Optimizer: {opt}\")\n    plt.xlabel(\"Epoch\")\n    plt.ylabel(\"Accuracy (%)\")\n    plt.legend()\n    plt.show()\n    print(f\"{opt}: Train Acc={train_acc[-1]:.2f}% | Val Acc={val_acc[-1]:.2f}%\")\n\n# ==========================\n# Removed Kaggle submission logic\n# ==========================\n","metadata":{"execution":{"iopub.status.busy":"2026-02-22T13:27:19.263026Z","iopub.execute_input":"2026-02-22T13:27:19.263802Z","iopub.status.idle":"2026-02-22T13:39:03.265051Z","shell.execute_reply.started":"2026-02-22T13:27:19.263769Z","shell.execute_reply":"2026-02-22T13:39:03.264232Z"},"trusted":true},"outputs":[],"execution_count":null}]}