{"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":"gpu","dataSources":[{"sourceId":127713,"databundleVersionId":15291341,"sourceType":"competition"}],"dockerImageVersionId":31236,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\n%matplotlib inline\n\nimport torch\nimport torch.nn as nn\nfrom torchvision.transforms import transforms\nfrom torch.utils.data import Dataset,DataLoader\n\nfrom PIL import Image\nimport os\n\nfrom sklearn.model_selection import StratifiedKFold\nfrom sklearn.metrics import *","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-01-12T17:38:43.637058Z","iopub.execute_input":"2026-01-12T17:38:43.637382Z","iopub.status.idle":"2026-01-12T17:38:47.375225Z","shell.execute_reply.started":"2026-01-12T17:38:43.637355Z","shell.execute_reply":"2026-01-12T17:38:47.374555Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class location:\n    train_csv = r'/kaggle/input/anime-gender-classification/dataset/train.csv'\n    val_csv = r'/kaggle/input/anime-gender-classification/dataset/val.csv'\n    test_csv = r'/kaggle/input/anime-gender-classification/dataset/test.csv'\n\n    train_img = r'/kaggle/input/anime-gender-classification/dataset/train_images'\n    val_img = r'/kaggle/input/anime-gender-classification/dataset/val_images'\n    test_img = r'/kaggle/input/anime-gender-classification/dataset/test_images'","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-12T17:38:47.376611Z","iopub.execute_input":"2026-01-12T17:38:47.377005Z","iopub.status.idle":"2026-01-12T17:38:47.381021Z","shell.execute_reply.started":"2026-01-12T17:38:47.376981Z","shell.execute_reply":"2026-01-12T17:38:47.380476Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_df = pd.read_csv(location.train_csv)\nval_df = pd.read_csv(location.val_csv)\ntest_df = pd.read_csv(location.test_csv)\n\ntrain_df['id'] = train_df['id'].apply(lambda x: os.path.join(location.train_img,x))\nval_df['id'] = val_df['id'].apply(lambda x: os.path.join(location.val_img,x))\ntest_df['id'] = test_df['id'].apply(lambda x: os.path.join(location.test_img,x))\n\nall_df = pd.concat([train_df,val_df],axis=0)\n\nall_df.shape","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-12T17:38:47.381746Z","iopub.execute_input":"2026-01-12T17:38:47.382029Z","iopub.status.idle":"2026-01-12T17:38:47.446191Z","shell.execute_reply.started":"2026-01-12T17:38:47.381999Z","shell.execute_reply":"2026-01-12T17:38:47.445470Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"fig,ax = plt.subplots(1,2)\nsample = all_df.groupby('label').sample(1)\n\nfor idx,(_,row) in enumerate(sample.iterrows()):\n    img_path = row['id']\n    label = row['label']\n\n    img = Image.open(img_path).convert(\"RGB\")\n\n    ax[idx].imshow(img)\n    ax[idx].axis('off')\n    ax[idx].set_title(f'{label}')\n\nplt.tight_layout()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-12T17:38:47.447665Z","iopub.execute_input":"2026-01-12T17:38:47.447896Z","iopub.status.idle":"2026-01-12T17:38:47.693264Z","shell.execute_reply.started":"2026-01-12T17:38:47.447876Z","shell.execute_reply":"2026-01-12T17:38:47.692487Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class AnimeCharacters(Dataset):\n    def __init__(self,df,tfms=None,test=None):\n        self.df = df\n        self.tfms = tfms\n        self.test = test\n    def __len__(self):\n        return len(self.df)\n    def __getitem__(self,idx):\n        row = self.df.iloc[idx]\n\n        img_path = row['id']\n        \n        if self.test is None:\n            label = row['label']\n\n\n        img = Image.open(img_path).convert(\"RGB\")\n        \n        if self.tfms:\n            img = self.tfms(img)\n\n\n        if self.test:\n            return {\n                'feature': img,\n                # 'label':torch.tensor(label,dtype=torch.long),\n                'metadata': img_path.split('/')[-1]\n            }\n        return {\n                'feature': img,\n                'label':torch.tensor(label,dtype=torch.long),\n                # 'metadata': img_path.strip('/')[-1]\n        }\n\n\ndef get_loaders(df,splitter,transforms:list,batches:list):\n\n    loaders = []\n\n    for tr_idx,val_idx in splitter.split(np.zeros(len(df)),df['label']):\n        train = df.iloc[tr_idx]\n        val = df.iloc[val_idx]\n\n        train_loader = DataLoader(\n            AnimeCharacters(train,transforms[0]),\n            shuffle=True,\n            batch_size=batches[0],\n            pin_memory=True,\n            num_workers=0\n        )\n        val_loader = DataLoader(\n            AnimeCharacters(val,transforms[1]),\n            shuffle=False,\n            batch_size=batches[1]\n        )\n\n        loaders.append((train_loader,val_loader))\n\n    return loaders","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-12T19:09:01.349029Z","iopub.execute_input":"2026-01-12T19:09:01.349347Z","iopub.status.idle":"2026-01-12T19:09:01.356788Z","shell.execute_reply.started":"2026-01-12T19:09:01.349322Z","shell.execute_reply":"2026-01-12T19:09:01.356119Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_tfms = transforms.Compose([\n    transforms.RandomResizedCrop(\n        224, scale=(0.85, 1.0), ratio=(0.9, 1.1)\n    ),\n    transforms.RandomHorizontalFlip(p=0.5),\n    transforms.RandomRotation(12),\n\n    transforms.ColorJitter(\n        brightness=0.15,\n        contrast=0.15,\n        saturation=0.08,\n        hue=0.02\n    ),\n\n    transforms.ToTensor(),\n\n    transforms.RandomErasing(\n        p=0.25,\n        scale=(0.02, 0.12),\n        ratio=(0.3, 3.3),\n        value=\"random\"\n    ),\n\n    transforms.Normalize(\n        mean=[0.485, 0.456, 0.406],\n        std =[0.229, 0.224, 0.225]\n    ),\n])\n\nval_tfms = transforms.Compose([\n    transforms.Resize(256),\n    transforms.CenterCrop(224),\n    transforms.ToTensor(),\n    transforms.Normalize(\n        mean=[0.485, 0.456, 0.406],\n        std =[0.229, 0.224, 0.225]\n    ),\n])\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-12T17:38:48.192098Z","iopub.execute_input":"2026-01-12T17:38:48.192623Z","iopub.status.idle":"2026-01-12T17:38:48.199088Z","shell.execute_reply.started":"2026-01-12T17:38:48.192564Z","shell.execute_reply":"2026-01-12T17:38:48.198391Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"splitter = StratifiedKFold(n_splits=15,shuffle=True,random_state=42)\nall_loaders = get_loaders(all_df,splitter,[train_tfms,val_tfms],[32,64])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-12T19:17:26.776718Z","iopub.execute_input":"2026-01-12T19:17:26.777382Z","iopub.status.idle":"2026-01-12T19:17:26.789270Z","shell.execute_reply.started":"2026-01-12T19:17:26.777355Z","shell.execute_reply":"2026-01-12T19:17:26.788656Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from torchvision.models import resnet50, ResNet50_Weights\n\ndef get_model():\n    model = resnet50(weights=ResNet50_Weights.DEFAULT)\n    input_feats = model.fc.in_features\n\n    model.fc= nn.Sequential(\n        nn.Linear(input_feats,512),\n        nn.GELU(),\n        nn.Dropout(0.2),\n        nn.Linear(512,2)\n    )\n\n    return model\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-12T19:17:33.993834Z","iopub.execute_input":"2026-01-12T19:17:33.994152Z","iopub.status.idle":"2026-01-12T19:17:33.999123Z","shell.execute_reply.started":"2026-01-12T19:17:33.994126Z","shell.execute_reply":"2026-01-12T19:17:33.998367Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from transformers import get_cosine_schedule_with_warmup\nfrom tqdm.notebook import tqdm,trange\n\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n\nEPOCHS = 18\nPATIENCE = 3\nLR = 1e-4\nWARMUP_RATIO = 0.04\n\nfor fold_idx, loaders in (enumerate(tqdm(all_loaders))):\n    print(f\"\\n🚀 Training Fold {fold_idx+1}\")\n\n    train_loader, val_loader = loaders\n    \n    model = get_model().to(device)\n\n    optimizer = torch.optim.Adam(model.parameters(), lr=LR)\n    criterion = nn.CrossEntropyLoss(label_smoothing=0.01)\n\n    total_steps = EPOCHS * len(train_loader)\n    warmup_steps = int(WARMUP_RATIO * total_steps)\n\n    scheduler = get_cosine_schedule_with_warmup(\n        optimizer,\n        num_warmup_steps=warmup_steps,\n        num_training_steps=total_steps\n    )\n\n    best_val_loss = float(\"inf\")\n    patience_counter = 0\n\n    history = {\n        \"train_loss\": [],\n        \"val_loss\": [],\n        \"val_acc\": []\n    }\n\n    for epoch in range(EPOCHS):\n        model.train()\n        train_losses = []\n\n        pbar = tqdm(train_loader, desc=f\"Epoch {epoch+1}\", leave=False)\n\n        for batch in pbar:\n            images = batch[\"feature\"].to(device)\n            labels = batch[\"label\"].to(device)\n\n            optimizer.zero_grad()\n\n            logits = model(images)\n            loss = criterion(logits, labels)\n\n            loss.backward()\n            optimizer.step()\n            scheduler.step()\n\n            train_losses.append(loss.item())\n            pbar.set_postfix(train_loss=np.mean(train_losses))\n\n        avg_train_loss = np.mean(train_losses)\n\n        model.eval()\n        val_losses = []\n        correct = 0\n        total = 0\n\n        with torch.no_grad():\n            for batch in val_loader:\n                images = batch[\"feature\"].to(device)\n                labels = batch[\"label\"].to(device)\n\n                logits = model(images)\n                loss = criterion(logits, labels)\n\n                val_losses.append(loss.item())\n\n                preds = logits.argmax(dim=1)\n                correct += (preds == labels).sum().item()\n                total += labels.size(0)\n\n        avg_val_loss = np.mean(val_losses)\n        val_acc = correct / total\n\n        history[\"train_loss\"].append(avg_train_loss)\n        history[\"val_loss\"].append(avg_val_loss)\n        history[\"val_acc\"].append(val_acc)\n\n        print(\n            f\"Epoch [{epoch+1}/{EPOCHS}] | \"\n            f\"Train Loss: {avg_train_loss:.4f} | \"\n            f\"Val Loss: {avg_val_loss:.4f} | \"\n            f\"Val Acc: {val_acc:.4f}\"\n        )\n\n        if avg_val_loss < best_val_loss:\n            best_val_loss = avg_val_loss\n            patience_counter = 0\n\n            torch.save(\n                model.state_dict(),\n                f\"best_model_fold_{fold_idx}.pt\"\n            )\n        else:\n            patience_counter += 1\n            if patience_counter >= PATIENCE:\n                print(\"🛑 Early stopping triggered.\")\n                del model, optimizer\n                torch.cuda.empty_cache()\n                break\n\n    print(f\"✅ Fold {fold_idx+1} finished.\\n\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-12T19:18:12.684274Z","iopub.execute_input":"2026-01-12T19:18:12.684608Z"},"scrolled":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import glob\nimport torch.nn.functional as F\n\ntest_loader = DataLoader(AnimeCharacters(test_df,val_tfms,test=True),batch_size=32,shuffle=False)\n\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n\nmodel_paths = sorted(glob.glob(\"best_model_fold_*.pt\"))\nprint(\"Loaded models:\", model_paths)\n\nmodels = []\n\nfor path in model_paths:\n    model = get_model().to(device)\n    model.load_state_dict(torch.load(path, map_location=device))\n    torch.cuda.empty_cache()\n    model.eval()\n    models.append(model)\n\nnum_models = len(models)\nassert num_models > 0, \"No models found for ensembling.\"\n\n\nall_ids = []\nall_preds = []\nall_probs = []\n\n@torch.no_grad()\ndef ensemble_predict(test_loader, models):\n    for batch in tqdm(test_loader):\n        images = batch[\"feature\"].to(device)\n        ids = batch[\"metadata\"]\n\n        # Sum probabilities from each model\n        prob_sum = None\n\n        for model in models:\n            logits = model(images)\n            probs = F.softmax(logits, dim=1)\n\n            if prob_sum is None:\n                prob_sum = probs\n            else:\n                prob_sum += probs\n\n        avg_probs = prob_sum / len(models)\n        preds = avg_probs.argmax(dim=1)\n\n        all_ids.extend(ids)\n        all_preds.extend(preds.cpu())\n        all_probs.extend(avg_probs.cpu().numpy())\n\nensemble_predict(test_loader, models)\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"preds = [preds.item() for preds in all_preds]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-12T19:10:22.059644Z","iopub.execute_input":"2026-01-12T19:10:22.059933Z","iopub.status.idle":"2026-01-12T19:10:22.064528Z","shell.execute_reply.started":"2026-01-12T19:10:22.059908Z","shell.execute_reply":"2026-01-12T19:10:22.064009Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"data = {'id':all_ids,'label':preds}\ndf = pd.DataFrame(data)\ndf.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-12T19:10:57.076955Z","iopub.execute_input":"2026-01-12T19:10:57.077566Z","iopub.status.idle":"2026-01-12T19:10:57.096115Z","shell.execute_reply.started":"2026-01-12T19:10:57.077538Z","shell.execute_reply":"2026-01-12T19:10:57.095575Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df.to_csv('submission.csv',index=False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-12T19:11:14.795409Z","iopub.execute_input":"2026-01-12T19:11:14.795729Z","iopub.status.idle":"2026-01-12T19:11:14.804991Z","shell.execute_reply.started":"2026-01-12T19:11:14.795703Z","shell.execute_reply":"2026-01-12T19:11:14.804425Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}