{"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":"none","dataSources":[{"sourceType":"competition","sourceId":126777,"databundleVersionId":15314950}],"dockerImageVersionId":31286,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os\nimport pandas as pd\nimport numpy as np\nfrom PIL import Image\nfrom tqdm import tqdm\n\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom torch.utils.data import Dataset, DataLoader\nfrom torchvision import transforms, models\n\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nprint(\"Using device:\", device)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-04T11:23:55.174921Z","iopub.execute_input":"2026-03-04T11:23:55.175296Z","iopub.status.idle":"2026-03-04T11:23:55.182365Z","shell.execute_reply.started":"2026-03-04T11:23:55.175254Z","shell.execute_reply":"2026-03-04T11:23:55.181579Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"TRAIN_DIR = \"/kaggle/input/competitions/jaguar-re-id/train/train\"\nTEST_DIR  = \"/kaggle/input/competitions/jaguar-re-id/test/test\"\n\nBATCH_SIZE = 32\nEPOCHS = 15\nLR = 3e-4\nEMBED_DIM = 256","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-04T11:23:55.689460Z","iopub.execute_input":"2026-03-04T11:23:55.690031Z","iopub.status.idle":"2026-03-04T11:23:55.694149Z","shell.execute_reply.started":"2026-03-04T11:23:55.690005Z","shell.execute_reply":"2026-03-04T11:23:55.693364Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_tfms = transforms.Compose([\n    transforms.Resize((256, 256)),\n    transforms.RandomHorizontalFlip(),\n    transforms.RandomRotation(10),\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\ntest_tfms = transforms.Compose([\n    transforms.Resize((256, 256)),\n    transforms.ToTensor(),\n    transforms.Normalize(\n        mean=[0.485, 0.456, 0.406],\n        std=[0.229, 0.224, 0.225]\n    )\n])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-04T11:23:57.635359Z","iopub.execute_input":"2026-03-04T11:23:57.636187Z","iopub.status.idle":"2026-03-04T11:23:57.641484Z","shell.execute_reply.started":"2026-03-04T11:23:57.636156Z","shell.execute_reply":"2026-03-04T11:23:57.640778Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class JaguarDataset(Dataset):\n    def __init__(self, dataframe, root_dir, transform=None):\n        self.df = dataframe\n        self.root = root_dir\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.df)\n\n    def __getitem__(self, idx):\n        row = self.df.iloc[idx]\n        img_path = os.path.join(self.root, row[\"filename\"])\n        image = Image.open(img_path).convert(\"RGB\")\n\n        if self.transform:\n            image = self.transform(image)\n\n        if \"ground_truth\" in self.df.columns:\n            label = row[\"ground_truth\"]\n            return image, label\n        else:\n            return image, row[\"filename\"]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-04T11:23:58.097843Z","iopub.execute_input":"2026-03-04T11:23:58.098389Z","iopub.status.idle":"2026-03-04T11:24:28.145522Z","shell.execute_reply.started":"2026-03-04T11:23:58.098362Z","shell.execute_reply":"2026-03-04T11:24:28.144512Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(train_df.columns)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-04T11:24:28.147188Z","iopub.execute_input":"2026-03-04T11:24:28.147530Z","iopub.status.idle":"2026-03-04T11:24:28.162483Z","shell.execute_reply.started":"2026-03-04T11:24:28.147496Z","shell.execute_reply":"2026-03-04T11:24:28.161837Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_df = pd.read_csv(\"/kaggle/input/competitions/jaguar-re-id/train.csv\")\ntest_df  = pd.read_csv(\"/kaggle/input/competitions/jaguar-re-id/test.csv\")\n\n# Encode labels\nlabel2idx = {l: i for i, l in enumerate(train_df[\"ground_truth\"].unique())}\nidx2label = {v: k for k, v in label2idx.items()}\n\ntrain_df[\"ground_truth\"] = train_df[\"ground_truth\"].map(label2idx)\n\nnum_classes = len(label2idx)\nprint(\"Classes:\", num_classes)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-04T11:24:28.163458Z","iopub.execute_input":"2026-03-04T11:24:28.163805Z","iopub.status.idle":"2026-03-04T11:24:28.265444Z","shell.execute_reply.started":"2026-03-04T11:24:28.163768Z","shell.execute_reply":"2026-03-04T11:24:28.264657Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_dataset = JaguarDataset(train_df, TRAIN_DIR, train_tfms)\ntest_dataset  = JaguarDataset(test_df, TEST_DIR, test_tfms)\n\ntrain_loader = DataLoader(\n    train_dataset,\n    batch_size=BATCH_SIZE,\n    shuffle=True,\n    num_workers=2\n)\n\ntest_loader = DataLoader(\n    test_dataset,\n    batch_size=BATCH_SIZE,\n    shuffle=False,\n    num_workers=2\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-04T11:24:28.267153Z","iopub.execute_input":"2026-03-04T11:24:28.267488Z","iopub.status.idle":"2026-03-04T11:24:28.276203Z","shell.execute_reply.started":"2026-03-04T11:24:28.267455Z","shell.execute_reply":"2026-03-04T11:24:28.275436Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class JaguarReIDModel(nn.Module):\n    def __init__(self, num_classes, embedding_dim=256):\n        super().__init__()\n\n        backbone = models.resnet50(\n            weights=models.ResNet50_Weights.IMAGENET1K_V1\n        )\n\n        self.features = nn.Sequential(*list(backbone.children())[:-1])\n        self.embedding = nn.Linear(2048, embedding_dim)\n        self.classifier = nn.Linear(embedding_dim, num_classes)\n\n    def forward(self, x):\n        x = self.features(x)\n        x = x.view(x.size(0), -1)\n        x = self.embedding(x)\n        x = F.relu(x)\n\n        logits = self.classifier(x)\n        return x, logits","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-04T11:24:28.277407Z","iopub.execute_input":"2026-03-04T11:24:28.277921Z","iopub.status.idle":"2026-03-04T11:24:28.289872Z","shell.execute_reply.started":"2026-03-04T11:24:28.277896Z","shell.execute_reply":"2026-03-04T11:24:28.289146Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model = JaguarReIDModel(num_classes, EMBED_DIM).to(device)\n\ncriterion = nn.CrossEntropyLoss()\noptimizer = torch.optim.Adam(model.parameters(), lr=LR)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-04T11:24:28.290674Z","iopub.execute_input":"2026-03-04T11:24:28.290954Z","iopub.status.idle":"2026-03-04T11:24:28.804009Z","shell.execute_reply.started":"2026-03-04T11:24:28.290932Z","shell.execute_reply":"2026-03-04T11:24:28.803362Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"for epoch in range(EPOCHS):\n    model.train()\n    total_loss = 0\n\n    for images, labels in tqdm(train_loader):\n        images = images.to(device)\n        labels = labels.to(device)\n\n        embeddings, logits = model(images)\n        loss = criterion(logits, labels)\n\n        optimizer.zero_grad()\n        loss.backward()\n        optimizer.step()\n\n        total_loss += loss.item()\n\n    print(f\"Epoch {epoch+1}/{EPOCHS} | Loss: {total_loss/len(train_loader):.4f}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-04T11:24:28.804776Z","iopub.execute_input":"2026-03-04T11:24:28.805003Z","iopub.status.idle":"2026-03-04T12:51:34.904173Z","shell.execute_reply.started":"2026-03-04T11:24:28.804982Z","shell.execute_reply":"2026-03-04T12:51:34.903130Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"unique_test_images = pd.unique(\n    test_df[['query_image', 'gallery_image']].values.ravel()\n)\n\nprint(\"Unique test images:\", len(unique_test_images))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-04T13:01:36.445640Z","iopub.execute_input":"2026-03-04T13:01:36.446310Z","iopub.status.idle":"2026-03-04T13:01:36.479317Z","shell.execute_reply.started":"2026-03-04T13:01:36.446282Z","shell.execute_reply":"2026-03-04T13:01:36.478572Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class TestImageDataset(Dataset):\n    def __init__(self, image_list, root_dir, transform):\n        self.images = image_list\n        self.root = root_dir\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.images)\n\n    def __getitem__(self, idx):\n        img_name = self.images[idx]\n        img_path = os.path.join(TEST_DIR, img_name)\n        image = Image.open(img_path).convert(\"RGB\")\n        image = self.transform(image)\n        return image, img_name","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-04T13:01:38.144504Z","iopub.execute_input":"2026-03-04T13:01:38.145125Z","iopub.status.idle":"2026-03-04T13:01:38.150253Z","shell.execute_reply.started":"2026-03-04T13:01:38.145096Z","shell.execute_reply":"2026-03-04T13:01:38.149436Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test_image_dataset = TestImageDataset(unique_test_images, TEST_DIR, test_tfms)\n\ntest_image_loader = DataLoader(\n    test_image_dataset,\n    batch_size=32,\n    shuffle=False,\n    num_workers=2\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-04T13:01:40.133010Z","iopub.execute_input":"2026-03-04T13:01:40.133323Z","iopub.status.idle":"2026-03-04T13:01:40.138465Z","shell.execute_reply.started":"2026-03-04T13:01:40.133297Z","shell.execute_reply":"2026-03-04T13:01:40.137480Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def extract_test_embeddings(loader):\n    model.eval()\n    embeddings_dict = {}\n\n    with torch.no_grad():\n        for images, names in tqdm(loader):\n            images = images.to(device)\n            emb, _ = model(images)\n            emb = F.normalize(emb, dim=1).cpu()\n\n            for i, name in enumerate(names):\n                embeddings_dict[name] = emb[i]\n\n    return embeddings_dict","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-04T13:01:42.248202Z","iopub.execute_input":"2026-03-04T13:01:42.248518Z","iopub.status.idle":"2026-03-04T13:01:42.254044Z","shell.execute_reply.started":"2026-03-04T13:01:42.248493Z","shell.execute_reply":"2026-03-04T13:01:42.253277Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test_embeddings = extract_test_embeddings(test_image_loader)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-04T13:01:42.791460Z","iopub.execute_input":"2026-03-04T13:01:42.792253Z","iopub.status.idle":"2026-03-04T13:02:54.414648Z","shell.execute_reply.started":"2026-03-04T13:01:42.792225Z","shell.execute_reply":"2026-03-04T13:02:54.413729Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"scores = []\n\nfor _, row in tqdm(test_df.iterrows(), total=len(test_df)):\n    q_emb = test_embeddings[row[\"query_image\"]]\n    g_emb = test_embeddings[row[\"gallery_image\"]]\n\n    similarity = torch.dot(q_emb, g_emb).item()\n    scores.append(similarity)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-04T13:03:00.820107Z","iopub.execute_input":"2026-03-04T13:03:00.820794Z","iopub.status.idle":"2026-03-04T13:03:08.637658Z","shell.execute_reply.started":"2026-03-04T13:03:00.820761Z","shell.execute_reply":"2026-03-04T13:03:08.636902Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"submission = pd.DataFrame({\n    \"row_id\": test_df[\"row_id\"],\n    \"similarity\": scores \n})\n\nsubmission.to_csv(\"submission.csv\", index=False)\nprint(\"Submission saved!\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-04T13:04:40.889801Z","iopub.execute_input":"2026-03-04T13:04:40.890365Z","iopub.status.idle":"2026-03-04T13:04:41.169866Z","shell.execute_reply.started":"2026-03-04T13:04:40.890335Z","shell.execute_reply":"2026-03-04T13:04:41.169193Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}