{"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":"gpu","dataSources":[{"sourceId":112509,"databundleVersionId":14254895,"isSourceIdPinned":false,"sourceType":"competition"},{"sourceId":13825588,"sourceType":"datasetVersion","datasetId":8804702},{"sourceId":13825622,"sourceType":"datasetVersion","datasetId":8804728},{"sourceId":13841952,"sourceType":"datasetVersion","datasetId":8816207},{"sourceId":13841975,"sourceType":"datasetVersion","datasetId":8816226}],"dockerImageVersionId":31193,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os\nfrom pathlib import Path\nfrom PIL import Image\nimport numpy as np\nfrom tqdm import tqdm\nimport torch\nimport torchvision.models as models\nimport torch\nfrom torch.utils.data import Dataset, DataLoader\nfrom torchvision import transforms\nimport timm  # timm provides many pretrained models\nimport matplotlib.pyplot as plt\nfrom sklearn.neighbors import KernelDensity\nimport numpy as np\nimport os\nimport numpy as np\nimport pandas as pd\nfrom pathlib import Path\nfrom PIL import Image\n\nimport torch\nfrom torch.utils.data import Dataset, DataLoader\nfrom torchvision import transforms\nimport torchvision.models as models\n\n# ================== CONFIG ==================\nIMAGE_DIR = \"/kaggle/input/csiro-biomass/test\"\nOUTPUT_DIR = \"/kaggle/working/\"\n\nBATCH_SIZE = 1\nNUM_WORKERS = 0\nDEVICE = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n\nTRAIN_FEATURES_PATH = \"/kaggle/input/my-features-1/train_features (2).npy\"\nFILENAMES_PATH      = \"/kaggle/input/my-features-1/train_filenames (1).npy\"\nTRAIN_CSV_PATH      = \"/kaggle/input/csiro-biomass/train.csv\"\n\nTARGET_NAMES = [\"Dry_Clover_g\", \"Dry_Dead_g\", \"Dry_Green_g\", \"Dry_Total_g\", \"GDM_g\"]\nTOP_K = 14\n\nos.makedirs(OUTPUT_DIR, exist_ok=True)\n\nIMG_EXTS = {\".jpg\", \".jpeg\", \".png\", \".bmp\", \".tiff\", \".webp\"}\n\n# ================== DATASET ==================\nclass ImageFolderList(Dataset):\n    def __init__(self, root_dir, transform=None):\n        self.root_dir = Path(root_dir)\n        self.files = sorted(\n            [p for p in self.root_dir.rglob(\"*\") if p.suffix.lower() in IMG_EXTS]\n        )\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.files)\n\n    def __getitem__(self, idx):\n        p = self.files[idx]\n        img = Image.open(p).convert(\"RGB\")\n        if self.transform:\n            img = self.transform(img)\n        return img, str(p)\n\n# ================== TRANSFORM ==================\n# IMPORTANT: this must match whatever you used when creating train_features.npy\ntransform = transforms.Compose([\n    #transforms.Resize((224, 224)),   # keep same size as used for train features\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\ndataset = ImageFolderList(IMAGE_DIR, transform=transform)\nloader = DataLoader(dataset, batch_size=BATCH_SIZE, shuffle=False, num_workers=NUM_WORKERS)\n\n# ================== LOAD MODEL ==================\n# Use torchvision EfficientNet-B2, then drop the classifier to get embeddings\nmodel = timm.create_model(\"efficientnet_b0\", pretrained=False, num_classes=0)\nstate_dict = torch.load(\"/kaggle/input/model-good-one/efficientnet_b0_timm_pretrained.pth\")\nmodel.load_state_dict(state_dict)\n\n\nmodel.to(DEVICE)\nmodel.eval()\n\n# ================== EXTRACT TEST FEATURES ==================\ntest_features = []\ntest_paths = []\n\nwith torch.no_grad():\n    for batch_imgs, batch_paths in loader:\n        batch_imgs = batch_imgs.to(DEVICE)\n        feats = model(batch_imgs)            # shape: (B, 1408)\n        if isinstance(feats, (list, tuple)):\n            feats = feats[0]\n        test_features.append(feats.cpu().numpy())\n        test_paths.extend(batch_paths)\n\ntest_features = np.vstack(test_features)     # (N_test, 1408)\ntest_paths = np.array(test_paths)\nprint(test_features.shape)\n\n# ================== LOAD TRAIN FEATURES ==================\ntrain_feats = np.load(TRAIN_FEATURES_PATH)   # should be (N_train, 1408)\ntry:\n    train_filenames = np.load(FILENAMES_PATH)\nexcept:\n    train_filenames = np.load(FILENAMES_PATH, allow_pickle=True)\n\n# Sanity check: dim must match\nif train_feats.shape[1] != test_features.shape[1]:\n    raise ValueError(f\"Feature dimension mismatch: train {train_feats.shape}, test {test_features.shape}\")\n\n# ================== L2 NORMALIZATION ==================\n# Apply L2 norm explicitly to both train and test features\neps = 1e-12\ntrain_norm = train_feats / (np.linalg.norm(train_feats, axis=1, keepdims=True) + eps)\ntest_norm  = test_features / (np.linalg.norm(test_features, axis=1, keepdims=True) + eps)\n\n# ================== LOAD TRAIN CSV ==================\ntrain_df = pd.read_csv(TRAIN_CSV_PATH)\ntrain_df[\"image_id\"] = train_df[\"image_path\"].astype(str).apply(lambda p: Path(p).stem)\n\n# Lookup for (image_id, target_name) → target mean\ntarget_lookup = (\n    train_df.groupby([\"image_id\", \"target_name\"])[\"target\"].mean()\n)\nlookup_index = target_lookup.index\n\n# ================== BUILD SUBMISSION ==================\nsubmission_rows = []\n\nfor feat_norm, test_path in zip(test_norm, test_paths):\n\n    test_image_id = Path(test_path).stem\n\n    # Euclidean distance in normalized feature space\n    # diffs shape: (N_train, 1408)\n    diffs = train_norm - feat_norm[np.newaxis, :]\n    dists = np.linalg.norm(diffs, axis=1)          # shape: (N_train,)\n\n    # smaller distance = closer neighbor\n    topk_idx = np.argsort(dists)[:TOP_K]\n\n    # Get nearest train image IDs\n    neighbor_ids = [Path(str(train_filenames[i])).stem for i in topk_idx]\n\n    # Aggregate target predictions\n    for tname in TARGET_NAMES:\n        vals = []\n        for nid in neighbor_ids:\n            key = (nid, tname)\n            if key in lookup_index:\n                vals.append(target_lookup[key])\n        pred_val = float(np.mean(vals)) if len(vals) > 0 else float(\"nan\")\n\n        submission_rows.append({\n            \"sample_id\": f\"{test_image_id}__{tname}\",\n            \"target\": pred_val\n        })\n\n# ================== SAVE SUBMISSION ==================\nsubmission_df = pd.DataFrame(submission_rows)\nsubmission_df.to_csv(os.path.join(OUTPUT_DIR, \"submission.csv\"), index=False)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-23T18:48:03.416696Z","iopub.execute_input":"2025-11-23T18:48:03.417001Z","iopub.status.idle":"2025-11-23T18:48:03.728372Z","shell.execute_reply.started":"2025-11-23T18:48:03.416978Z","shell.execute_reply":"2025-11-23T18:48:03.727612Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# import os\n# import numpy as np\n# from pathlib import Path\n# from PIL import Image\n\n# import torch\n# from torch.utils.data import Dataset, DataLoader\n# from torchvision import transforms\n# import timm\n\n# # ================== CONFIG ==================\n# TRAIN_IMG_DIR = \"/kaggle/input/csiro-biomass/train\"\n# OUTPUT_DIR     = \"/kaggle/working\"\n# BATCH_SIZE     = 16\n# NUM_WORKERS    = 2\n# DEVICE         = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n\n# os.makedirs(OUTPUT_DIR, exist_ok=True)\n\n# IMG_EXTS = {\".jpg\", \".jpeg\", \".png\", \".bmp\", \".tiff\", \".webp\"}\n\n# # ================== DATASET ==================\n# class ImageFolderList(Dataset):\n#     def __init__(self, root_dir, transform=None):\n#         self.root_dir = Path(root_dir)\n#         self.files = sorted(\n#             [p for p in self.root_dir.rglob(\"*\") if p.suffix.lower() in IMG_EXTS]\n#         )\n#         self.transform = transform\n\n#     def __len__(self):\n#         return len(self.files)\n\n#     def __getitem__(self, idx):\n#         p = self.files[idx]\n#         img = Image.open(p).convert(\"RGB\")\n#         if self.transform:\n#             img = self.transform(img)\n#         return img, str(p)\n\n# # ================== TRANSFORM ==================\n# # IMPORTANT: EfficientNet-B0 expects 224×224\n# transform = transforms.Compose([\n#     #transforms.Resize((224, 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\n# dataset = ImageFolderList(TRAIN_IMG_DIR, transform=transform)\n# loader = DataLoader(dataset, batch_size=BATCH_SIZE, shuffle=False, num_workers=NUM_WORKERS)\n\n# # ================== LOAD MODEL (TIMM) ==================\n# # EfficientNet-B0 with pretrained ImageNet weights\n# # num_classes=0 → classifier removed → output = 1280-dim embedding\n# model = timm.create_model(\n#     \"efficientnet_b0\",\n#     pretrained=True,\n#     num_classes=0,\n#     global_pool=\"avg\"\n# )\n\n# model.to(DEVICE)\n# model.eval()\n\n# # ================== EXTRACT TRAIN FEATURES ==================\n# train_features = []\n# train_paths = []\n\n# with torch.no_grad():\n#     for batch_imgs, batch_paths in loader:\n#         batch_imgs = batch_imgs.to(DEVICE)\n\n#         feats = model(batch_imgs)       # shape: (B, 1280)\n#         if isinstance(feats, (list, tuple)):\n#             feats = feats[0]\n\n#         train_features.append(feats.cpu().numpy())\n#         train_paths.extend(batch_paths)\n\n# # Stack features\n# train_features = np.vstack(train_features)      # (N_train, 1280)\n# train_paths = np.array(train_paths)\n\n# # ================== SAVE FEATURES ==================\n# np.save(os.path.join(OUTPUT_DIR, \"train_features.npy\"), train_features)\n# np.save(os.path.join(OUTPUT_DIR, \"train_filenames.npy\"), train_paths)\n\n# print(\"Saved:\")\n# print(os.path.join(OUTPUT_DIR, \"train_features.npy\"))\n# print(os.path.join(OUTPUT_DIR, \"train_filenames.npy\"))\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-23T18:30:54.156522Z","iopub.execute_input":"2025-11-23T18:30:54.156955Z","iopub.status.idle":"2025-11-23T18:31:17.737838Z","shell.execute_reply.started":"2025-11-23T18:30:54.156928Z","shell.execute_reply":"2025-11-23T18:31:17.737072Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# feat = np.load(\"/kaggle/input/my-features-1/train_features (2).npy\")\n# feat.shape","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-23T18:37:43.931606Z","iopub.execute_input":"2025-11-23T18:37:43.932292Z","iopub.status.idle":"2025-11-23T18:37:43.980659Z","shell.execute_reply.started":"2025-11-23T18:37:43.932265Z","shell.execute_reply":"2025-11-23T18:37:43.980054Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}