{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":29762,"databundleVersionId":2541532,"sourceType":"competition"},{"sourceId":14238105,"sourceType":"datasetVersion","datasetId":9083844},{"sourceId":686273,"sourceType":"modelInstanceVersion","isSourceIdPinned":true,"modelInstanceId":520543,"modelId":534825}],"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os\nimport shutil\nimport sys\n\n# ==========================================\n# 1. 环境初始化 \n# ==========================================\nprint(\"Step 1/3: Initializing Environment...\")\n\nif os.getcwd() != \"/kaggle/working\":\n    os.chdir(\"/kaggle/working\")\nprint(f\"Current working directory: {os.getcwd()}\")\n\nbase_dir = \"/kaggle/working/code\"\ndirs = {\n    \"root\": base_dir,\n    \"configs\": os.path.join(base_dir, \"configs\"),\n    \"models\": os.path.join(base_dir, \"models\"),\n    \"data\": os.path.join(base_dir, \"data\")\n}\n\n# 1.1 清理并重建目录\nif os.path.exists(base_dir): \n    print(\"Cleaning old directory...\")\n    shutil.rmtree(base_dir)\n    \nfor d in dirs.values():\n    os.makedirs(d, exist_ok=True)\n    with open(os.path.join(d, \"__init__.py\"), 'w') as f: pass\nprint(\"Directory structure created.\")\n\n# 1.2 复制 Data (使用已知路径)\nsource_data_path = \"/kaggle/input/dinoehst/pytorch/default/1/kaggle-landmark-2021-1st-place-main - 副本/data\"\n\nif os.path.exists(source_data_path):\n    print(f\"Copying data from: {source_data_path}\")\n    shutil.copytree(source_data_path, dirs[\"data\"], dirs_exist_ok=True)\n    print(\" Data module copied successfully.\")\nelse:\n    print(f\" Path not found: {source_data_path}\")\n    for root, dirs, files in os.walk(\"/kaggle/input\"):\n        if \"data\" in dirs and \"ch_ds_1.py\" in os.listdir(os.path.join(root, \"data\")):\n            src = os.path.join(root, \"data\")\n            print(f\"Found alternative path: {src}\")\n            shutil.copytree(src, dirs[\"data\"], dirs_exist_ok=True)\n            break\n\nprint(\"Installing dependencies...\")\nos.system(\"pip install -q timm --upgrade\")\nprint(\"Dependencies installed.\")\n\n# ==========================================\n# 2. 写入配置文件\n# ==========================================\nprint(\"Step 2/3: Generating Configs...\")\n\n# default_config.py\nwith open(os.path.join(dirs[\"configs\"], \"default_config.py\"), \"w\") as f:\n    f.write(\"\"\"\nfrom types import SimpleNamespace\ncfg = SimpleNamespace(**{})\ncfg.suffix = \".jpg\"\ncfg.normalization = 'imagenet'\nbasic_cfg = cfg\n\"\"\")\n\nwith open(os.path.join(dirs[\"configs\"], \"cfg_kaggle_dino.py\"), \"w\") as f:\n    f.write(\"\"\"\nfrom default_config import basic_cfg\nimport albumentations as A\ncfg = basic_cfg\ncfg.model = \"ch_mdl_dino\"\ncfg.backbone = \"vit_base_patch14_dinov2.lvd142m\"\ncfg.img_size = (448, 448)\ncfg.embedding_size = 512\ncfg.batch_size = 32\ncfg.lr = 1e-4\ncfg.optimizer = \"adamw\"\ncfg.epochs = 1\ncfg.freeze_backbone = True\ncfg.normalization = 'imagenet' \ncfg.arcface_m_x = 0.45; cfg.arcface_m_y = 0.05; cfg.arcface_s = 45.0\nimage_size = cfg.img_size[0]\ncfg.train_aug = A.Compose([A.HorizontalFlip(p=0.5), A.Resize(image_size, image_size)])\ncfg.val_aug = A.Compose([A.Resize(image_size, image_size)])\n\"\"\")\n\n# ==========================================\n# 3. 写入模型文件 \n# ==========================================\nprint(\"Step 3/3: Generating Model...\")\n\nmodel_code = \"\"\"\nimport timm\nimport torch\nfrom torch import nn\nfrom torch.nn import functional as F\nimport math\nimport numpy as np\n\nclass DenseCrossEntropy(nn.Module):\n    def forward(self, x, target):\n        logprobs = F.log_softmax(x.float(), dim=-1)\n        loss = -logprobs * target.float()\n        return loss.sum(-1).mean()\n\nclass ArcMarginProduct_subcenter(nn.Module):\n    def __init__(self, in_f, out_f, k=3):\n        super().__init__(); self.weight = nn.Parameter(torch.FloatTensor(out_f*k, in_f)); nn.init.xavier_uniform_(self.weight); self.k, self.out_features = k, out_f\n    def forward(self, features):\n        cosine_all = F.linear(F.normalize(features), F.normalize(self.weight))\n        cosine_all = cosine_all.view(-1, self.out_features, self.k)\n        cosine, _ = torch.max(cosine_all, dim=2); return cosine\n\nclass ArcFaceLossAdaptiveMargin(nn.Module):\n    def __init__(self, margins, n_classes, s=30.0):\n        super().__init__(); self.crit, self.s, self.margins, self.out_dim = DenseCrossEntropy(), s, margins, n_classes\n    def forward(self, logits, labels):\n        ms = self.margins[labels.cpu().numpy()] if isinstance(self.margins, np.ndarray) else np.array([0.5]*len(labels))\n        cos_m, sin_m, th, mm = [torch.from_numpy(np.cos(ms)).float().to(logits.device), torch.from_numpy(np.sin(ms)).float().to(logits.device), torch.from_numpy(np.cos(math.pi-ms)).float().to(logits.device), torch.from_numpy(np.sin(math.pi-ms)*ms).float().to(logits.device)]\n        labels_ohe = F.one_hot(labels, self.out_dim).float(); cosine = logits.float(); sine = torch.sqrt(1.0-cosine.pow(2))\n        phi = cosine * cos_m.view(-1,1) - sine * sin_m.view(-1,1); phi = torch.where(cosine > th.view(-1,1), phi, cosine - mm.view(-1,1))\n        output = (labels_ohe * phi) + ((1.0-labels_ohe)*cosine); output *= self.s; return self.crit(output, labels_ohe)\n\nclass Net(nn.Module):\n    def __init__(self, cfg, dataset):\n        super(Net, self).__init__(); self.cfg, self.n_classes = cfg, cfg.n_classes\n        print(f\">>> Loading Backbone: {cfg.backbone}\")\n        self.backbone = timm.create_model(cfg.backbone, pretrained=True, num_classes=0, img_size=cfg.img_size[0])\n        if getattr(cfg, 'freeze_backbone', False): \n            print(\"Freezing backbone...\")\n            for p in self.backbone.parameters(): p.requires_grad = False\n        self.neck = nn.Sequential(nn.Linear(self.backbone.num_features, cfg.embedding_size, bias=True), nn.BatchNorm1d(cfg.embedding_size))\n        if not hasattr(cfg, 'headless') or not cfg.headless:\n            self.head = ArcMarginProduct_subcenter(cfg.embedding_size, self.n_classes)\n            margins = getattr(dataset, 'margins', None)\n            self.loss_fn = ArcFaceLossAdaptiveMargin(margins, self.n_classes, cfg.arcface_s)\n\n    def forward(self, batch):\n        x = batch['input']\n        features = self.backbone.forward_features(x)\n        # 修复 IndexError: 兼容 Tensor 和 Dict 输出\n        if isinstance(features, dict): x_global = features['x_norm_clstoken']\n        else: x_global = features[:, 0]\n        x_emb = self.neck(x_global)\n        if hasattr(self.cfg, 'headless') and self.cfg.headless: return {'embeddings': x_emb}\n        logits = self.head(x_emb)\n        if self.training: return {'loss': self.loss_fn(logits, batch['target'].long())}\n        else: return {'loss': torch.zeros(1), 'embeddings': x_emb}\n\"\"\"\n\nwith open(os.path.join(dirs[\"models\"], \"ch_mdl_dino.py\"), \"w\") as f:\n    f.write(model_code)\n\nprint(\"All files ready. Please run Cell 2.\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-21T06:14:43.074615Z","iopub.execute_input":"2025-12-21T06:14:43.074849Z","iopub.status.idle":"2025-12-21T06:16:08.264870Z","shell.execute_reply.started":"2025-12-21T06:14:43.074823Z","shell.execute_reply":"2025-12-21T06:16:08.264253Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# import sys\n# import os\n# import pandas as pd\n# import torch\n# from torch.utils.data import DataLoader\n# from tqdm.notebook import tqdm\n# import importlib.util\n# import math\n# from torch import nn\n# from torch.nn import functional as F\n# import numpy as np\n# import albumentations as A\n# from sklearn.model_selection import train_test_split \n\n# # Setup paths and change working directory\n# base_dir = \"/kaggle/working/code\"\n# if os.path.exists(base_dir):\n#     os.chdir(base_dir)\n#     sys.path.insert(0, base_dir)\n#     sys.path.insert(0, os.path.join(base_dir, \"configs\"))\n\n# # Dynamic module loader\n# def load_module(name, path):\n#     spec = importlib.util.spec_from_file_location(name, path)\n#     mod = importlib.util.module_from_spec(spec)\n#     sys.modules[name] = mod\n#     spec.loader.exec_module(mod)\n#     return mod\n\n# try:\n#     # 1. Patch ch_ds_1.py to fix KeyError and ensure stability\n#     ds_path = os.path.join(base_dir, \"data\", \"ch_ds_1.py\")\n#     if os.path.exists(ds_path):\n#         with open(ds_path, 'r') as f: lines = f.readlines()\n#         with open(ds_path, 'w') as f:\n#             for line in lines:\n#                 # Comment out problematic lines involving 'landmarks' column\n#                 if \"self.df['landmarks'].apply\" in line or \"self.df['landmarks'].fillna\" in line:\n#                     f.write(\"# \" + line) \n#                 else:\n#                     f.write(line)\n#         print(\" Patched data/ch_ds_1.py\")\n\n#     # 2. Load Modules\n#     load_module(\"default_config\", os.path.join(base_dir, \"configs\", \"default_config.py\"))\n#     cfg_module = load_module(\"cfg_kaggle_dino\", os.path.join(base_dir, \"configs\", \"cfg_kaggle_dino.py\"))\n#     cfg = cfg_module.cfg\n    \n#     # Ensure critical config keys exist\n#     if not hasattr(cfg, 'suffix'): cfg.suffix = \".jpg\"\n    \n#     ds_module = load_module(\"ch_ds_1_patched\", ds_path) \n#     CustomDataset = ds_module.CustomDataset\n    \n#     mdl_path = os.path.join(base_dir, \"models\", \"ch_mdl_dino.py\")\n#     Net = load_module(\"ch_mdl_dino\", mdl_path).Net\n\n#     # 3. Data Preparation & Cleaning\n#     train_csv = \"/kaggle/input/landmark-recognition-2021/train.csv\"\n#     train_img_dir = \"/kaggle/input/landmark-recognition-2021/train/\"\n    \n#     print(f\"\\n Reading CSV and creating data pool...\")\n#     # Load first 150,000 rows (Balanced pool)\n#     raw_df = pd.read_csv(train_csv).iloc[:150000] \n#     print(f\"Original Pool Size: {len(raw_df)}\")\n\n#     # === Data Cleaning Step ===\n#     print(\" Cleaning data: Removing classes with < 15 samples...\")\n#     counts = raw_df['landmark_id'].value_counts()\n#     valid_landmarks = counts[counts >= 15].index\n#     filtered_df = raw_df[raw_df['landmark_id'].isin(valid_landmarks)].copy()\n#     print(f\"Cleaned Data Size: {len(filtered_df)}\")\n#     print(f\"Valid Classes: {len(valid_landmarks)}\")\n    \n#     # Label Encoding\n#     unique_landmarks = filtered_df['landmark_id'].unique()\n#     landmark_map = { lid: i for i, lid in enumerate(unique_landmarks) }\n#     filtered_df['target'] = filtered_df['landmark_id'].map(landmark_map)\n    \n#     # Train/Val Split (Stratified)\n#     train_df, val_df = train_test_split(filtered_df, test_size=0.1, random_state=42, stratify=filtered_df['landmark_id'])\n#     print(f\" Training Set: {len(train_df)} | Validation Set: {len(val_df)}\")\n    \n#     # Update Config\n#     cfg.n_classes = len(unique_landmarks)\n#     cfg.data_folder = train_img_dir\n#     cfg.val_data_folder = train_img_dir # Fix: Add val_data_folder\n#     cfg.landmark_id2class_id = pd.DataFrame({'landmark_id': unique_landmarks, 'class_id': range(len(unique_landmarks))})\n    \n#     # 4. DataLoaders\n#     train_ds = CustomDataset(train_df, cfg, cfg.train_aug, mode='train')\n#     train_loader = DataLoader(\n#         train_ds, \n#         batch_size=cfg.batch_size, \n#         shuffle=True, \n#         num_workers=2,            # 尝试开启双进程\n#         pin_memory=True,          # 加速 GPU 传输\n#         persistent_workers=True,  # 核心：保持进程存活，避免反复启动卡顿\n#         prefetch_factor=2,        # 限制预取数量，防止内存溢出卡死\n#         drop_last=True            # 丢弃最后一个不完整的 Batch，有时候能增加稳定性\n#     )\n    \n#     # 验证集 Loader\n#     val_ds = CustomDataset(val_df, cfg, cfg.val_aug, mode='val')\n#     val_loader = DataLoader(\n#         val_ds, \n#         batch_size=cfg.batch_size, \n#         shuffle=False, \n#         num_workers=2,            # 验证集也开双进程\n#         pin_memory=True, \n#         persistent_workers=True,\n#         prefetch_factor=2\n#     )\n\n#     # 5. Model Setup\n#     device = torch.device(\"cuda\")\n#     model = Net(cfg, train_ds)\n#     model.to(device)\n#     optimizer = torch.optim.AdamW(model.parameters(), lr=cfg.lr)\n    \n#     # 6. Training Loop (for 1 epoch)\n#     print(f\"\\n Starting Training for {cfg.epochs} Epoch(s)...\")\n    \n#     history = {'train_loss': [], 'val_acc': []}\n#     for epoch in range(cfg.epochs):\n#         # Train\n#         model.train()\n#         train_loss = 0\n#         pbar = tqdm(train_loader, desc=f\"Epoch {epoch+1}/{cfg.epochs} [Train]\")\n#         for batch in pbar:\n#             inputs, targets = batch['input'].to(device), batch['target'].to(device)\n#             loss = model({'input': inputs, 'target': targets})['loss']\n#             optimizer.zero_grad(); loss.backward(); optimizer.step()\n#             train_loss += loss.item()\n#             pbar.set_postfix({'loss': f\"{loss.item():.4f}\"})\n#         avg_train_loss = train_loss / len(train_loader)\n#         history['train_loss'].append(avg_train_loss)\n        \n#         # Validation\n#         model.eval()\n#         correct, total = 0, 0\n#         with torch.no_grad():\n#             for batch in tqdm(val_loader, desc=f\"Epoch {epoch+1} [Valid]\"):\n#                 inputs, targets = batch['input'].to(device), batch['target'].to(device)\n                \n#                 # --- FIX: Robust Feature Extraction ---\n#                 features = model.backbone.forward_features(inputs)\n                \n#                 # Handle different timm versions (Dict vs Tensor)\n#                 if isinstance(features, dict):\n#                     x_global = features['x_norm_clstoken']\n#                 else:\n#                     # New timm returns [B, N, C], CLS token is at index 0\n#                     x_global = features[:, 0]\n                \n#                 logits = model.head(model.neck(x_global))\n\n#                 _, predicted = torch.max(logits.data, 1)\n#                 total += batch['target'].size(0)\n#                 correct += (predicted == batch['target'].to(device)).sum().item()\n        \n#         val_acc = 100 * correct / total\n#         history['val_acc'].append(val_acc)\n        \n#         print(f\" Epoch {epoch+1}: Train Loss = {avg_train_loss:.4f} | Val Accuracy = {val_acc:.2f}%\")\n\n#     # Save checkpoint after the first epoch\n#     torch.save(model.state_dict(), \"dino_finetuned_epoch1.pth\")\n#     print(\"\\n Training Finished & Checkpoint 'dino_finetuned_epoch1.pth' Saved!\")\n\n# except Exception as e:\n#     import traceback\n#     traceback.print_exc()\n#     print(f\" Error: {e}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-18T02:24:32.641203Z","iopub.execute_input":"2025-12-18T02:24:32.641479Z","iopub.status.idle":"2025-12-18T04:56:40.609927Z","shell.execute_reply.started":"2025-12-18T02:24:32.641458Z","shell.execute_reply":"2025-12-18T04:56:40.609075Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import sys\nimport os\nimport pandas as pd\nimport torch\nfrom torch.utils.data import DataLoader\nfrom tqdm.notebook import tqdm\nimport importlib.util\nimport math\nfrom torch import nn\nfrom torch.nn import functional as F\nimport numpy as np\nimport albumentations as A\nfrom sklearn.model_selection import train_test_split \n\n# ==========================================\n# 配置区域\n# ==========================================\n# 1. 权重路径 \nLOAD_FROM = \"/kaggle/input/dino-finetuned-epoch1-pth/dino_finetuned_epoch1.pth\" \n\n# 2. 续训参数\nMORE_EPOCHS = 4  \nNEW_LR = 5e-5  \n\nbase_dir = \"/kaggle/working/code\"\ndirs = {\n    \"root\": base_dir,\n    \"configs\": os.path.join(base_dir, \"configs\"),\n    \"models\": os.path.join(base_dir, \"models\"),\n    \"data\": os.path.join(base_dir, \"data\")\n}\n\nif os.path.exists(base_dir):\n    os.chdir(base_dir)\n    sys.path.insert(0, base_dir)\n    sys.path.insert(0, dirs[\"configs\"])\n\nds_path = os.path.join(dirs[\"data\"], \"ch_ds_1.py\")\nif os.path.exists(ds_path):\n    with open(ds_path, 'r') as f:\n        lines = f.readlines()\n    with open(ds_path, 'w') as f:\n        for line in lines:\n            if \"self.df['landmarks'].apply\" in line or \"self.df['landmarks'].fillna\" in line:\n                f.write(\"# \" + line) \n            else:\n                f.write(line)\n    print(\" repaired ch_ds_1.py\")\nelse:\n    print(\" warning: cannot find ch_ds_1.py\")\n\n# 动态加载函数\ndef load_module(name, path):\n    spec = importlib.util.spec_from_file_location(name, path)\n    mod = importlib.util.module_from_spec(spec)\n    sys.modules[name] = mod\n    spec.loader.exec_module(mod)\n    return mod\n\ntry:\n    # 1. 加载模块\n    load_module(\"default_config\", os.path.join(dirs[\"configs\"], \"default_config.py\"))\n    cfg_module = load_module(\"cfg_kaggle_dino\", os.path.join(dirs[\"configs\"], \"cfg_kaggle_dino.py\"))\n    cfg = cfg_module.cfg\n\n    if not hasattr(cfg, 'suffix'): cfg.suffix = \".jpg\"\n\n    ds_module = load_module(\"ch_ds_1_fixed\", ds_path) \n    CustomDataset = ds_module.CustomDataset\n    \n    mdl_path = os.path.join(dirs[\"models\"], \"ch_mdl_dino.py\")\n    Net = load_module(\"ch_mdl_dino\", mdl_path).Net\n\n    # 2. 准备数据 \n    print(\" Preparing Data...\")\n    train_csv = \"/kaggle/input/landmark-recognition-2021/train.csv\"\n    train_img_dir = \"/kaggle/input/landmark-recognition-2021/train/\"\n \n    # 读取前 150000 条\n    raw_df = pd.read_csv(train_csv).iloc[:150000] \n    \n    # 执行同样的筛选逻辑 (>=15)\n    counts = raw_df['landmark_id'].value_counts()\n    valid_landmarks = counts[counts >= 15].index\n    filtered_df = raw_df[raw_df['landmark_id'].isin(valid_landmarks)].copy()\n    \n    # 标签映射\n    unique_landmarks = filtered_df['landmark_id'].unique()\n    landmark_map = { lid: i for i, lid in enumerate(unique_landmarks) }\n    filtered_df['target'] = filtered_df['landmark_id'].map(landmark_map)\n \n    # 划分训练/验证集\n    train_df, val_df = train_test_split(filtered_df, test_size=0.1, random_state=42, stratify=filtered_df['landmark_id'])\n\n    # 更新 Config\n    cfg.n_classes = len(unique_landmarks)\n    cfg.data_folder = train_img_dir\n    cfg.val_data_folder = train_img_dir \n    cfg.landmark_id2class_id = pd.DataFrame({'landmark_id': unique_landmarks, 'class_id': range(len(unique_landmarks))})\n    \n    print(f\" Training on {len(train_df)} samples, Validation on {len(val_df)} samples\")\n\n    # 3. 初始化\n    device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n    dummy_ds = CustomDataset(train_df, cfg, cfg.train_aug, mode='train')\n    model = Net(cfg, dummy_ds)\n    model.to(device)\n    \n    # 4. 加载权重\n    print(f\" Loading weights from: {LOAD_FROM}\")\n    if os.path.exists(LOAD_FROM):\n        state_dict = torch.load(LOAD_FROM, map_location=device)\n        model.load_state_dict(state_dict)\n        print(\" Weights loaded successfully.\")\n    else:\n        raise FileNotFoundError(f\"Cannot find weight file: {LOAD_FROM}\")\n\n    # 5. 准备 Loader 和 Optimizer\n    train_loader = DataLoader(dummy_ds, batch_size=cfg.batch_size, shuffle=True, num_workers=2)\n    \n    val_ds = CustomDataset(val_df, cfg, cfg.val_aug, mode='val')\n    val_loader = DataLoader(val_ds, batch_size=cfg.batch_size, shuffle=False, num_workers=2)\n    \n    optimizer = torch.optim.AdamW(model.parameters(), lr=NEW_LR)\n    \n    # 6. 续训循环\n    print(f\"\\n Starting {MORE_EPOCHS} more Epochs...\")\n    \n    for epoch in range(MORE_EPOCHS):\n        model.train()\n        train_loss = 0\n        pbar = tqdm(train_loader, desc=f\"Resume Epoch {epoch+1}/{MORE_EPOCHS}\")\n        \n        for batch in pbar:\n            inputs, targets = batch['input'].to(device), batch['target'].to(device)\n            loss = model({'input': inputs, 'target': targets})['loss']\n            \n            optimizer.zero_grad()\n            loss.backward()\n            optimizer.step()\n            \n            train_loss += loss.item()\n            pbar.set_postfix({'loss': f\"{loss.item():.4f}\"})\n            \n        # --- Validation ---\n        model.eval()\n        correct, total = 0, 0\n        with torch.no_grad():\n            for batch in tqdm(val_loader, desc=\"Validating\", leave=False):\n                inputs, targets = batch['input'].to(device), batch['target'].to(device)\n                \n                features = model.backbone.forward_features(inputs)\n                if isinstance(features, dict):\n                    x_global = features['x_norm_clstoken']\n                else:\n                    x_global = features[:, 0, :]\n                    \n                logits = model.head(model.neck(x_global))\n                _, predicted = torch.max(logits.data, 1)\n                total += targets.size(0)\n                correct += (predicted == targets).sum().item()\n        \n        val_acc = 100 * correct / total\n        print(f\" Epoch {epoch+1} Finished: Train Loss = {train_loss/len(train_loader):.4f} | Val Accuracy = {val_acc:.2f}%\")\n \n        epoch_save_path = f\"dino_resume_epoch_{epoch+1}.pth\"\n        torch.save(model.state_dict(), epoch_save_path)\n        print(f\" Saved checkpoint: {epoch_save_path}\")\n\n    # 最终保存\n    torch.save(model.state_dict(), \"dino_finetuned_final_resume.pth\")\n    print(\" All Done! Final model saved.\")\n\nexcept Exception as e:\n    import traceback\n    traceback.print_exc()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-21T06:16:08.266031Z","iopub.execute_input":"2025-12-21T06:16:08.266251Z","iopub.status.idle":"2025-12-21T16:17:42.160494Z","shell.execute_reply.started":"2025-12-21T06:16:08.266234Z","shell.execute_reply":"2025-12-21T16:17:42.159647Z"}},"outputs":[],"execution_count":null}]}