{"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":"nvidiaTeslaT4","dataSources":[{"sourceId":108418,"databundleVersionId":13147963,"sourceType":"competition"}],"dockerImageVersionId":31090,"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\ndf = pd.read_csv(\"/kaggle/input/binary-biplob-can-you-guess-the-chess-opening/chess_dataset/train.csv\")\nencoder = dict(zip([\"A\", \"B\", \"C\", \"D\", \"E\"], [0,1,2,3,4]))\ntest_df = pd.read_csv(\"/kaggle/input/binary-biplob-can-you-guess-the-chess-opening/chess_dataset/test.csv\")\ndf[\"eco_volume\"] = df[\"eco_volume\"].apply(lambda x: encoder.get(x,x))\nimport copy\n# os.environ[\"CUDA_LAUNCH_BLOCKING\"] = \"1\"","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-07-30T15:29:43.70357Z","iopub.execute_input":"2025-07-30T15:29:43.703904Z","iopub.status.idle":"2025-07-30T15:29:44.020831Z","shell.execute_reply.started":"2025-07-30T15:29:43.703878Z","shell.execute_reply":"2025-07-30T15:29:44.020066Z"}},"outputs":[],"execution_count":1},{"cell_type":"code","source":"len(df[\"opening_name\"].unique())\ndf[\"opening_eng\"] = df[\"opening_name\"].str.split(\":\").str[0]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-30T15:29:45.943898Z","iopub.execute_input":"2025-07-30T15:29:45.944854Z","iopub.status.idle":"2025-07-30T15:29:45.961125Z","shell.execute_reply.started":"2025-07-30T15:29:45.944807Z","shell.execute_reply":"2025-07-30T15:29:45.960379Z"}},"outputs":[],"execution_count":2},{"cell_type":"code","source":"from sklearn.model_selection import train_test_split\nfrom PIL import Image\nimport os\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom torchvision import transforms as T\nfrom torch.utils.data import Dataset, DataLoader\ntrain_df, dev_df = train_test_split(df, test_size = 0.2)\nimage_dir = \"/kaggle/input/binary-biplob-can-you-guess-the-chess-opening/chess_dataset/train_images\"\nclass custom_dataset(Dataset):\n    def __init__(self, df, image_dir, transform = None):\n        self.df = df\n        self.transform = transform\n        self.image_dir = image_dir\n    def __len__(self):\n        return len(self.df)\n    def __getitem__(self,idx):\n        row = self.df.iloc[idx]\n        label = row[\"eco_volume\"]\n        image_id = row[\"image_id\"]+\".jpg\"\n        img_path = os.path.join(self.image_dir, image_id)\n        img = Image.open(img_path).convert(\"RGB\")\n        if self.transform:\n            img = self.transform(img)\n        return img, int(label)\ntrain_transform = T.Compose([\n    T.Resize((256,256)),\n    T.ToTensor(),\n    T.Normalize(mean = [0.7322, 0.5626, 0.3963],\n               std = [0.3183, 0.2537, 0.2185])\n])\ntest_transform = T.Compose([\n    T.Resize((256,256)),\n    T.ToTensor(),\n    T.Normalize(mean = [0.7322, 0.5626, 0.3963],\n               std = [0.3183, 0.2537, 0.2185])\n])\ntrain_dataset = custom_dataset(train_df, image_dir, transform = train_transform)\ntrain_loader = DataLoader(train_dataset, batch_size = 16, drop_last = True, shuffle = True)\ndev_dataset = custom_dataset(dev_df, image_dir, transform = test_transform)\ndev_loader = DataLoader(dev_dataset, batch_size = 16, drop_last = False, shuffle = False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-30T15:31:55.895752Z","iopub.execute_input":"2025-07-30T15:31:55.896555Z","iopub.status.idle":"2025-07-30T15:31:55.911319Z","shell.execute_reply.started":"2025-07-30T15:31:55.896524Z","shell.execute_reply":"2025-07-30T15:31:55.910535Z"}},"outputs":[],"execution_count":4},{"cell_type":"code","source":"device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nprint(f\"Using device: {device}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-30T15:31:57.374925Z","iopub.execute_input":"2025-07-30T15:31:57.375231Z","iopub.status.idle":"2025-07-30T15:31:57.470121Z","shell.execute_reply.started":"2025-07-30T15:31:57.375209Z","shell.execute_reply":"2025-07-30T15:31:57.469456Z"}},"outputs":[{"name":"stdout","text":"Using device: cuda\n","output_type":"stream"}],"execution_count":5},{"cell_type":"code","source":"class CNN224(nn.Module):\n    def __init__(self):\n        super(CNN224, self).__init__()\n\n        self.features = nn.Sequential(\n            # Block 1\n            nn.Conv2d(3, 32, kernel_size=3, padding=1),\n            nn.BatchNorm2d(32),\n            nn.ReLU(),\n            nn.Conv2d(32, 32, kernel_size=3, padding=1),\n            nn.BatchNorm2d(32),\n            nn.ReLU(),\n            nn.MaxPool2d(2),  # 224 → 112\n\n            # Block 2\n            nn.Conv2d(32, 64, kernel_size=3, padding=1),\n            nn.BatchNorm2d(64),\n            nn.ReLU(),\n            nn.Conv2d(64, 64, kernel_size=3, padding=1),\n            nn.BatchNorm2d(64),\n            nn.ReLU(),\n            nn.MaxPool2d(2),  # 112 → 56\n\n            # Block 3\n            nn.Conv2d(64, 128, kernel_size=3, padding=1),\n            nn.BatchNorm2d(128),\n            nn.ReLU(),\n            nn.Conv2d(128, 128, kernel_size=3, padding=1),\n            nn.BatchNorm2d(128),\n            nn.ReLU(),\n            nn.MaxPool2d(2),  # 56 → 28\n\n            # Block 4\n            nn.Conv2d(128, 256, kernel_size=3, padding=1),\n            nn.BatchNorm2d(256),\n            nn.ReLU(),\n            nn.Conv2d(256, 256, kernel_size=3, padding=1),\n            nn.BatchNorm2d(256),\n            nn.ReLU(),\n            nn.MaxPool2d(2),  # 28 → 14\n        )\n\n        self.classifier = nn.Sequential(\n            nn.Flatten(),                         # 256×14×14 = 50176\n            nn.Linear(65536, 1024),\n            nn.BatchNorm1d(1024),\n            nn.ReLU(),\n            nn.Dropout(0.5),\n            nn.Linear(1024, 512),\n            nn.BatchNorm1d(512),\n            nn.ReLU(),\n            nn.Dropout(0.3),\n            nn.Linear(512, 5)\n        )\n\n    def forward(self, x):\n        x = self.features(x)\n        x = self.classifier(x)\n        return x","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-30T15:33:23.824978Z","iopub.execute_input":"2025-07-30T15:33:23.825597Z","iopub.status.idle":"2025-07-30T15:33:23.833305Z","shell.execute_reply.started":"2025-07-30T15:33:23.825572Z","shell.execute_reply":"2025-07-30T15:33:23.832591Z"}},"outputs":[],"execution_count":6},{"cell_type":"code","source":"import numpy as np, copy, torch\nfrom torch.cuda.amp import autocast, GradScaler\nmodel = CNN224()\ndevice = torch.device(\"cuda\")\nmodel.to(device)\nlossfun = nn.CrossEntropyLoss(label_smoothing = 0.05)\noptimizer = torch.optim.AdamW(model.parameters(), lr = 0.0001, weight_decay = 0.02)\nuse_amp = (device.type == \"cuda\")\nscaler  = GradScaler(enabled=use_amp)\nscheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size = 5, gamma = 0.985)\nnum_epochs = 40\nbest_acc = -float(\"inf\")\nbest_model = None\ntrain_loss, dev_loss, train_acc, dev_acc = [], [], [], []\n\nfor epochi in range(num_epochs):\n    model.train()\n    batch_loss, batch_acc = [], []\n\n    for x, y in train_loader:\n        x, y = x.to(device), y.to(device)\n        optimizer.zero_grad(set_to_none=True)\n\n        with autocast(enabled=use_amp):\n            yHat = model(x)\n            loss = lossfun(yHat, y)\n\n        scaler.scale(loss).backward()\n        scaler.step(optimizer)\n        scaler.update()\n\n        batch_acc.append((torch.argmax(yHat, dim=1) == y).float().mean().item())\n        batch_loss.append(loss.item())\n\n    train_loss.append(np.mean(batch_loss))\n    train_acc.append(np.mean(batch_acc))\n\n    # ----- validation -----\n    model.eval()\n    batch_loss, batch_acc = [], []\n    with torch.no_grad():\n        for x, y in dev_loader:\n            x, y = x.to(device), y.to(device)\n            with autocast(enabled=use_amp):\n                yHat = model(x)\n                loss = lossfun(yHat, y)\n            batch_acc.append((torch.argmax(yHat, dim=1) == y).float().mean().item())\n            batch_loss.append(loss.item())\n\n    dev_loss.append(np.mean(batch_loss))\n    dev_acc.append(np.mean(batch_acc))\n\n    if best_acc < dev_acc[epochi]:\n        best_acc = dev_acc[epochi]\n        best_model = copy.deepcopy(model.state_dict())\n    scheduler.step()\n    print(f\"for epoch {epochi+1} train_acc: {train_acc[epochi]:.4f}, train_loss: {train_loss[epochi]:.4f}\")\n    print(f\"for epoch {epochi+1} dev_acc: {dev_acc[epochi]:.4f}, dev_loss: {dev_loss[epochi]:.4f}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-30T15:55:24.821163Z","iopub.execute_input":"2025-07-30T15:55:24.821426Z","iopub.status.idle":"2025-07-30T16:14:28.479533Z","shell.execute_reply.started":"2025-07-30T15:55:24.821408Z","shell.execute_reply":"2025-07-30T16:14:28.478912Z"}},"outputs":[{"name":"stderr","text":"/tmp/ipykernel_36/4142875268.py:9: FutureWarning: `torch.cuda.amp.GradScaler(args...)` is deprecated. Please use `torch.amp.GradScaler('cuda', args...)` instead.\n  scaler  = GradScaler(enabled=use_amp)\n/tmp/ipykernel_36/4142875268.py:24: FutureWarning: `torch.cuda.amp.autocast(args...)` is deprecated. Please use `torch.amp.autocast('cuda', args...)` instead.\n  with autocast(enabled=use_amp):\n/tmp/ipykernel_36/4142875268.py:44: FutureWarning: `torch.cuda.amp.autocast(args...)` is deprecated. Please use `torch.amp.autocast('cuda', args...)` instead.\n  with autocast(enabled=use_amp):\n","output_type":"stream"},{"name":"stdout","text":"for epoch 1 train_acc: 0.7762, train_loss: 0.8246\nfor epoch 1 dev_acc: 0.9028, dev_loss: 0.5951\nfor epoch 2 train_acc: 0.8790, train_loss: 0.6241\nfor epoch 2 dev_acc: 0.9340, dev_loss: 0.5246\nfor epoch 3 train_acc: 0.9131, train_loss: 0.5639\nfor epoch 3 dev_acc: 0.9271, dev_loss: 0.5147\nfor epoch 4 train_acc: 0.9211, train_loss: 0.5395\nfor epoch 4 dev_acc: 0.9427, dev_loss: 0.4864\nfor epoch 5 train_acc: 0.9207, train_loss: 0.5274\nfor epoch 5 dev_acc: 0.9375, dev_loss: 0.4807\nfor epoch 6 train_acc: 0.9362, train_loss: 0.5143\nfor epoch 6 dev_acc: 0.9462, dev_loss: 0.4690\nfor epoch 7 train_acc: 0.9415, train_loss: 0.5005\nfor epoch 7 dev_acc: 0.9340, dev_loss: 0.4913\nfor epoch 8 train_acc: 0.9437, train_loss: 0.4883\nfor epoch 8 dev_acc: 0.9444, dev_loss: 0.4736\nfor epoch 9 train_acc: 0.9388, train_loss: 0.4966\nfor epoch 9 dev_acc: 0.9479, dev_loss: 0.4809\nfor epoch 10 train_acc: 0.9548, train_loss: 0.4646\nfor epoch 10 dev_acc: 0.9479, dev_loss: 0.4650\nfor epoch 11 train_acc: 0.9637, train_loss: 0.4587\nfor epoch 11 dev_acc: 0.9583, dev_loss: 0.4585\nfor epoch 12 train_acc: 0.9561, train_loss: 0.4687\nfor epoch 12 dev_acc: 0.9375, dev_loss: 0.4806\nfor epoch 13 train_acc: 0.9601, train_loss: 0.4542\nfor epoch 13 dev_acc: 0.9427, dev_loss: 0.5062\nfor epoch 14 train_acc: 0.9632, train_loss: 0.4532\nfor epoch 14 dev_acc: 0.9479, dev_loss: 0.4707\nfor epoch 15 train_acc: 0.9632, train_loss: 0.4525\nfor epoch 15 dev_acc: 0.9531, dev_loss: 0.4482\nfor epoch 16 train_acc: 0.9663, train_loss: 0.4464\nfor epoch 16 dev_acc: 0.9601, dev_loss: 0.4508\nfor epoch 17 train_acc: 0.9721, train_loss: 0.4396\nfor epoch 17 dev_acc: 0.9583, dev_loss: 0.4606\nfor epoch 18 train_acc: 0.9659, train_loss: 0.4394\nfor epoch 18 dev_acc: 0.9410, dev_loss: 0.4806\nfor epoch 19 train_acc: 0.9738, train_loss: 0.4310\nfor epoch 19 dev_acc: 0.9514, dev_loss: 0.4609\nfor epoch 20 train_acc: 0.9747, train_loss: 0.4271\nfor epoch 20 dev_acc: 0.9566, dev_loss: 0.4543\nfor epoch 21 train_acc: 0.9783, train_loss: 0.4238\nfor epoch 21 dev_acc: 0.9479, dev_loss: 0.4553\nfor epoch 22 train_acc: 0.9805, train_loss: 0.4201\nfor epoch 22 dev_acc: 0.9583, dev_loss: 0.4566\nfor epoch 23 train_acc: 0.9801, train_loss: 0.4171\nfor epoch 23 dev_acc: 0.9531, dev_loss: 0.4630\nfor epoch 24 train_acc: 0.9827, train_loss: 0.4151\nfor epoch 24 dev_acc: 0.9601, dev_loss: 0.4593\nfor epoch 25 train_acc: 0.9809, train_loss: 0.4151\nfor epoch 25 dev_acc: 0.9583, dev_loss: 0.4466\nfor epoch 26 train_acc: 0.9805, train_loss: 0.4093\nfor epoch 26 dev_acc: 0.9566, dev_loss: 0.4549\nfor epoch 27 train_acc: 0.9836, train_loss: 0.4110\nfor epoch 27 dev_acc: 0.9531, dev_loss: 0.4688\nfor epoch 28 train_acc: 0.9814, train_loss: 0.4073\nfor epoch 28 dev_acc: 0.9566, dev_loss: 0.4468\nfor epoch 29 train_acc: 0.9845, train_loss: 0.4086\nfor epoch 29 dev_acc: 0.9514, dev_loss: 0.4632\nfor epoch 30 train_acc: 0.9783, train_loss: 0.4150\nfor epoch 30 dev_acc: 0.9479, dev_loss: 0.4679\nfor epoch 31 train_acc: 0.9827, train_loss: 0.4068\nfor epoch 31 dev_acc: 0.9549, dev_loss: 0.4549\nfor epoch 32 train_acc: 0.9858, train_loss: 0.3990\nfor epoch 32 dev_acc: 0.9549, dev_loss: 0.4550\nfor epoch 33 train_acc: 0.9863, train_loss: 0.4023\nfor epoch 33 dev_acc: 0.9531, dev_loss: 0.4639\nfor epoch 34 train_acc: 0.9792, train_loss: 0.4047\nfor epoch 34 dev_acc: 0.9479, dev_loss: 0.4593\nfor epoch 35 train_acc: 0.9894, train_loss: 0.3989\nfor epoch 35 dev_acc: 0.9601, dev_loss: 0.4462\nfor epoch 36 train_acc: 0.9871, train_loss: 0.3968\nfor epoch 36 dev_acc: 0.9566, dev_loss: 0.4599\nfor epoch 37 train_acc: 0.9805, train_loss: 0.4022\nfor epoch 37 dev_acc: 0.9531, dev_loss: 0.4561\nfor epoch 38 train_acc: 0.9845, train_loss: 0.3970\nfor epoch 38 dev_acc: 0.9514, dev_loss: 0.4649\nfor epoch 39 train_acc: 0.9885, train_loss: 0.3924\nfor epoch 39 dev_acc: 0.9531, dev_loss: 0.4532\nfor epoch 40 train_acc: 0.9894, train_loss: 0.3881\nfor epoch 40 dev_acc: 0.9601, dev_loss: 0.4502\n","output_type":"stream"}],"execution_count":11},{"cell_type":"code","source":"class custom_dataset_test(Dataset):\n    def __init__(self, df, image_dir, transform = None):\n        self.df = df\n        self.transform = transform\n        self.image_dir = image_dir\n    def __len__(self):\n        return len(self.df)\n    def __getitem__(self,idx):\n        row = self.df.iloc[idx]\n        image_id = row[\"image_id\"]+\".jpg\"\n        img_path = os.path.join(self.image_dir, image_id)\n        img = Image.open(img_path).convert(\"RGB\")\n        if self.transform:\n            img = self.transform(img)\n        return img\ndecoder = {}\nimage_dir_test = \"/kaggle/input/binary-biplob-can-you-guess-the-chess-opening/chess_dataset/test_images\"\ntest_dataset = custom_dataset_test(test_df, image_dir_test, transform = test_transform)\ntest_loader = DataLoader(test_dataset, batch_size = 16, drop_last = False, shuffle = False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-30T16:14:45.549177Z","iopub.execute_input":"2025-07-30T16:14:45.54971Z","iopub.status.idle":"2025-07-30T16:14:45.555861Z","shell.execute_reply.started":"2025-07-30T16:14:45.549687Z","shell.execute_reply":"2025-07-30T16:14:45.554969Z"}},"outputs":[],"execution_count":12},{"cell_type":"code","source":"predictions = []\nmodel.eval()\nmodel.load_state_dict(best_model)\nwith torch.no_grad():\n    for x in test_loader:\n        x = x.to(device)\n        yHat = model(x)\n        labels = torch.argmax(yHat, dim=1)\n        predictions.extend(labels.cpu().numpy())\ndecoder = {v: k for k, v in encoder.items()}\noutput = {\"image_id\" :test_df[\"image_id\"],\n          \"eco_volume\" : [decoder.get(x,x) for x in predictions]\n         }\noutput = pd.DataFrame(output)\noutput.to_csv(\"/kaggle/working/output.csv\", index = False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-30T16:14:53.412849Z","iopub.execute_input":"2025-07-30T16:14:53.413326Z","iopub.status.idle":"2025-07-30T16:14:59.770706Z","shell.execute_reply.started":"2025-07-30T16:14:53.413302Z","shell.execute_reply":"2025-07-30T16:14:59.770155Z"}},"outputs":[],"execution_count":14},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}