{"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":"nvidiaTeslaT4","dataSources":[{"sourceId":126777,"databundleVersionId":15314950,"sourceType":"competition"}],"dockerImageVersionId":31260,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"pip install transformers","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-02T09:18:13.635074Z","iopub.execute_input":"2026-02-02T09:18:13.635868Z","iopub.status.idle":"2026-02-02T09:18:16.962585Z","shell.execute_reply.started":"2026-02-02T09:18:13.635823Z","shell.execute_reply":"2026-02-02T09:18:16.961713Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"pip install ultralytics","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-02T09:18:16.9642Z","iopub.execute_input":"2026-02-02T09:18:16.964497Z","iopub.status.idle":"2026-02-02T09:18:20.359454Z","shell.execute_reply.started":"2026-02-02T09:18:16.964463Z","shell.execute_reply":"2026-02-02T09:18:20.358608Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport sys\nfrom PIL import Image\nimport requests\nfrom torch import nn\nimport pandas as pd\nimport random\nimport os\nimport csv\nimport numpy as np\nimport json\nimport argparse\nfrom torch.utils.data import Dataset\nimport torch\nfrom torchvision import transforms\nfrom torch.utils.data import DataLoader\nimport matplotlib.pyplot as plt\nimport requests\nfrom sklearn.metrics.pairwise import cosine_similarity\nfrom ultralytics.utils import TQDM\nfrom sklearn.metrics import accuracy_score\nfrom transformers import (\n    ViTImageProcessor,\n    ViTForImageClassification,\n    AutoConfig,\n    BertForSequenceClassification,\n    AutoTokenizer,\n    EvalPrediction,\n    Trainer,\n    TrainingArguments,\n    default_data_collator,\n    set_seed,\n    get_scheduler\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-02T09:18:20.360733Z","iopub.execute_input":"2026-02-02T09:18:20.360971Z","iopub.status.idle":"2026-02-02T09:18:20.367717Z","shell.execute_reply.started":"2026-02-02T09:18:20.36094Z","shell.execute_reply":"2026-02-02T09:18:20.366908Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def seed_everything(seed):\n    random.seed(seed)\n    np.random.seed(seed)\n    torch.manual_seed(seed)\n    if torch.cuda.is_available():\n        torch.cuda.manual_seed_all(seed)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-02T09:18:20.368724Z","iopub.execute_input":"2026-02-02T09:18:20.369003Z","iopub.status.idle":"2026-02-02T09:18:20.385355Z","shell.execute_reply.started":"2026-02-02T09:18:20.368978Z","shell.execute_reply":"2026-02-02T09:18:20.384536Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class TrainDataset(Dataset): \n    def __init__(self,transform=None): \n        self.train_df=pd.read_csv(r'/kaggle/input/jaguar-re-id/train.csv') \n        self.root=r'/kaggle/input/jaguar-re-id/train/train' \n        self.transform=transform\n        self.img_names=self.train_df['filename'].tolist() \n        self.labels=self.train_df['ground_truth'].tolist() \n        self.classes=self.train_df['ground_truth'].unique().tolist() \n    def __len__(self): \n        return len(self.img_names) \n    def __getitem__(self,idx): \n        img_name=self.img_names[idx] \n        img_path=os.path.join(self.root,img_name) \n        img=Image.open(img_path).convert(\"RGB\")\n        if self.transform is not None: \n            img=self.transform(img)\n        label=self.classes.index(self.labels[idx])\n        return img,label\n\nclass TransformDataset(torch.utils.data.Dataset):\n    def __init__(self, dataset, transform=None):\n        self.dataset = dataset\n        self.transform = transform\n    def __getitem__(self, idx):\n        img,label = self.dataset[idx]\n        if self.transform is not None:\n            img = self.transform(img)\n        return img,label\n    def __len__(self):\n        return len(self.dataset)\n\ndef csv_add_row(**kwargs):\n    new_row={key:value for key,value in zip(csv_columns,kwargs.values())}\n    pd.DataFrame([new_row]).to_csv(\n        csv_path,\n        mode=\"a\",\n        header=False,\n        index=False,\n        encoding=\"utf-8\"\n    )\n\ndef dataset_split(dataset, batch_size, train_transform,val_transform,ratio):\n    size = len(dataset)\n    train_size =int(ratio*size)\n    val_size =size-train_size\n    train_dataset, eval_dataset = torch.utils.data.random_split(dataset, [train_size, val_size])\n    train_loader = torch.utils.data.DataLoader(\n        TransformDataset(train_dataset, train_transform),\n        batch_size=batch_size,\n        shuffle=True,\n        num_workers=2,  # 多进程加载（根据CPU核心数设置，如4/8）\n        pin_memory=True,  # 锁页内存，加速GPU数据传输\n        prefetch_factor=2,  # 预加载下一批数据\n        persistent_workers=True,  # 保持工作进程，避免重复创建\n        drop_last=True\n    )\n    eval_loader = torch.utils.data.DataLoader(\n        TransformDataset(eval_dataset, val_transform),\n        batch_size=batch_size,\n        shuffle=False,\n        num_workers=2,  # 多进程加载（根据CPU核心数设置，如4/8）\n        pin_memory=True,  # 锁页内存，加速GPU数据传输\n        prefetch_factor=2,  # 预加载下一批数据\n        persistent_workers=True,  # 保持工作进程，避免重复创建\n        drop_last=True\n    )\n    return train_loader, eval_loader","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-02T09:18:20.387662Z","iopub.execute_input":"2026-02-02T09:18:20.388342Z","iopub.status.idle":"2026-02-02T09:18:20.402282Z","shell.execute_reply.started":"2026-02-02T09:18:20.388297Z","shell.execute_reply":"2026-02-02T09:18:20.401681Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"csv_columns=[\"Epoch\", \"Train_Loss\", \"Train_Acc\", \"Val_Loss\", \"Val_Acc\"]\nnum = 1\nwhile os.path.isdir(os.path.join('train', f'exp{num}')):num += 1\n\nproject_root = '/kaggle/working/'\nroot=os.path.join(project_root,'train')\nsave_dir = os.path.join(root, f\"exp{num}\")\nweights_path = os.path.join(save_dir, 'weights') #模型保存路径\ncsv_path = os.path.join(save_dir, \"training_log.csv\") #csv保存路径\ndef train_one_epoch(model,train_loader,loss,optimizer,device):\n    model.train()\n    train_loss = 0\n    correct = 0\n    total = 0\n    pbar = TQDM(\n        train_loader,\n        desc=\"Training\",\n        total=len(train_loader),\n        unit=\"batch\",\n    )\n    for img, label in pbar:\n        img, label = img.to(device), label.to(device)\n\n        optimizer.zero_grad()\n        output = model(img).logits\n        l = loss(output, label)\n        l.backward()\n        optimizer.step()\n\n        total += label.size(0)\n        train_loss += l.item()*label.size(0)\n        correct += (output.argmax(dim=1) == label).sum().item()\n        pbar.set_postfix(\n            loss=f\"{train_loss / total:.4f}\",\n            acc=f\"{correct / total:.4f}\"\n        )\n    train_loss = train_loss / total\n    train_acc = correct / total\n    pbar.close()\n    return train_loss, train_acc\n\ndef val(model, val_loader, loss, device):\n    model.eval()\n    val_loss = 0\n    correct = 0\n    total = 0\n    pbar = TQDM(\n        val_loader,\n        desc=\"Validation\",\n        total=len(val_loader),\n        unit=\"batch\",\n    )\n    with torch.no_grad():\n        for img, label in pbar:\n            img, label = img.to(device), label.to(device)\n            output = model(img).logits\n            l = loss(output, label)\n            total += label.size(0)\n            val_loss += l.item()*label.size(0)\n            correct += (output.argmax(dim=1) == label).sum().item()\n            pbar.set_postfix(\n                loss=f\"{val_loss / total:.4f}\",\n                acc=f\"{correct / total:.4f}\"\n            )\n    val_loss = val_loss / total\n    val_acc = correct / total\n    pbar.close()\n    return val_loss,val_acc\n\ndef train(model,device,train_loader,val_loader,loss,optimizer,num_epochs,model_save=True,scheduler=None)->None:\n    if not os.path.exists(csv_path):\n        os.makedirs(save_dir, exist_ok=True)\n        df_header = pd.DataFrame(columns=csv_columns)\n        df_header.to_csv(csv_path, index=False, encoding=\"utf-8\")\n    best_acc=0\n    try:\n        for epoch in range(num_epochs):\n            print(f'Epoch:{epoch+1}/{num_epochs}    ')\n            train_loss,train_acc=train_one_epoch(model,train_loader,loss,optimizer,device)\n            val_loss,val_acc=val(model, val_loader, loss, device)\n            csv_add_row(\n                Epoch=epoch+1,\n                Train_Loss=train_loss,\n                Train_Acc=train_acc,\n                Val_Loss=val_loss,\n                Val_Acc=val_acc\n            )\n            if scheduler is not None:\n                scheduler.step()\n            if model_save:\n                os.makedirs(weights_path, exist_ok=True)\n                if best_acc<val_acc:\n                    best_acc=val_acc\n                    torch.save(model.state_dict(),os.path.join(weights_path, \"best.pt\"))\n                torch.save(model.state_dict(),os.path.join(weights_path, \"last.pt\"))\n    except KeyboardInterrupt:\n        print(f\"Training stopped.\")\n        print(f'Best Acc={best_acc:.3f}')\n        print(f'模型保存至{weights_path}目录下')\n        sys.exit(0)\n    except Exception as e:\n        print(e)\n        sys.exit(1)\n    finally:\n        pass\n    print(f'Best Acc={best_acc:.3f}')\n    print(f'模型保存至{weights_path}目录下')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-02T09:18:20.403433Z","iopub.execute_input":"2026-02-02T09:18:20.403748Z","iopub.status.idle":"2026-02-02T09:18:20.421984Z","shell.execute_reply.started":"2026-02-02T09:18:20.40371Z","shell.execute_reply":"2026-02-02T09:18:20.42136Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class NoamScheduler:\n    def __init__(self, optimizer, d, step, warmup_steps=4000):\n        self.optimizer = optimizer\n        self._step = step\n        self.warmup_steps = warmup_steps\n        self.d = d\n        self._rate = 0\n\n    def step(self):\n        self._step += 1\n        rate = (self.d) * min(\n            self._step ** -0.5, self._step * self.warmup_steps ** -1.5\n        )\n        for p in self.optimizer.param_groups:\n            p['lr'] = rate\n        self._rate = rate\n    def zero_grad(self):\n        self.optimizer.zero_grad()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-02T09:18:20.422948Z","iopub.execute_input":"2026-02-02T09:18:20.423269Z","iopub.status.idle":"2026-02-02T09:18:20.435546Z","shell.execute_reply.started":"2026-02-02T09:18:20.423215Z","shell.execute_reply":"2026-02-02T09:18:20.434716Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def parse_args():\n    parser = argparse.ArgumentParser()\n    parser.add_argument('--output_dir', type=str, default='/kaggle/working/experiments')\n    parser.add_argument('--epoch', type=int, default=8)\n    parser.add_argument('--bs', type=int, default=32)\n    parser.add_argument('--seed', type=int, default=42, help='random seed')\n\n    parser.add_argument(\"--learning_rate\", type=float, default=5e-5, help=\"Learning rate for AdamW optimizer\")\n    parser.add_argument(\"--weight_decay\", type=float, default=0.01, help=\"Weight decay for AdamW optimizer\")\n    parser.add_argument(\"--adam_beta1\", type=float, default=0.9, help=\"Beta1 for Adam optimizer\")\n    parser.add_argument(\"--adam_beta2\", type=float, default=0.999, help=\"Beta2 for Adam optimizer\")\n    parser.add_argument(\"--adam_epsilon\", type=float, default=1e-8, help=\"Epsilon for Adam optimizer\")\n    parser.add_argument(\"--max_grad_norm\", type=float, default=1.0, help=\"Maximum gradient norm (for clipping)\")\n    parser.add_argument(\"--max_steps\", type=int, default=-1, help=\"Max number of training steps\")\n    parser.add_argument(\"--lr_scheduler_type\", type=str, default=\"cosine\", choices=[\"linear\", \"cosine\"], help=\"Learning rate scheduler type\")\n    parser.add_argument(\"--warmup_ratio\", type=float, default=0.0, help=\"Ratio of training steps for warmup\")\n    parser.add_argument(\"--warmup_steps\", type=int, default=0, help=\"Number of warmup steps\")\n    \n    args ,_= parser.parse_known_args()\n    return args","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-02T09:18:20.436442Z","iopub.execute_input":"2026-02-02T09:18:20.436743Z","iopub.status.idle":"2026-02-02T09:18:20.448818Z","shell.execute_reply.started":"2026-02-02T09:18:20.436705Z","shell.execute_reply":"2026-02-02T09:18:20.448161Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def main():\n    args = parse_args()\n    print(\"args\",args)\n    print('====Input Arguments====')\n    print(json.dumps(vars(args), indent=2, sort_keys=False))\n    \n    # Set seed before initializing model, for reproduction purpose.\n    set_seed(args.seed)\n    seed_everything(args.seed)\n    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n    # Load pretrained model and tokenizer\n    processor = ViTImageProcessor.from_pretrained('google/vit-base-patch16-224')\n    model = ViTForImageClassification.from_pretrained('google/vit-base-patch16-224')\n    model.classifier=nn.Linear(in_features=768, out_features=31, bias=True)\n    model.config.num_labels = 31 \n    if torch.cuda.device_count() > 1:\n        print(f\"Using {torch.cuda.device_count()} GPUs!\")\n        model = nn.DataParallel(model)  # 使用DataParallel包装模型\n    model.to(device)\n    # Load data\n    dataset = TrainDataset(transform=transforms.ToTensor())\n    train_transform =transforms.Compose([\n        # 随机裁剪图像，所得图像为原始面积的0.08～1之间，高宽比在3/4和4/3之间。\n        # 然后，缩放图像以创建512的新图像\n        transforms.RandomResizedCrop(224, scale=(0.08, 1.0),ratio=(3.0/4.0, 4.0/3.0)),\n        transforms.RandomHorizontalFlip(),\n        # 随机更改亮度，对比度和饱和度\n        transforms.ColorJitter(brightness=0.4, contrast=0.4, saturation=0.4),  # 添加随机噪声\n        # 标准化图像的每个通道\n        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])\n    ])\n    val_transform = transforms.Compose([\n        transforms.Resize(224),\n        # 从图像中心裁切224x224大小的图片\n        transforms.CenterCrop(224),\n        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])\n    ])\n    train_loader, eval_loader = dataset_split(dataset=dataset, batch_size=args.bs, train_transform=train_transform,val_transform=val_transform,ratio=0.8)\n    \n    loss=nn.CrossEntropyLoss()\n    \n    optimizer = torch.optim.AdamW(\n        model.parameters(),\n        lr=args.learning_rate,\n        weight_decay=args.weight_decay,\n        betas=(args.adam_beta1, args.adam_beta2),\n        eps=args.adam_epsilon\n    )\n    if args.max_grad_norm > 0:\n        torch.nn.utils.clip_grad_norm_(model.parameters(), args.max_grad_norm)\n    scheduler = get_scheduler(\n        args.lr_scheduler_type,\n        optimizer=optimizer,\n        num_warmup_steps=args.warmup_steps,\n        num_training_steps=len(train_loader) * args.epoch\n    )\n    train(model,device,train_loader,eval_loader,loss,optimizer,num_epochs=args.epoch,scheduler=scheduler)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-02T09:18:20.449736Z","iopub.execute_input":"2026-02-02T09:18:20.450158Z","iopub.status.idle":"2026-02-02T09:18:20.461975Z","shell.execute_reply.started":"2026-02-02T09:18:20.450134Z","shell.execute_reply":"2026-02-02T09:18:20.46138Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def submission():\n    test_transform = transforms.Compose([\n        transforms.Resize(224),\n        # 从图像中心裁切224x224大小的图片\n        transforms.CenterCrop(224),\n        transforms.ToTensor(),\n        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])\n    ])\n    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n    # Load pretrained model and tokenizer\n    processor = ViTImageProcessor.from_pretrained('google/vit-base-patch16-224')\n    model = ViTForImageClassification.from_pretrained('google/vit-base-patch16-224')\n    model.classifier=nn.Linear(in_features=768, out_features=31, bias=True)\n    model.config.num_labels = 31 \n    \n    if torch.cuda.device_count() > 1:\n        print(f\"Using {torch.cuda.device_count()} GPUs!\")\n        model = nn.DataParallel(model)  # 使用DataParallel包装模型\n    model.to(device)\n\n    state_dict = torch.load(r'/kaggle/working/train/exp1/weights/best.pt', weights_only=True,map_location='cuda')\n    # 3. 将参数载入模型\n    model.load_state_dict(state_dict)\n    # Load test data\n    test_df = pd.read_csv('/kaggle/input/jaguar-re-id/test.csv')\n    # Get unique image filenames\n    unique_images = sorted(set(test_df['query_image']) | set(test_df['gallery_image']))\n    n_images = len(unique_images)  # Should be 371\n    # Extract embeddings for all images\n    model.eval()\n    embeddings = []\n    pbar = TQDM(\n            unique_images,\n            desc=\"Predicting\",\n            total=len(unique_images),\n            unit=\"batch\",\n        )\n    for img_file in pbar:\n        img = Image.open(f'/kaggle/input/jaguar-re-id/test/test/{img_file}').convert(\"RGB\")\n        img = test_transform(img).unsqueeze(0).to(device)\n        emb = model(img).logits          \n        emb = emb.squeeze(0)           \n        emb = emb.detach().cpu()       \n        embeddings.append(emb)\n\n    pbar.close()\n    embeddings = torch.stack(embeddings).numpy()\n    # Compute similarity matrix (371 × 371)\n    similarity_matrix = cosine_similarity(embeddings)\n    # Ensure values are in [0, 1]\n    similarity_matrix = np.clip(similarity_matrix, 0, 1)\n    # Create filename to index mapping\n    img_to_idx = {img: idx for idx, img in enumerate(unique_images)}\n    # Extract similarities for each test pair\n    similarities = []\n    for _, row in test_df.iterrows():\n        query_idx = img_to_idx[row['query_image']]\n        gallery_idx = img_to_idx[row['gallery_image']]\n        sim = similarity_matrix[query_idx, gallery_idx]\n        similarities.append(sim)\n    # Create submission\n    submission = pd.DataFrame({\n        'row_id': test_df['row_id'],\n        'similarity': similarities\n    })\n    submission.to_csv('/kaggle/working/submission.csv', index=False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-02T09:18:20.462847Z","iopub.execute_input":"2026-02-02T09:18:20.463623Z","iopub.status.idle":"2026-02-02T09:18:20.475958Z","shell.execute_reply.started":"2026-02-02T09:18:20.463597Z","shell.execute_reply":"2026-02-02T09:18:20.475296Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"if __name__ == \"__main__\":\n    # main()\n    submission()\n    ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-02T09:18:20.476831Z","iopub.execute_input":"2026-02-02T09:18:20.477098Z","iopub.status.idle":"2026-02-02T09:20:53.386441Z","shell.execute_reply.started":"2026-02-02T09:18:20.477074Z","shell.execute_reply":"2026-02-02T09:20:53.385438Z"}},"outputs":[],"execution_count":null}]}