{"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","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":"nvidiaTeslaT4","dataSources":[{"sourceType":"competition","sourceId":126777,"databundleVersionId":15314950}],"dockerImageVersionId":31287,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# 🐆 Jaguar Re‑Identification Challenge\n## DINOv2‑Base + ArcFace ","metadata":{}},{"cell_type":"markdown","source":"## 1️⃣ Install Dependencies","metadata":{}},{"cell_type":"code","source":"!pip install -q timm albumentations pytorch-metric-learning","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-04T06:20:14.971819Z","iopub.execute_input":"2026-03-04T06:20:14.972107Z","iopub.status.idle":"2026-03-04T06:20:20.208068Z","shell.execute_reply.started":"2026-03-04T06:20:14.972076Z","shell.execute_reply":"2026-03-04T06:20:20.207202Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 2️⃣ Memory Optimization & Imports","metadata":{}},{"cell_type":"code","source":"import os\nos.environ['PYTORCH_CUDA_ALLOC_CONF'] = 'expandable_segments:True'\n\nimport numpy as np\nimport pandas as pd\nfrom pathlib import Path\nfrom PIL import Image\n\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom torch.utils.data import Dataset, DataLoader\n\nimport timm\nimport albumentations as A\nfrom albumentations.pytorch import ToTensorV2\n\nfrom pytorch_metric_learning.losses import ArcFaceLoss\n\ndevice = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\nprint('Device:', device)\nif torch.cuda.is_available():\n    print('GPU:', torch.cuda.get_device_name(0))\n    torch.cuda.empty_cache()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-04T06:20:24.185893Z","iopub.execute_input":"2026-03-04T06:20:24.186686Z","iopub.status.idle":"2026-03-04T06:20:37.516221Z","shell.execute_reply.started":"2026-03-04T06:20:24.186654Z","shell.execute_reply":"2026-03-04T06:20:37.515429Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 3️⃣ Load Dataset & Fix Nested Paths","metadata":{}},{"cell_type":"code","source":"DATA_DIR = Path('/kaggle/input/competitions/jaguar-re-id')\n\nTRAIN_DIR = DATA_DIR/'train'/'train'\nTEST_DIR  = DATA_DIR/'test'/'test'\n\ntrain_df = pd.read_csv(DATA_DIR/'train.csv')\ntest_df  = pd.read_csv(DATA_DIR/'test.csv')\n\ntrain_df.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-04T06:20:45.725330Z","iopub.execute_input":"2026-03-04T06:20:45.726190Z","iopub.status.idle":"2026-03-04T06:20:45.867697Z","shell.execute_reply.started":"2026-03-04T06:20:45.726157Z","shell.execute_reply":"2026-03-04T06:20:45.866980Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 4️⃣ Encode Jaguar Labels","metadata":{}},{"cell_type":"code","source":"label_to_idx = {l:i for i,l in enumerate(train_df.ground_truth.unique())}\ntrain_df['label'] = train_df.ground_truth.map(label_to_idx)\nNUM_CLASSES = len(label_to_idx)\nprint('Total Jaguars:', NUM_CLASSES)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-04T06:20:50.301418Z","iopub.execute_input":"2026-03-04T06:20:50.302001Z","iopub.status.idle":"2026-03-04T06:20:50.312193Z","shell.execute_reply.started":"2026-03-04T06:20:50.301972Z","shell.execute_reply":"2026-03-04T06:20:50.311486Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 5️⃣ Image Transformations","metadata":{}},{"cell_type":"code","source":"IMAGE_SIZE = 448\n\ntrain_tfms = A.Compose([\n    A.Resize(IMAGE_SIZE, IMAGE_SIZE),\n    A.HorizontalFlip(p=0.5),\n    A.RandomBrightnessContrast(p=0.5),\n    A.Normalize(),\n    ToTensorV2()\n])\n\nvalid_tfms = A.Compose([\n    A.Resize(IMAGE_SIZE, IMAGE_SIZE),\n    A.Normalize(),\n    ToTensorV2()\n])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-04T06:20:58.053116Z","iopub.execute_input":"2026-03-04T06:20:58.053403Z","iopub.status.idle":"2026-03-04T06:20:58.061825Z","shell.execute_reply.started":"2026-03-04T06:20:58.053379Z","shell.execute_reply":"2026-03-04T06:20:58.061117Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 6️⃣ Dataset Class","metadata":{}},{"cell_type":"code","source":"class JaguarDataset(Dataset):\n    def __init__(self, df, folder, tfms):\n        self.df = df.reset_index(drop=True)\n        self.folder = folder\n        self.tfms = tfms\n\n    def __len__(self):\n        return len(self.df)\n\n    def __getitem__(self, idx):\n        row = self.df.iloc[idx]\n        img = np.array(Image.open(self.folder/row.filename).convert('RGB'))\n        img = self.tfms(image=img)['image']\n        return img, row.label","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-04T06:21:03.309478Z","iopub.execute_input":"2026-03-04T06:21:03.309783Z","iopub.status.idle":"2026-03-04T06:21:03.315119Z","shell.execute_reply.started":"2026-03-04T06:21:03.309758Z","shell.execute_reply":"2026-03-04T06:21:03.314337Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 7️⃣ DataLoader","metadata":{}},{"cell_type":"code","source":"train_loader = DataLoader(\n    JaguarDataset(train_df, TRAIN_DIR, train_tfms),\n    batch_size=4,\n    shuffle=True,\n    num_workers=2,\n    pin_memory=True\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-04T06:21:10.278382Z","iopub.execute_input":"2026-03-04T06:21:10.278676Z","iopub.status.idle":"2026-03-04T06:21:10.283212Z","shell.execute_reply.started":"2026-03-04T06:21:10.278650Z","shell.execute_reply":"2026-03-04T06:21:10.282505Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 8️⃣ Model: DINOv2‑Base","metadata":{}},{"cell_type":"code","source":"class JaguarModel(nn.Module):\n\n    def __init__(self):\n\n        super().__init__()\n\n        self.backbone = timm.create_model(\n            \"vit_base_patch14_dinov2\",\n            pretrained=True,\n            num_classes=0,\n            img_size=448,            # ← IMPORTANT\n            dynamic_img_size=True    # ← IMPORTANT\n        )\n\n        self.embedding = nn.Linear(768, 512)\n\n    def forward(self, x):\n\n        x = self.backbone(x)\n\n        x = self.embedding(x)\n\n        return F.normalize(x)\n\nmodel = JaguarModel().to(device)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-04T06:21:14.215740Z","iopub.execute_input":"2026-03-04T06:21:14.216118Z","iopub.status.idle":"2026-03-04T06:21:17.750606Z","shell.execute_reply.started":"2026-03-04T06:21:14.216093Z","shell.execute_reply":"2026-03-04T06:21:17.749991Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 9️⃣ ArcFace Loss + Optimizer","metadata":{}},{"cell_type":"code","source":"arcface = ArcFaceLoss(NUM_CLASSES, 512).to(device)\noptimizer = torch.optim.AdamW(model.parameters(), lr=1e-4)\nscaler = torch.cuda.amp.GradScaler()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-04T06:21:24.369762Z","iopub.execute_input":"2026-03-04T06:21:24.370399Z","iopub.status.idle":"2026-03-04T06:21:24.376606Z","shell.execute_reply.started":"2026-03-04T06:21:24.370370Z","shell.execute_reply":"2026-03-04T06:21:24.375984Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 🔁 Resume Training if Checkpoint Exists","metadata":{}},{"cell_type":"code","source":"checkpoint_path = '/kaggle/working/checkpoint.pth'\nstart_epoch = 0\n\nif os.path.exists(checkpoint_path):\n    print('Resuming training...')\n    checkpoint = torch.load(checkpoint_path)\n    model.load_state_dict(checkpoint['model'])\n    optimizer.load_state_dict(checkpoint['optimizer'])\n    start_epoch = checkpoint['epoch'] + 1","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-04T06:21:45.585422Z","iopub.execute_input":"2026-03-04T06:21:45.586149Z","iopub.status.idle":"2026-03-04T06:21:45.590025Z","shell.execute_reply.started":"2026-03-04T06:21:45.586120Z","shell.execute_reply":"2026-03-04T06:21:45.589447Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 🔟 Training Loop with Auto‑Save","metadata":{}},{"cell_type":"code","source":"EPOCHS = 20\n\nfor epoch in range(start_epoch, EPOCHS):\n\n    model.train()\n    total_loss = 0\n\n    for imgs, labels in train_loader:\n        imgs = imgs.to(device)\n        labels = labels.to(device)\n\n        optimizer.zero_grad()\n\n        with torch.cuda.amp.autocast():\n            emb = model(imgs)\n            loss = arcface(emb, labels)\n\n        scaler.scale(loss).backward()\n        scaler.step(optimizer)\n        scaler.update()\n\n        total_loss += loss.item()\n\n    print(f'Epoch {epoch}: {total_loss/len(train_loader):.4f}')\n\n    torch.save({\n        'epoch': epoch,\n        'model': model.state_dict(),\n        'optimizer': optimizer.state_dict()\n    }, checkpoint_path)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-04T06:21:49.780366Z","iopub.execute_input":"2026-03-04T06:21:49.780684Z","iopub.status.idle":"2026-03-04T08:17:42.907749Z","shell.execute_reply.started":"2026-03-04T06:21:49.780658Z","shell.execute_reply":"2026-03-04T08:17:42.906594Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 1️⃣1️⃣ Extract Test Embeddings (TTA)","metadata":{}},{"cell_type":"code","source":"model.eval()\ntest_images = sorted(set(test_df.query_image) | set(test_df.gallery_image))\nembeddings = {}\n\ndef extract(path):\n    img = np.array(Image.open(path).convert('RGB'))\n    img1 = valid_tfms(image=img)['image']\n    img2 = valid_tfms(image=np.fliplr(img))['image']\n    img1 = img1.unsqueeze(0).to(device)\n    img2 = img2.unsqueeze(0).to(device)\n    with torch.no_grad():\n        e = (model(img1) + model(img2)) / 2\n    return e.cpu().numpy()\n\nfor img in test_images:\n    embeddings[img] = extract(TEST_DIR/img)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-04T08:21:40.964128Z","iopub.execute_input":"2026-03-04T08:21:40.965020Z","iopub.status.idle":"2026-03-04T08:24:55.055421Z","shell.execute_reply.started":"2026-03-04T08:21:40.964984Z","shell.execute_reply":"2026-03-04T08:24:55.054792Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 1️⃣2️⃣ Generate Submission File","metadata":{}},{"cell_type":"code","source":"similarities = []\n\nfor _, row in test_df.iterrows():\n    q = embeddings[row.query_image]\n    g = embeddings[row.gallery_image]\n    sim = np.dot(q, g.T)[0][0]\n    similarities.append((sim+1)/2)\n\nsubmission = pd.DataFrame({\n    'row_id': test_df.row_id,\n    'similarity': similarities\n})\n\nsubmission.to_csv('submission.csv', index=False)\nsubmission.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-04T08:27:27.741842Z","iopub.execute_input":"2026-03-04T08:27:27.742582Z","iopub.status.idle":"2026-03-04T08:27:34.347511Z","shell.execute_reply.started":"2026-03-04T08:27:27.742554Z","shell.execute_reply":"2026-03-04T08:27:34.346734Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}