{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.12.13","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":"import pandas as pd\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":"2026-06-19T12:44:09.009030Z","iopub.execute_input":"2026-06-19T12:44:09.009373Z","iopub.status.idle":"2026-06-19T12:44:09.013871Z","shell.execute_reply.started":"2026-06-19T12:44:09.009338Z","shell.execute_reply":"2026-06-19T12:44:09.012964Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train = pd.read_csv(\"/kaggle/input/competitions/facial-emotion-recognition22/data/train.csv\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-19T12:44:09.023473Z","iopub.execute_input":"2026-06-19T12:44:09.023679Z","iopub.status.idle":"2026-06-19T12:44:11.579877Z","shell.execute_reply.started":"2026-06-19T12:44:09.023661Z","shell.execute_reply":"2026-06-19T12:44:11.578949Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def display_images(dataframe, num_images=5):\n    pixel_col = [col for col in dataframe.columns if col.lower() == 'pixels'][0]\n    label_col = [col for col in dataframe.columns if col.lower() in ['emotion', 'category', 'label']]\n    \n    plt.figure(figsize=(15, 3))\n    \n    for i in range(num_images):\n        pixel_string = dataframe[pixel_col].iloc[i]\n        image = np.fromstring(pixel_string, sep=' ', dtype=np.uint8).reshape(48, 48)\n        \n        title = f\"Class: {dataframe[label_col[0]].iloc[i]}\" if label_col else f\"Idx: {i}\"\n        \n        plt.subplot(1, num_images, i + 1)\n        plt.imshow(image, cmap='gray')\n        plt.title(title)\n        plt.axis('off')\n        \n    plt.tight_layout()\n    plt.show()\n\ndisplay_images(train, num_images=5)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-19T12:44:11.581305Z","iopub.execute_input":"2026-06-19T12:44:11.581639Z","iopub.status.idle":"2026-06-19T12:44:11.800454Z","shell.execute_reply.started":"2026-06-19T12:44:11.581616Z","shell.execute_reply":"2026-06-19T12:44:11.799798Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train[\"emotion\"].value_counts()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-19T12:44:11.801206Z","iopub.execute_input":"2026-06-19T12:44:11.801548Z","iopub.status.idle":"2026-06-19T12:44:11.808075Z","shell.execute_reply.started":"2026-06-19T12:44:11.801525Z","shell.execute_reply":"2026-06-19T12:44:11.807346Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-19T12:44:11.808958Z","iopub.execute_input":"2026-06-19T12:44:11.809164Z","iopub.status.idle":"2026-06-19T12:44:11.820098Z","shell.execute_reply.started":"2026-06-19T12:44:11.809143Z","shell.execute_reply":"2026-06-19T12:44:11.819352Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class_counts = torch.tensor([3995, 436, 4097, 7215, 4830, 3171, 4965], dtype=torch.float32)\n\ntotal_samples = class_counts.sum()\nnum_classes = len(class_counts)\nclass_weights = total_samples / (num_classes * class_counts)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-19T12:44:11.822225Z","iopub.execute_input":"2026-06-19T12:44:11.822571Z","iopub.status.idle":"2026-06-19T12:44:11.833055Z","shell.execute_reply.started":"2026-06-19T12:44:11.822550Z","shell.execute_reply":"2026-06-19T12:44:11.832454Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from torch import nn\nimport torch.nn.functional as F","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-19T12:44:11.833979Z","iopub.execute_input":"2026-06-19T12:44:11.834235Z","iopub.status.idle":"2026-06-19T12:44:11.846867Z","shell.execute_reply.started":"2026-06-19T12:44:11.834206Z","shell.execute_reply":"2026-06-19T12:44:11.846091Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class SpatialAttention(nn.Module):\n    def __init__(self, kernel_size=7):\n        super().__init__()\n        self.conv = nn.Conv2d(2, 1, kernel_size=kernel_size, padding=kernel_size//2, bias=False)\n        self.sigmoid = nn.Sigmoid()\n\n    def forward(self, x):\n        avg_out = torch.mean(x, dim=1, keepdim=True)\n        max_out, _ = torch.max(x, dim=1, keepdim=True)\n        spatial_features = torch.cat([avg_out, max_out], dim=1)\n        attention_map = self.sigmoid(self.conv(spatial_features))\n\n        return x * attention_map","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-19T12:44:11.847744Z","iopub.execute_input":"2026-06-19T12:44:11.847999Z","iopub.status.idle":"2026-06-19T12:44:11.858809Z","shell.execute_reply.started":"2026-06-19T12:44:11.847971Z","shell.execute_reply":"2026-06-19T12:44:11.858154Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class ResidualBlock(nn.Module):\n    def __init__(self, in_channels, out_channels, stride=1):\n        super().__init__()\n        self.conv1 = nn.Conv2d(in_channels, out_channels, kernel_size=3, stride=stride, padding=1, bias=False)\n        self.bn1 = nn.BatchNorm2d(out_channels)\n        self.conv2 = nn.Conv2d(out_channels, out_channels, kernel_size=3, stride=1, padding=1, bias=False)\n        self.bn2 = nn.BatchNorm2d(out_channels)\n\n        self.shortcut = nn.Sequential()\n        if stride != 1 or in_channels != out_channels:\n            self.shortcut = nn.Sequential(\n                nn.Conv2d(in_channels, out_channels, kernel_size=1, stride=stride, bias=False),\n                nn.BatchNorm2d(out_channels)\n            )\n\n    def forward(self, x):\n        out = F.relu(self.bn1(self.conv1(x)))\n        out = self.bn2(self.conv2(out))\n        out += self.shortcut(x)\n        out = F.relu(out)\n        return out","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-19T12:44:11.859853Z","iopub.execute_input":"2026-06-19T12:44:11.860242Z","iopub.status.idle":"2026-06-19T12:44:11.871538Z","shell.execute_reply.started":"2026-06-19T12:44:11.860221Z","shell.execute_reply":"2026-06-19T12:44:11.870826Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class FERAttentionNet(nn.Module):\n    def __init__(self, num_classes=7):\n        super().__init__()\n        self.stem = nn.Sequential(\n            nn.Conv2d(1, 64, kernel_size=3, stride=1, padding=1, bias=False),\n            nn.BatchNorm2d(64),\n            nn.ReLU()\n        )\n        self.layer1 = ResidualBlock(64, 124, stride=2) \n        self.layer2 = ResidualBlock(124, 256, stride=2)\n        self.layer3 = ResidualBlock(256, 512, stride=2)\n        self.spatial_attention = SpatialAttention(kernel_size=3)\n        self.pool = nn.AdaptiveAvgPool2d((1, 1))\n        self.fc = nn.Sequential(\n            nn.Dropout(0.4),\n            nn.Linear(512, num_classes)\n        )\n\n    def forward(self, x):\n        x = self.stem(x)\n        x = self.layer1(x)\n        x = self.layer2(x)\n        x = self.layer3(x)\n        \n        x = self.spatial_attention(x)\n        \n        x = self.pool(x)\n        x = torch.flatten(x, 1)\n        x = self.fc(x)\n        return x","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-19T12:44:11.872392Z","iopub.execute_input":"2026-06-19T12:44:11.872662Z","iopub.status.idle":"2026-06-19T12:44:11.883870Z","shell.execute_reply.started":"2026-06-19T12:44:11.872618Z","shell.execute_reply":"2026-06-19T12:44:11.883259Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from torch.utils.data import Dataset, DataLoader\nimport torchvision.transforms as transforms\nfrom tqdm import tqdm","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-19T12:44:11.884694Z","iopub.execute_input":"2026-06-19T12:44:11.885027Z","iopub.status.idle":"2026-06-19T12:44:11.897868Z","shell.execute_reply.started":"2026-06-19T12:44:11.884997Z","shell.execute_reply":"2026-06-19T12:44:11.897173Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_transform = transforms.Compose([\n    transforms.ToPILImage(),\n    transforms.RandomHorizontalFlip(p=0.5),\n    transforms.RandomRotation(15),\n    transforms.ToTensor(),\n    transforms.Normalize(mean=[0.5], std=[0.5])\n])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-19T12:44:11.898733Z","iopub.execute_input":"2026-06-19T12:44:11.899012Z","iopub.status.idle":"2026-06-19T12:44:11.909614Z","shell.execute_reply.started":"2026-06-19T12:44:11.898981Z","shell.execute_reply":"2026-06-19T12:44:11.909047Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test_transform = transforms.Compose([\n    transforms.ToPILImage(),\n    transforms.ToTensor(),\n    transforms.Normalize(mean=[0.5], std=[0.5])\n])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-19T12:44:11.910643Z","iopub.execute_input":"2026-06-19T12:44:11.910902Z","iopub.status.idle":"2026-06-19T12:44:11.921958Z","shell.execute_reply.started":"2026-06-19T12:44:11.910869Z","shell.execute_reply":"2026-06-19T12:44:11.921339Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class FERDataset(Dataset):\n    def __init__(self, dataframe, transform=None, is_test=False):\n        self.df = dataframe.reset_index(drop=True)\n        self.transform = transform\n        self.is_test = is_test\n        \n        self.pixel_col = [col for col in self.df.columns if col.lower() == 'pixels'][0]\n        if not self.is_test:\n            self.label_col = [col for col in self.df.columns if col.lower() in ['emotion', 'category', 'label']][0]\n\n    def __len__(self):\n        return len(self.df)\n\n    def __getitem__(self, idx):\n        pixel_string = self.df.iloc[idx][self.pixel_col]\n        image = np.fromstring(pixel_string, sep=' ', dtype=np.uint8).reshape(48, 48)\n        \n        if self.transform:\n            image = self.transform(image)\n        else:\n            image = torch.tensor(image, dtype=torch.float32).unsqueeze(0) / 255.0\n            \n        if self.is_test:\n            id_col = 'Id' if 'Id' in self.df.columns else 'id'\n            img_id = self.df.iloc[idx][id_col] if id_col in self.df.columns else idx\n            return image, img_id\n            \n        label = int(self.df.iloc[idx][self.label_col])\n        return image, label","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-19T12:44:11.922927Z","iopub.execute_input":"2026-06-19T12:44:11.923189Z","iopub.status.idle":"2026-06-19T12:44:11.934821Z","shell.execute_reply.started":"2026-06-19T12:44:11.923170Z","shell.execute_reply":"2026-06-19T12:44:11.934066Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.metrics import f1_score","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-19T12:45:14.714227Z","iopub.execute_input":"2026-06-19T12:45:14.714552Z","iopub.status.idle":"2026-06-19T12:45:15.683921Z","shell.execute_reply.started":"2026-06-19T12:45:14.714522Z","shell.execute_reply":"2026-06-19T12:45:15.683308Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def train_epoch(model, dataloader, criterion, optimizer, device):\n    model.train()\n    running_loss = 0.0\n    all_preds = []\n    all_labels = []\n    \n    for images, labels in dataloader:\n        images, labels = images.to(device), labels.to(device)\n        \n        optimizer.zero_grad()\n        outputs = model(images)\n        loss = criterion(outputs, labels)\n        loss.backward()\n        optimizer.step()\n        \n        running_loss += loss.item() * images.size(0)\n        _, preds = torch.max(outputs, 1)\n        \n        all_preds.extend(preds.cpu().numpy())\n        all_labels.extend(labels.cpu().numpy())\n        \n    epoch_loss = running_loss / len(dataloader.dataset)\n    epoch_f1 = f1_score(all_labels, all_preds, average='macro')\n    return epoch_loss, epoch_f1","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-19T12:45:17.039063Z","iopub.execute_input":"2026-06-19T12:45:17.039783Z","iopub.status.idle":"2026-06-19T12:45:17.045613Z","shell.execute_reply.started":"2026-06-19T12:45:17.039752Z","shell.execute_reply":"2026-06-19T12:45:17.044827Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_dataset = FERDataset(train, transform=train_transform)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-19T12:44:11.951134Z","iopub.execute_input":"2026-06-19T12:44:11.951433Z","iopub.status.idle":"2026-06-19T12:44:11.963711Z","shell.execute_reply.started":"2026-06-19T12:44:11.951398Z","shell.execute_reply":"2026-06-19T12:44:11.963045Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True, num_workers=2, pin_memory=True)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-19T12:44:11.964492Z","iopub.execute_input":"2026-06-19T12:44:11.964830Z","iopub.status.idle":"2026-06-19T12:44:11.975024Z","shell.execute_reply.started":"2026-06-19T12:44:11.964794Z","shell.execute_reply":"2026-06-19T12:44:11.974205Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\nmodel = FERAttentionNet(num_classes=7).to(device)\n\ncriterion = nn.CrossEntropyLoss(weight=class_weights.to(device))\noptimizer = torch.optim.AdamW(model.parameters(), lr=1e-3, weight_decay=1e-4)\nscheduler = torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=20)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-19T12:44:11.976007Z","iopub.execute_input":"2026-06-19T12:44:11.976349Z","iopub.status.idle":"2026-06-19T12:44:12.033351Z","shell.execute_reply.started":"2026-06-19T12:44:11.976320Z","shell.execute_reply":"2026-06-19T12:44:12.032797Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"epochs = 20\n\nfor epoch in range(epochs):\n    train_loss, train_f1 = train_epoch(model, train_loader, criterion, optimizer, device)\n    scheduler.step()\n    \n    print(f\"Epoch [{epoch+1}/{epochs}] | \"\n          f\"Train Loss: {train_loss:.4f} - Train MeanF1: {train_f1:.4f} | \")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-19T12:45:21.073765Z","iopub.execute_input":"2026-06-19T12:45:21.074522Z","iopub.status.idle":"2026-06-19T12:53:17.741999Z","shell.execute_reply.started":"2026-06-19T12:45:21.074489Z","shell.execute_reply":"2026-06-19T12:53:17.741170Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def generate_submission(model, test_dataframe, output_filename=\"submission.csv\"):\n    checkpoint_model = model\n    checkpoint_model.eval()\n    \n    test_dataset = FERDataset(test_dataframe, transform=test_transform, is_test=True)\n    test_loader = DataLoader(test_dataset, batch_size=64, shuffle=False, num_workers=2)\n    \n    submission_ids = []\n    submission_preds = []\n    \n    with torch.no_grad():\n        for images, img_ids in tqdm(test_loader, desc=\"Generating Predictions\"):\n            images = images.to(device)\n            outputs = checkpoint_model(images)\n            _, preds = torch.max(outputs, 1)\n            \n            submission_ids.extend(img_ids.numpy() if isinstance(img_ids, torch.Tensor) else img_ids)\n            submission_preds.extend(preds.cpu().numpy())\n            \n    submission_df = pd.DataFrame({\n        \"Id\": submission_ids,\n        \"Category\": submission_preds\n    })\n    \n    submission_df.to_csv(output_filename, index=False)\n    print(f\"\\nFile exported successfully to {output_filename}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-19T12:54:47.207384Z","iopub.execute_input":"2026-06-19T12:54:47.208152Z","iopub.status.idle":"2026-06-19T12:54:47.214251Z","shell.execute_reply.started":"2026-06-19T12:54:47.208120Z","shell.execute_reply":"2026-06-19T12:54:47.213527Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test = pd.read_csv(\"/kaggle/input/competitions/facial-emotion-recognition22/data/test.csv\")\ngenerate_submission(model, test)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-19T12:54:48.848868Z","iopub.execute_input":"2026-06-19T12:54:48.849445Z","iopub.status.idle":"2026-06-19T12:54:51.585778Z","shell.execute_reply.started":"2026-06-19T12:54:48.849411Z","shell.execute_reply":"2026-06-19T12:54:51.584825Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}