{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.6.6","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[],"dockerImageVersionId":28755,"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\n\n# Use the kagglehub client library to attach Kaggle resources like competitions, datasets, and models to your session\n# Learn more about kagglehub: https://github.com/Kaggle/kagglehub/blob/main/README.md\n\nimport kagglehub\n# kagglehub.dataset_download('<owner>/<dataset-slug>')","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport numpy as np\nimport pandas as pd\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom torch.utils.data import TensorDataset, DataLoader\nfrom sklearn.model_selection import train_test_split\n\n\n# ----------------------------------------------------------------------\n# Config - \n# ----------------------------------------------------------------------\nDATA_DIR = \"/kaggle/input/competitions/digit-recognizer-challenge\" if os.path.exists(\"/kaggle/input/competitions/digit-recognizer-challenge\") else \".\"\nOUT_DIR = \"/kaggle/working\" if os.path.exists(\"/kaggle/working\") else \".\"\nEPOCHS = 10\nBATCH_SIZE = 128\nLR = 1e-3\nVAL_FRACTION = 0.1\nSEED = 42\n\ntorch.manual_seed(SEED)\nDEVICE = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n\n\n# ----------------------------------------------------------------------\n#  Load\n# ----------------------------------------------------------------------\ndef load_data():\n    train_path = os.path.join(DATA_DIR, \"train.csv\")\n    test_path = os.path.join(DATA_DIR, \"test.csv\")\n    if not os.path.exists(train_path):\n        raise FileNotFoundError(\n            f\"Missing {train_path}. Attach the Digit Recognizer competition \"\n            f\"data on Kaggle, or place train.csv/test.csv next to this script.\"\n        )\n    train_df = pd.read_csv(train_path)\n    test_df = pd.read_csv(test_path)\n    return train_df, test_df\n\n\n# ----------------------------------------------------------------------\n#  Preprocess\n# ----------------------------------------------------------------------\ndef preprocess(train_df, test_df):\n    y = train_df[\"label\"].values\n    X = train_df.drop(columns=[\"label\"]).values.astype(np.float32) / 255.0\n    X_test = test_df.values.astype(np.float32) / 255.0\n\n    X_train, X_val, y_train, y_val = train_test_split(\n        X, y, test_size=VAL_FRACTION, random_state=SEED, stratify=y\n    )\n\n    def to_tensor(X):\n        return torch.tensor(X).reshape(-1, 1, 28, 28)\n\n    train_ds = TensorDataset(to_tensor(X_train), torch.tensor(y_train, dtype=torch.long))\n    val_ds = TensorDataset(to_tensor(X_val), torch.tensor(y_val, dtype=torch.long))\n    test_tensor = to_tensor(X_test)\n\n    train_loader = DataLoader(train_ds, batch_size=BATCH_SIZE, shuffle=True)\n    val_loader = DataLoader(val_ds, batch_size=BATCH_SIZE, shuffle=False)\n\n    return train_loader, val_loader, test_tensor\n\n\n# ----------------------------------------------------------------------\n#  Model \n# ----------------------------------------------------------------------\nclass BaselineCNN(nn.Module):\n    def __init__(self):\n        super().__init__()\n        self.conv1 = nn.Conv2d(1, 16, 3, padding=1)\n        self.conv2 = nn.Conv2d(16, 32, 3, padding=1)\n        self.pool = nn.MaxPool2d(2, 2)\n        self.fc1 = nn.Linear(32 * 7 * 7, 128)\n        self.fc2 = nn.Linear(128, 10)\n        self.dropout = nn.Dropout(0.25)\n\n    def forward(self, x):\n        x = self.pool(F.relu(self.conv1(x)))   # 28x28 -> 14x14\n        x = self.pool(F.relu(self.conv2(x)))   # 14x14 -> 7x7\n        x = x.view(x.size(0), -1)\n        x = F.relu(self.fc1(x))\n        x = self.dropout(x)\n        return self.fc2(x)\n\n\n# ----------------------------------------------------------------------\n#  Train + Evaluate\n# ----------------------------------------------------------------------\ndef train_and_evaluate(model, train_loader, val_loader):\n    optimizer = torch.optim.Adam(model.parameters(), lr=LR)\n    criterion = nn.CrossEntropyLoss()\n\n    for epoch in range(1, EPOCHS + 1):\n        model.train()\n        for imgs, labels in train_loader:\n            imgs, labels = imgs.to(DEVICE), labels.to(DEVICE)\n            optimizer.zero_grad()\n            loss = criterion(model(imgs), labels)\n            loss.backward()\n            optimizer.step()\n\n        val_acc = evaluate(model, val_loader)\n        print(f\"Epoch {epoch:2d}/{EPOCHS} | val_acc={val_acc:.4f}\")\n\n    return model\n\n\ndef evaluate(model, loader):\n    model.eval()\n    correct, total = 0, 0\n    with torch.no_grad():\n        for imgs, labels in loader:\n            imgs, labels = imgs.to(DEVICE), labels.to(DEVICE)\n            preds = model(imgs).argmax(dim=1)\n            correct += (preds == labels).sum().item()\n            total += labels.size(0)\n    return correct / total\n\n\n# ----------------------------------------------------------------------\n#  Predict + Submit\n# ----------------------------------------------------------------------\ndef predict_and_submit(model, test_tensor):\n    model.eval()\n    preds = []\n    with torch.no_grad():\n        for i in range(0, len(test_tensor), BATCH_SIZE):\n            batch = test_tensor[i : i + BATCH_SIZE].to(DEVICE)\n            preds.append(model(batch).argmax(dim=1).cpu().numpy())\n    preds = np.concatenate(preds)\n\n    submission = pd.DataFrame({\n        \"ImageId\": np.arange(1, len(preds) + 1),\n        \"Label\": preds,\n    })\n    out_path = os.path.join(OUT_DIR, \"submission.csv\")\n    submission.to_csv(out_path, index=False)\n    print(f\"\\nSaved submission to {out_path}\")\n\n\n# ----------------------------------------------------------------------\n# full pipeline\n# ----------------------------------------------------------------------\ndef main():\n    print(f\"Using device: {DEVICE}\")\n\n    train_df, test_df = load_data()\n    print(f\"Loaded: train={train_df.shape}, test={test_df.shape}\")\n\n    train_loader, val_loader, test_tensor = preprocess(train_df, test_df)\n\n    model = BaselineCNN().to(DEVICE)\n    model = train_and_evaluate(model, train_loader, val_loader)\n\n    final_val_acc = evaluate(model, val_loader)\n    print(f\"\\nFinal validation accuracy: {final_val_acc:.4f}\")\n\n    predict_and_submit(model, test_tensor)\n\n\nif __name__ == \"__main__\":\n    main()","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}