{"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":91448,"databundleVersionId":12156235,"sourceType":"competition"}],"dockerImageVersionId":31154,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"data_path = '/kaggle/input/fungi-clef-2025/'","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-10-08T04:50:27.283434Z","iopub.execute_input":"2025-10-08T04:50:27.28414Z","iopub.status.idle":"2025-10-08T04:50:27.2875Z","shell.execute_reply.started":"2025-10-08T04:50:27.284119Z","shell.execute_reply":"2025-10-08T04:50:27.286752Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!pip install git+https://github.com/mlfoundations/open_clip.git","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-08T04:50:34.172596Z","iopub.execute_input":"2025-10-08T04:50:34.172856Z","iopub.status.idle":"2025-10-08T04:50:42.237209Z","shell.execute_reply.started":"2025-10-08T04:50:34.172839Z","shell.execute_reply":"2025-10-08T04:50:42.236259Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport json\nimport yaml\nfrom pathlib import Path\nfrom types import SimpleNamespace\nimport argparse\n\nimport numpy as np\nimport pandas as pd\nimport torch\n# import faiss\n\nimport matplotlib.pyplot as plt\nfrom PIL import Image\nfrom tqdm import tqdm\nfrom torchvision import transforms as tfms\nimport torchvision.transforms as T\nimport open_clip\n\nfrom typing import Sequence, Tuple, Any, Dict, List, Optional, Union\nimport importlib","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-08T04:50:42.238943Z","iopub.execute_input":"2025-10-08T04:50:42.239234Z","iopub.status.idle":"2025-10-08T04:50:42.244489Z","shell.execute_reply.started":"2025-10-08T04:50:42.239213Z","shell.execute_reply":"2025-10-08T04:50:42.243588Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class FungiTastic(torch.nn.Module):\n    \"\"\"\n    Dataset class for the FewShot subset of the Danish Fungi dataset (size 300, closed-set).\n\n    This dataset loader supports training, validation, and testing splits, and provides\n    convenient access to images, class IDs, and file paths. It also supports optional\n    image transformations.\n    \"\"\"\n\n    SPLIT2STR = {'train': 'Train', 'val': 'Val', 'test': 'Test'}\n\n    def __init__(self, root: str, split: str = 'val', transform=None):\n        \"\"\"\n        Initializes the FungiTastic dataset.\n\n        Args:\n            root (str): The root directory of the dataset.\n            split (str, optional): The dataset split to use. Must be one of {'train', 'val', 'test'}.\n                Defaults to 'val'.\n            transform (callable, optional): Optional transform to be applied on a sample.\n        \"\"\"\n        super().__init__()\n        self.split = split\n        self.transform = transform\n        self.df = self._get_df(root, split)\n\n        assert \"image_path\" in self.df\n        if self.split != 'test':\n            assert \"category_id\" in self.df\n            self.n_classes = len(self.df['category_id'].unique())\n            self.category_id2label = {\n                k: v[0] for k, v in self.df.groupby('category_id')['species'].unique().to_dict().items()\n            }\n            self.label2category_id = {\n                v: k for k, v in self.category_id2label.items()\n            }\n\n    def add_embeddings(self, embeddings: pd.DataFrame):\n        \"\"\"\n        Updates the dataset instance with new embeddings.\n\n        Args:\n            embeddings (pd.DataFrame): A DataFrame containing an 'embedding' column.\n                                       It must align with `self.df` in terms of indexing.\n        \"\"\"\n        assert isinstance(embeddings, pd.DataFrame), \"Embeddings must be a pandas DataFrame.\"\n        assert \"embedding\" in embeddings.columns, \"Embeddings DataFrame must have an 'embedding' column.\"\n        assert len(embeddings) == len(self.df), \"Embeddings must match dataset length.\"\n\n        self.df = pd.merge(self.df, embeddings, on=\"filename\", how=\"inner\")\n\n    def get_embeddings_for_class(self, id):\n        # return the embeddings for class class_idx\n        class_idxs = self.df[self.df['category_id'] == id].index\n        return self.df.iloc[class_idxs]['embedding']\n    \n    @staticmethod\n    def _get_df(data_path: str, split: str) -> pd.DataFrame:\n        \"\"\"\n        Loads the dataset metadata as a pandas DataFrame.\n\n        Args:\n            data_path (str): The root directory where the dataset is stored.\n            split (str): The dataset split to load. Must be one of {'train', 'val', 'test'}.\n\n        Returns:\n            pd.DataFrame: A DataFrame containing metadata and file paths for the split.\n        \"\"\"\n        df_path = os.path.join(\n            data_path,\n            \"metadata\",\n            \"FungiTastic-FewShot\",\n            f\"FungiTastic-FewShot-{FungiTastic.SPLIT2STR[split]}.csv\"\n        )\n        df = pd.read_csv(df_path)\n        df[\"image_path\"] = df.filename.apply(\n            lambda x: os.path.join(data_path, \"FungiTastic-FewShot\", split, '300p', x)\n        )\n        return df\n\n    def __getitem__(self, idx: int):\n        \"\"\"\n        Retrieves a single data sample by index.\n    \n        Args:\n            idx (int): Index of the sample to retrieve.\n            ret_image (bool, optional): Whether to explicitly return the image. Defaults to False.\n    \n        Returns:\n            tuple:\n                - If embeddings exist: (image?, embedding, category_id, file_path)\n                - If no embeddings: (image, category_id, file_path) (original version)\n        \"\"\"\n        file_path = self.df[\"image_path\"].iloc[idx].replace('FungiTastic-FewShot', 'images/FungiTastic-FewShot')\n    \n        if self.split != 'test':\n            category_id = self.df[\"category_id\"].iloc[idx]\n        else:\n            category_id = None\n    \n        image = Image.open(file_path)\n    \n        if self.transform:\n            image = self.transform(image)\n    \n        # Check if embeddings exist\n        if \"embedding\" in self.df.columns:\n            emb = torch.tensor(self.df.iloc[idx]['embedding'], dtype=torch.float32).squeeze()\n        else:\n            emb = None  # No embeddings available\n    \n\n        return image, category_id, file_path, emb\n\n\n    def __len__(self):\n        \"\"\"\n        Returns the number of samples in the dataset.\n        \"\"\"\n        return len(self.df)\n\n    def get_class_id(self, idx: int) -> int:\n        \"\"\"\n        Returns the class ID of a specific sample.\n        \"\"\"\n        return self.df[\"category_id\"].iloc[idx]\n\n    def show_sample(self, idx: int) -> None:\n        \"\"\"\n        Displays a sample image along with its class name and index.\n        \"\"\"\n        image, category_id, _, _ = self.__getitem__(idx)\n        class_name = self.category_id2label[category_id]\n\n        plt.imshow(image)\n        plt.title(f\"Class: {class_name}; id: {idx}\")\n        plt.axis('off')\n        plt.show()\n\n    def get_category_idxs(self, category_id: int) -> List[int]:\n        \"\"\"\n        Retrieves all indexes for a given category ID.\n        \"\"\"\n        return self.df[self.df.category_id == category_id].index.tolist()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-08T04:50:42.2452Z","iopub.execute_input":"2025-10-08T04:50:42.245435Z","iopub.status.idle":"2025-10-08T04:50:42.264404Z","shell.execute_reply.started":"2025-10-08T04:50:42.245409Z","shell.execute_reply":"2025-10-08T04:50:42.263672Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class BioCLIP(torch.nn.Module):\n\n    def __init__(self, device):\n        \"\"\"\n        Initialize the BioCLIP feature extractor.\n        \"\"\"\n        super(BioCLIP, self).__init__()\n        self.device = device\n        self.model = None\n        self.processor = None\n        self.size = (224, 224)\n\n    def load(self):\n        \"\"\"\n        Load the BioCLIP model and its associated image processor.\n        \n        The model is loaded from the Hugging Face Hub and moved to the specified device.\n        \"\"\"\n        self.model, _, self.processor = open_clip.create_model_and_transforms('hf-hub:imageomics/bioclip')\n        self.model.to(self.device)\n\n    def extract_features(self, image):\n        \"\"\"\n        Extract normalized feature embeddings from a given image.\n\n        Args:\n            image (PIL.Image.Image): The input image from which to extract features.\n\n        Returns:\n            torch.Tensor: A normalized feature embedding vector for the input image.\n\n        Raises:\n            ValueError: If the model has not been loaded prior to calling this method.\n        \"\"\"\n        if self.model is None:\n            raise ValueError('Model not loaded')\n        image = image.resize(self.size, Image.BICUBIC)\n        image_tensor_proc = self.processor(image)[None]\n        features = self.model.encode_image(image_tensor_proc.to(self.device))\n        return self.normalize_embedding(features)\n\n    @staticmethod\n    def normalize_embedding(embs):\n        \"\"\"\n        Normalize the embedding vectors to have unit length.\n\n        Args:\n            embs (torch.Tensor): The raw embedding vectors.\n\n        Returns:\n            torch.Tensor: L2-normalized embedding vectors.\n        \"\"\"\n        return torch.nn.functional.normalize(embs.float(), dim=1, p=2)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-08T04:50:54.747782Z","iopub.execute_input":"2025-10-08T04:50:54.748523Z","iopub.status.idle":"2025-10-08T04:50:54.754577Z","shell.execute_reply.started":"2025-10-08T04:50:54.748498Z","shell.execute_reply":"2025-10-08T04:50:54.753809Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nmodel = BioCLIP(device=device)\nmodel.load()\nmodel.eval()\n\ndef generate_embeddings(dataset):\n\n    idxs = np.arange(len(dataset))\n    im_names, embs = [], []\n    for idx in tqdm(idxs):\n        im, label, file_path, _ = dataset[idx]\n\n        with torch.no_grad():\n            feat = model.extract_features(im)\n            \n        # feat_quant = model.quantize_normalized_embedding(feat)\n\n        im_names.append(os.path.basename(file_path))\n        embs.append(feat.detach().cpu().numpy())\n\n    embeddings = pd.DataFrame({'filename': im_names, 'embedding': embs})\n\n    return embeddings","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-08T04:50:55.220919Z","iopub.execute_input":"2025-10-08T04:50:55.221535Z","iopub.status.idle":"2025-10-08T04:50:57.616858Z","shell.execute_reply.started":"2025-10-08T04:50:55.221514Z","shell.execute_reply":"2025-10-08T04:50:57.616017Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"### Load the datasets\n\ntrain_dataset = FungiTastic(root=data_path, split='train', transform=None)\ntest_dataset = FungiTastic(root=data_path, split='test', transform=None)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-08T04:50:58.573008Z","iopub.execute_input":"2025-10-08T04:50:58.573934Z","iopub.status.idle":"2025-10-08T04:50:58.773157Z","shell.execute_reply.started":"2025-10-08T04:50:58.573902Z","shell.execute_reply":"2025-10-08T04:50:58.77252Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"embeddings = generate_embeddings(train_dataset)\ntrain_dataset.add_embeddings(embeddings)\nembeddings = generate_embeddings(test_dataset)\ntest_dataset.add_embeddings(embeddings)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-08T04:50:59.118947Z","iopub.execute_input":"2025-10-08T04:50:59.119567Z","iopub.status.idle":"2025-10-08T04:54:06.249792Z","shell.execute_reply.started":"2025-10-08T04:50:59.119545Z","shell.execute_reply":"2025-10-08T04:54:06.248977Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_df=train_dataset.df\ntest_df=test_dataset.df\ntest_df = test_df.drop_duplicates(subset=['observationID'], keep='first')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-08T04:54:06.251006Z","iopub.execute_input":"2025-10-08T04:54:06.251253Z","iopub.status.idle":"2025-10-08T04:54:06.271741Z","shell.execute_reply.started":"2025-10-08T04:54:06.251234Z","shell.execute_reply":"2025-10-08T04:54:06.270933Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_df['embedding_1d'] = train_df['embedding'].apply(lambda x: list(np.array(x).flatten()))\ntest_df['embedding_1d'] = test_df['embedding'].apply(lambda x: list(np.array(x).flatten()))\ntrain_df.drop('embedding',axis=1,inplace=True)\ntest_df.drop('embedding',axis=1,inplace=True)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-08T04:54:06.272502Z","iopub.execute_input":"2025-10-08T04:54:06.272827Z","iopub.status.idle":"2025-10-08T04:54:06.709346Z","shell.execute_reply.started":"2025-10-08T04:54:06.2728Z","shell.execute_reply":"2025-10-08T04:54:06.708606Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# from sklearn.preprocessing import LabelEncoder\n# le = LabelEncoder()\n# train_df['category_iden']=le.fit_transform(train_df['category_id'])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-08T03:00:09.79004Z","iopub.status.idle":"2025-10-08T03:00:09.790368Z","shell.execute_reply.started":"2025-10-08T03:00:09.790193Z","shell.execute_reply":"2025-10-08T03:00:09.790208Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"cat_counts=train_df['category_id'].value_counts()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-08T04:54:06.711321Z","iopub.execute_input":"2025-10-08T04:54:06.711556Z","iopub.status.idle":"2025-10-08T04:54:06.716218Z","shell.execute_reply.started":"2025-10-08T04:54:06.71154Z","shell.execute_reply":"2025-10-08T04:54:06.715458Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"cat_counts","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-08T04:54:06.717078Z","iopub.execute_input":"2025-10-08T04:54:06.71735Z","iopub.status.idle":"2025-10-08T04:54:06.73499Z","shell.execute_reply.started":"2025-10-08T04:54:06.717327Z","shell.execute_reply":"2025-10-08T04:54:06.734419Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"cat_index=cat_counts[cat_counts>=5].index","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-08T04:54:06.735942Z","iopub.execute_input":"2025-10-08T04:54:06.736137Z","iopub.status.idle":"2025-10-08T04:54:06.750348Z","shell.execute_reply.started":"2025-10-08T04:54:06.736114Z","shell.execute_reply":"2025-10-08T04:54:06.749613Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"filter_df=pd.DataFrame()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-08T04:54:06.75121Z","iopub.execute_input":"2025-10-08T04:54:06.751925Z","iopub.status.idle":"2025-10-08T04:54:06.764987Z","shell.execute_reply.started":"2025-10-08T04:54:06.751901Z","shell.execute_reply":"2025-10-08T04:54:06.764358Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"for i in range(len(cat_index)):\n    filter_df = pd.concat([filter_df, train_df[train_df['category_id'] == cat_index[i]]])\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-08T04:54:06.765633Z","iopub.execute_input":"2025-10-08T04:54:06.765827Z","iopub.status.idle":"2025-10-08T04:54:07.437516Z","shell.execute_reply.started":"2025-10-08T04:54:06.765813Z","shell.execute_reply":"2025-10-08T04:54:07.436616Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"file_name=filter_df[filter_df['category_id']==2383]['image_path'].values","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-08T04:54:07.438365Z","iopub.execute_input":"2025-10-08T04:54:07.438613Z","iopub.status.idle":"2025-10-08T04:54:07.443965Z","shell.execute_reply.started":"2025-10-08T04:54:07.438596Z","shell.execute_reply":"2025-10-08T04:54:07.443073Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"image_drop=['/kaggle/input/fungi-clef-2025/FungiTastic-FewShot/train/300p/2-3052905305.JPG','/kaggle/input/fungi-clef-2025/FungiTastic-FewShot/train/300p/3-3052905305.JPG',\n           '/kaggle/input/fungi-clef-2025/FungiTastic-FewShot/train/300p/4-3052905305.JPG','/kaggle/input/fungi-clef-2025/FungiTastic-FewShot/train/300p/6-3052905305.JPG',\n           '/kaggle/input/fungi-clef-2025/FungiTastic-FewShot/train/300p/18-3052905305.JPG']","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-08T04:54:07.446695Z","iopub.execute_input":"2025-10-08T04:54:07.44691Z","iopub.status.idle":"2025-10-08T04:54:07.459678Z","shell.execute_reply.started":"2025-10-08T04:54:07.446893Z","shell.execute_reply":"2025-10-08T04:54:07.45886Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"filter_df = filter_df[~filter_df['image_path'].isin(image_drop)]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-08T04:54:07.460344Z","iopub.execute_input":"2025-10-08T04:54:07.460578Z","iopub.status.idle":"2025-10-08T04:54:07.481359Z","shell.execute_reply.started":"2025-10-08T04:54:07.460562Z","shell.execute_reply":"2025-10-08T04:54:07.480667Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"filter_df['category_id'].value_counts().sum()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-08T04:54:07.482142Z","iopub.execute_input":"2025-10-08T04:54:07.482436Z","iopub.status.idle":"2025-10-08T04:54:07.496602Z","shell.execute_reply.started":"2025-10-08T04:54:07.482409Z","shell.execute_reply":"2025-10-08T04:54:07.495897Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from torchvision import transforms\nfrom PIL import Image\n\ntransform = transforms.Compose([\n    transforms.RandomHorizontalFlip(p=0.5),\n    transforms.RandomVerticalFlip(p=0.2),\n    transforms.RandomRotation(20),\n    transforms.ColorJitter(brightness=0.2, contrast=0.2, saturation=0.2),\n    transforms.RandomResizedCrop(224, scale=(0.8, 1.0)),\n    transforms.ToTensor(),\n])\n\n# # Example usage\n# img = Image.open('/kaggle/input/fungi-clef-2025/images/FungiTastic-FewShot/train/300p/0-2237853300.JPG')\n# aug_img = transform(img)\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-08T04:54:07.497496Z","iopub.execute_input":"2025-10-08T04:54:07.497712Z","iopub.status.idle":"2025-10-08T04:54:07.510697Z","shell.execute_reply.started":"2025-10-08T04:54:07.497697Z","shell.execute_reply":"2025-10-08T04:54:07.509856Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# import matplotlib.pyplot as plt\n# import torchvision.transforms.functional as F\n\n# plt.imshow(F.to_pil_image(aug_img))\n# plt.axis('off')\n# plt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-08T03:41:30.221721Z","iopub.execute_input":"2025-10-08T03:41:30.222305Z","iopub.status.idle":"2025-10-08T03:41:30.352489Z","shell.execute_reply.started":"2025-10-08T03:41:30.222284Z","shell.execute_reply":"2025-10-08T03:41:30.351746Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# img=Image.open(\"/kaggle/input/fungi-clef-2025/images/FungiTastic-FewShot/train/300p/0-2237853300.JPG\")\n# display(img)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-08T03:43:26.373071Z","iopub.execute_input":"2025-10-08T03:43:26.373349Z","iopub.status.idle":"2025-10-08T03:43:26.393549Z","shell.execute_reply.started":"2025-10-08T03:43:26.373328Z","shell.execute_reply":"2025-10-08T03:43:26.392841Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# for i in range(5):\n#     aug_img = transform(img)\n#     plt.subplot(1, 5, i + 1)\n#     plt.imshow(aug_img.permute(1, 2, 0))\n#     plt.axis('off')\n# plt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-08T03:45:51.157966Z","iopub.execute_input":"2025-10-08T03:45:51.158281Z","iopub.status.idle":"2025-10-08T03:45:51.329736Z","shell.execute_reply.started":"2025-10-08T03:45:51.158261Z","shell.execute_reply":"2025-10-08T03:45:51.328943Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"n_aug=5\nfor idx, row in filter_df.iterrows():\n    img = Image.open(row['image_path'])\n    cat = row['category_id']\n    for i in range(n_aug):\n        aug_img = transform(img)\n        # Convert tensor to PIL to save\n        aug_pil = transforms.ToPILImage()(aug_img)\n        filename = os.path.join(output_dir, f\"{cat}_{idx}_{i}.jpg\")\n        aug_pil.save(filename)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-08T04:54:23.596007Z","iopub.execute_input":"2025-10-08T04:54:23.596619Z","iopub.status.idle":"2025-10-08T04:56:31.132584Z","shell.execute_reply.started":"2025-10-08T04:54:23.596596Z","shell.execute_reply":"2025-10-08T04:56:31.131862Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\n\noutput_dir = \"augmented_images2\"\nos.makedirs(output_dir, exist_ok=True)  # create folder if missing","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-08T04:54:07.511334Z","iopub.execute_input":"2025-10-08T04:54:07.511629Z","iopub.status.idle":"2025-10-08T04:54:07.526078Z","shell.execute_reply.started":"2025-10-08T04:54:07.511605Z","shell.execute_reply":"2025-10-08T04:54:07.525575Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"filter_df['image_path'] = filter_df['image_path'].apply(\n    lambda x: x.replace(\n        '/kaggle/input/fungi-clef-2025/FungiTastic-FewShot/',\n        '/kaggle/input/fungi-clef-2025/images/FungiTastic-FewShot/'\n    )\n)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-08T04:54:18.427204Z","iopub.execute_input":"2025-10-08T04:54:18.427755Z","iopub.status.idle":"2025-10-08T04:54:18.434202Z","shell.execute_reply.started":"2025-10-08T04:54:18.427735Z","shell.execute_reply":"2025-10-08T04:54:18.433432Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd\nfrom tqdm import tqdm\nimport numpy as np\nimport os\nfrom PIL import Image\nimport torch","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-08T04:56:31.133903Z","iopub.execute_input":"2025-10-08T04:56:31.134589Z","iopub.status.idle":"2025-10-08T04:56:31.138931Z","shell.execute_reply.started":"2025-10-08T04:56:31.134562Z","shell.execute_reply":"2025-10-08T04:56:31.138148Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"aug_dir = \"augmented_images2\"\naug_files = os.listdir(aug_dir)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-08T04:56:31.139733Z","iopub.execute_input":"2025-10-08T04:56:31.139914Z","iopub.status.idle":"2025-10-08T04:56:31.162418Z","shell.execute_reply.started":"2025-10-08T04:56:31.1399Z","shell.execute_reply":"2025-10-08T04:56:31.16166Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"aug_filepaths = []\ncategory_ids = []\nspecies_list = []","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-08T04:56:31.163583Z","iopub.execute_input":"2025-10-08T04:56:31.163797Z","iopub.status.idle":"2025-10-08T04:56:31.174227Z","shell.execute_reply.started":"2025-10-08T04:56:31.163782Z","shell.execute_reply":"2025-10-08T04:56:31.173543Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"n_aug=5\nfor f in aug_files:\n    try:\n        cat_id = int(f.split('_')[0])  # get category_id from filename\n    except:\n        cat_id = np.nan\n    aug_filepaths.append(os.path.join(aug_dir, f))\n    category_ids.append(cat_id)\n    species_list.append(np.nan)  # unknown species for augmented images\n\n# Create DataFrame for augmented images\naug_df = pd.DataFrame({\n    \"filename\": [os.path.basename(p) for p in aug_filepaths],\n    \"image_path\": aug_filepaths,\n    \"category_id\": category_ids,\n    \"species\": species_list\n})\n\n# Generate embeddings\nemb_list = []\nfor idx in tqdm(range(len(aug_df))):\n    img_path = aug_df.loc[idx, \"image_path\"]\n    try:\n        im = Image.open(img_path).convert(\"RGB\")\n        with torch.no_grad():\n            emb = model.extract_features(im)\n        emb_list.append(emb.squeeze().cpu().numpy())\n    except Exception as e:\n        print(f\"Failed for {img_path}: {e}\")\n        emb_list.append(np.nan)\n\naug_df[\"embedding\"] = emb_list\n\n# Append to your existing filter_df\nfilter_df = pd.concat([filter_df, aug_df], ignore_index=True)\n\nprint(\"New dataset size:\", len(filter_df))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-08T04:56:31.174938Z","iopub.execute_input":"2025-10-08T04:56:31.175175Z","iopub.status.idle":"2025-10-08T05:01:56.240492Z","shell.execute_reply.started":"2025-10-08T04:56:31.175153Z","shell.execute_reply":"2025-10-08T05:01:56.239678Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def combine_embeddings(row):\n    emb_x = row['embedding_1d']\n    emb_y = row['embedding']\n\n    # If emb_x is a valid array, return it\n    if isinstance(emb_x, (np.ndarray, list)):\n        return np.array(emb_x)\n    # Otherwise, use emb_y\n    elif isinstance(emb_y, (np.ndarray, list)):\n        return np.array(emb_y)\n    else:\n        return np.nan  # both missing\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-08T05:10:14.137829Z","iopub.execute_input":"2025-10-08T05:10:14.138467Z","iopub.status.idle":"2025-10-08T05:10:14.142844Z","shell.execute_reply.started":"2025-10-08T05:10:14.138443Z","shell.execute_reply":"2025-10-08T05:10:14.141892Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"filter_df['combine_embedding'] = filter_df.apply(combine_embeddings, axis=1)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-08T05:10:21.148244Z","iopub.execute_input":"2025-10-08T05:10:21.148528Z","iopub.status.idle":"2025-10-08T05:10:21.462925Z","shell.execute_reply.started":"2025-10-08T05:10:21.148509Z","shell.execute_reply":"2025-10-08T05:10:21.462339Z"}},"outputs":[],"execution_count":null},{"cell_type":"raw","source":"filter_df['embedding_array'] = filter_df['embedding'].apply(\n    lambda x: np.array(x) if isinstance(x, (list, np.ndarray)) else np.nan\n)","metadata":{}},{"cell_type":"code","source":"filter_df['combine_embedding']=filter_df['combine_embedding'].apply(\n    lambda x: np.array(x) if isinstance(x, (list, np.ndarray)) else np.nan\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-08T05:12:16.829528Z","iopub.execute_input":"2025-10-08T05:12:16.830053Z","iopub.status.idle":"2025-10-08T05:12:16.901241Z","shell.execute_reply.started":"2025-10-08T05:12:16.83003Z","shell.execute_reply":"2025-10-08T05:12:16.900214Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"embed_dim = len(filter_df['combine_embedding'].iloc[0])\nemb_cols = [f\"embedding_{i}\" for i in range(embed_dim)]\ndf_final = pd.DataFrame(filter_df['combine_embedding'].tolist(), columns=emb_cols)\n\n# Add category_id\ndf_final['category_id'] = filter_df['category_id'].values\n\n# Reorder\ndf_final = df_final[['category_id'] + emb_cols]\n\nprint(df_final.shape)\ndf_final.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-08T05:13:43.264715Z","iopub.execute_input":"2025-10-08T05:13:43.265239Z","iopub.status.idle":"2025-10-08T05:13:48.071752Z","shell.execute_reply.started":"2025-10-08T05:13:43.265218Z","shell.execute_reply":"2025-10-08T05:13:48.07098Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import lightgbm as lgb\nfrom sklearn.model_selection import KFold\nfrom sklearn.metrics import accuracy_score, classification_report","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-08T05:17:21.830731Z","iopub.execute_input":"2025-10-08T05:17:21.831505Z","iopub.status.idle":"2025-10-08T05:17:21.835571Z","shell.execute_reply.started":"2025-10-08T05:17:21.831479Z","shell.execute_reply":"2025-10-08T05:17:21.834523Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Original category IDs\nunique_classes = np.sort(df_final['category_id'].unique())\nnum_classes = len(unique_classes)\n\n# Mapping: category_id -> 0-based label\ncat2label = {cat: idx for idx, cat in enumerate(unique_classes)}\nlabel2cat = {idx: cat for cat, idx in cat2label.items()}\n\n# Apply mapping\ndf_final['target'] = df_final['category_id'].map(cat2label)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-08T05:24:59.244486Z","iopub.execute_input":"2025-10-08T05:24:59.245061Z","iopub.status.idle":"2025-10-08T05:24:59.254207Z","shell.execute_reply.started":"2025-10-08T05:24:59.245043Z","shell.execute_reply":"2025-10-08T05:24:59.253657Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"X = df_final.drop(columns=['category_id', 'target']).values\ny = df_final['target'].values\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-08T05:25:10.572522Z","iopub.execute_input":"2025-10-08T05:25:10.572783Z","iopub.status.idle":"2025-10-08T05:25:10.58788Z","shell.execute_reply.started":"2025-10-08T05:25:10.572766Z","shell.execute_reply":"2025-10-08T05:25:10.587197Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"n_splits = 5\nkf = KFold(n_splits=n_splits, shuffle=True, random_state=42)\n\nfold = 1\nacc_scores = []\n\nfor train_idx, val_idx in kf.split(X):\n    print(f\"\\n===== Fold {fold} =====\")\n    \n    X_train, X_val = X[train_idx], X[val_idx]\n    y_train, y_val = y[train_idx], y[val_idx]\n    \n    train_data = lgb.Dataset(X_train, label=y_train)\n    val_data = lgb.Dataset(X_val, label=y_val, reference=train_data)\n    \n    params = {\n        'objective': 'multiclass',\n        'num_class': num_classes,\n        'metric': 'multi_logloss',\n        'boosting_type': 'gbdt',\n        'learning_rate': 0.05,\n        'num_leaves': 31,\n        'max_depth': -1,\n        'verbose': -1\n    }\n    \n    clf = lgb.train(\n        params,\n        train_data,\n        num_boost_round=500,\n        valid_sets=[train_data, val_data],\n        callbacks=[lgb.early_stopping(stopping_rounds=50),\n                   lgb.log_evaluation(period=50)]\n    )\n    \n    # Predict\n    y_pred = np.argmax(clf.predict(X_val), axis=1)\n    acc = accuracy_score(y_val, y_pred)\n    acc_scores.append(acc)\n    print(f\"Fold {fold} Accuracy: {acc}\")\n    \n    fold += 1\n\nprint(\"\\n===== K-Fold Cross-Validation Results =====\")\nprint(f\"Mean Accuracy: {np.mean(acc_scores):.4f}\")\nprint(f\"Std Accuracy: {np.std(acc_scores):.4f}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-08T05:28:58.079178Z","iopub.execute_input":"2025-10-08T05:28:58.079572Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}